[automerger skipped] Import translations. DO NOT MERGE ANYWHERE am: d6b0605005 -s ours am: 2b0c123c79 -s ours
am skip reason: subject contains skip directive
Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Launcher3/+/23787726
Change-Id: I94ca54febede15030d598974f57ae202d9efd7ff
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/Android.bp b/Android.bp
index a7edf2a..9b696a2 100644
--- a/Android.bp
+++ b/Android.bp
@@ -172,6 +172,7 @@
static_libs: [
"Launcher3ResLib",
"launcher-testing-shared",
+ "animationlib"
],
sdk_version: "current",
min_sdk_version: min_launcher3_sdk_version,
diff --git a/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java b/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java
index e5b2c67..ecb8365 100644
--- a/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java
+++ b/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java
@@ -32,7 +32,7 @@
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.LauncherModel;
import com.android.launcher3.ShortcutAndWidgetContainer;
import com.android.launcher3.testing.shared.TestProtocol;
@@ -191,10 +191,11 @@
case TestProtocol.REQUEST_CLEAR_DATA: {
final long identity = Binder.clearCallingIdentity();
try {
- LauncherSettings.Settings.call(mContext.getContentResolver(),
- LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
- MAIN_EXECUTOR.submit(() ->
- LauncherAppState.getInstance(mContext).getModel().forceReload());
+ MODEL_EXECUTOR.execute(() -> {
+ LauncherModel model = LauncherAppState.getInstance(mContext).getModel();
+ model.getModelDbController().createEmptyDB();
+ MAIN_EXECUTOR.execute(model::forceReload);
+ });
return response;
} finally {
Binder.restoreCallingIdentity(identity);
diff --git a/quickstep/protos_overrides/launcher_atom_extension.proto b/quickstep/protos_overrides/launcher_atom_extension.proto
index f5a277b..b3df353 100644
--- a/quickstep/protos_overrides/launcher_atom_extension.proto
+++ b/quickstep/protos_overrides/launcher_atom_extension.proto
@@ -61,6 +61,9 @@
// User entered by tapping on QSB bar on homescreen.
QSB = 2;
+
+ // User entered by swiping up from overview (using Rocket Gesture).
+ OVERVIEW = 3;
}
}
}
diff --git a/quickstep/res/drawable/bg_floating_desktop_select.xml b/quickstep/res/drawable/bg_floating_desktop_select.xml
new file mode 100644
index 0000000..d7df338
--- /dev/null
+++ b/quickstep/res/drawable/bg_floating_desktop_select.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright (C) 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<shape
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:shape="rectangle">
+
+ <corners android:radius="@dimen/rounded_button_radius" />
+ <solid android:color="?androidprv:attr/materialColorPrimaryContainer" />
+</shape>
\ No newline at end of file
diff --git a/quickstep/res/layout/floating_desktop_app_select.xml b/quickstep/res/layout/floating_desktop_app_select.xml
new file mode 100644
index 0000000..375fc44
--- /dev/null
+++ b/quickstep/res/layout/floating_desktop_app_select.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright (C) 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<com.android.quickstep.views.DesktopAppSelectView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/desktop_mode_floating_app_select_height"
+ android:layout_gravity="top|center_horizontal"
+ android:background="@drawable/bg_floating_desktop_select"
+ android:elevation="@dimen/desktop_mode_floating_app_select_elevation"
+ android:gravity="center_vertical"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/desktop_app_select_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="@dimen/desktop_mode_floating_app_select_text_margin"
+ android:layout_marginStart="@dimen/desktop_mode_floating_app_select_margin"
+ android:drawablePadding="@dimen/desktop_mode_floating_app_select_text_margin"
+ android:drawableStart="@drawable/ic_desktop"
+ android:drawableTint="?androidprv:attr/materialColorOnPrimaryContainer"
+ android:fontFamily="google-sans-medium"
+ android:gravity="center_vertical"
+ android:text="@string/desktop_select_app_toast"
+ android:textColor="?androidprv:attr/materialColorOnPrimaryContainer"
+ android:textSize="@dimen/desktop_mode_floating_app_select_text_size" />
+
+ <Button
+ android:id="@+id/close_button"
+ style="@android:style/Widget.DeviceDefault.Button.Borderless"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="@dimen/desktop_mode_floating_app_select_margin"
+ android:minWidth="0dp"
+ android:fontFamily="google-sans-medium"
+ android:text="@string/desktop_button_close_app_toast"
+ android:textAllCaps="false"
+ android:textColor="?androidprv:attr/materialColorPrimary"
+ android:textSize="@dimen/desktop_mode_floating_app_select_text_size" />
+
+</com.android.quickstep.views.DesktopAppSelectView>
diff --git a/quickstep/res/layout/taskbar_all_apps.xml b/quickstep/res/layout/taskbar_all_apps.xml
index 976cd9e..e234165 100644
--- a/quickstep/res/layout/taskbar_all_apps.xml
+++ b/quickstep/res/layout/taskbar_all_apps.xml
@@ -14,18 +14,11 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<com.android.launcher3.taskbar.allapps.TaskbarAllAppsSlideInView
+<com.android.launcher3.taskbar.allapps.TaskbarAllAppsContainerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:accessibilityPaneTitle="@string/all_apps_label">
-
- <com.android.launcher3.taskbar.allapps.TaskbarAllAppsContainerView
- android:id="@+id/apps_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:clipChildren="true"
- android:clipToPadding="false"
- android:focusable="false"
- android:saveEnabled="false" />
-</com.android.launcher3.taskbar.allapps.TaskbarAllAppsSlideInView>
+ android:clipChildren="true"
+ android:clipToPadding="false"
+ android:focusable="false"
+ android:saveEnabled="false" />
diff --git a/quickstep/res/layout/taskbar_all_apps_sheet.xml b/quickstep/res/layout/taskbar_all_apps_sheet.xml
new file mode 100644
index 0000000..a1d5fa6
--- /dev/null
+++ b/quickstep/res/layout/taskbar_all_apps_sheet.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<com.android.launcher3.taskbar.allapps.TaskbarAllAppsSlideInView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:accessibilityPaneTitle="@string/all_apps_label">
+
+ <include
+ android:id="@+id/apps_view"
+ layout="@layout/taskbar_all_apps" />
+</com.android.launcher3.taskbar.allapps.TaskbarAllAppsSlideInView>
diff --git a/quickstep/res/values-af/strings.xml b/quickstep/res/values-af/strings.xml
index f7a1d1a..8007646 100644
--- a/quickstep/res/values-af/strings.xml
+++ b/quickstep/res/values-af/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Skuif na regs onder"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Wys nog # app.}other{Wys nog # apps.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> en <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Voeg nou app by werkskerm"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Kanselleer"</string>
</resources>
diff --git a/quickstep/res/values-am/strings.xml b/quickstep/res/values-am/strings.xml
index 86273f5..d0269b8 100644
--- a/quickstep/res/values-am/strings.xml
+++ b/quickstep/res/values-am/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ወደ ታች/ቀኝ ይውሰዱ"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{ተጨማሪ # መተግበሪያ አሳይ።}one{ተጨማሪ # መተግበሪያ አሳይ።}other{ተጨማሪ # መተግበሪያዎች አሳይ።}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> እና <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"መተግበሪያን ወደ ዴስክቶፕ በማከል ላይ"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"ይቅር"</string>
</resources>
diff --git a/quickstep/res/values-ar/strings.xml b/quickstep/res/values-ar/strings.xml
index f58a75e..3f711de 100644
--- a/quickstep/res/values-ar/strings.xml
+++ b/quickstep/res/values-ar/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"الانتقال إلى يسار الشاشة أو أسفلها"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{إظهار تطبيق واحد آخر}zero{إظهار # تطبيق آخر}two{إظهار تطبيقَين آخرَين}few{إظهار # تطبيقات أخرى}many{إظهار # تطبيقًا آخر}other{إظهار # تطبيق آخر}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"\"<xliff:g id="APP_NAME_1">%1$s</xliff:g>\" و\"<xliff:g id="APP_NAME_2">%2$s</xliff:g>\""</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"إضافة تطبيق إلى سطح المكتب"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"إلغاء"</string>
</resources>
diff --git a/quickstep/res/values-as/strings.xml b/quickstep/res/values-as/strings.xml
index 05d1b99..9ab8c30 100644
--- a/quickstep/res/values-as/strings.xml
+++ b/quickstep/res/values-as/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"তলৰ সোঁফাললৈ নিয়ক"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{আৰু # টা এপ্ দেখুৱাওক।}one{আৰু # টা এপ্ দেখুৱাওক।}other{আৰু # টা এপ্ দেখুৱাওক।}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> আৰু <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"ডেস্কটপত এপ্ যোগ দি থকা হৈছে"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"বাতিল কৰক"</string>
</resources>
diff --git a/quickstep/res/values-az/strings.xml b/quickstep/res/values-az/strings.xml
index 3e5bb6f..4b45d50 100644
--- a/quickstep/res/values-az/strings.xml
+++ b/quickstep/res/values-az/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Aşağı/sağa köçürün"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Daha # tətbiqi göstərin.}other{Daha # tətbiqi göstərin.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> və <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Tətbiqin masaüstünə əlavə edilməsi"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Ləğv edin"</string>
</resources>
diff --git a/quickstep/res/values-b+sr+Latn/strings.xml b/quickstep/res/values-b+sr+Latn/strings.xml
index e6d87f8..b818edc 100644
--- a/quickstep/res/values-b+sr+Latn/strings.xml
+++ b/quickstep/res/values-b+sr+Latn/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Premesti dole desno"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Prikaži još # aplikaciju.}one{Prikaži još # aplikaciju.}few{Prikaži još # aplikacije.}other{Prikaži još # aplikacija.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> i <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Dodaje se aplikacija na radnu povrršinu"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Otkaži"</string>
</resources>
diff --git a/quickstep/res/values-be/strings.xml b/quickstep/res/values-be/strings.xml
index c7358ff..6438392 100644
--- a/quickstep/res/values-be/strings.xml
+++ b/quickstep/res/values-be/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Перамясціць уніз/управа"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Паказаць ячшэ # праграму.}one{Паказаць ячшэ # праграму.}few{Паказаць ячшэ # праграмы.}many{Паказаць ячшэ # праграм.}other{Паказаць ячшэ # праграмы.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> і <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Дадаванне праграмы на камп\'ютар"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Скасаваць"</string>
</resources>
diff --git a/quickstep/res/values-bg/strings.xml b/quickstep/res/values-bg/strings.xml
index 5291cad..506d5a7 100644
--- a/quickstep/res/values-bg/strings.xml
+++ b/quickstep/res/values-bg/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Преместване долу/вдясно"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Показване на още # приложение.}other{Показване на още # приложения.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> и <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Приложението се добавя на настолния компютър"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Отказ"</string>
</resources>
diff --git a/quickstep/res/values-bn/strings.xml b/quickstep/res/values-bn/strings.xml
index 7d8316c..b114620 100644
--- a/quickstep/res/values-bn/strings.xml
+++ b/quickstep/res/values-bn/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"নিচে/ডানদিকে সরান"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{আরও #টি অ্যাপ দেখুন।}one{আরও #টি অ্যাপ দেখুন।}other{আরও #টি অ্যাপ দেখুন।}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> ও <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"ডেস্কটপে অ্যাপ যোগ করা হচ্ছে"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"বাতিল করুন"</string>
</resources>
diff --git a/quickstep/res/values-bs/strings.xml b/quickstep/res/values-bs/strings.xml
index e8f82f8..193476e 100644
--- a/quickstep/res/values-bs/strings.xml
+++ b/quickstep/res/values-bs/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Premjesti dolje desno"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Prikaži još # aplikaciju.}one{Prikaži još # aplikaciju.}few{Prikaži još # aplikacije.}other{Prikaži još # aplikacija.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> i <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Dodavanje aplikacije na radnu površinu"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Otkaži"</string>
</resources>
diff --git a/quickstep/res/values-ca/strings.xml b/quickstep/res/values-ca/strings.xml
index 785e5b1..32a338e 100644
--- a/quickstep/res/values-ca/strings.xml
+++ b/quickstep/res/values-ca/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mou a la part inferior o a la dreta"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Mostra # aplicació més.}other{Mostra # aplicacions més.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> i <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"S\'està afegint l\'aplicació a l\'ordinador"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Cancel·la"</string>
</resources>
diff --git a/quickstep/res/values-cs/strings.xml b/quickstep/res/values-cs/strings.xml
index 40f8c3e..afb026e 100644
--- a/quickstep/res/values-cs/strings.xml
+++ b/quickstep/res/values-cs/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Přesunout doprava dolů"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Zobrazit # další aplikaci.}few{Zobrazit # další aplikace.}many{Zobrazit # další aplikace.}other{Zobrazit # dalších aplikací.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> a <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Přidání aplikace na plochu"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Zrušit"</string>
</resources>
diff --git a/quickstep/res/values-da/strings.xml b/quickstep/res/values-da/strings.xml
index 381ef14..4766523 100644
--- a/quickstep/res/values-da/strings.xml
+++ b/quickstep/res/values-da/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Flyt til bunden eller højre side"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Vis # app mere.}one{Vis # app mere.}other{Vis # apps mere.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> og <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Appen føjes til computeren"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Annuller"</string>
</resources>
diff --git a/quickstep/res/values-de/strings.xml b/quickstep/res/values-de/strings.xml
index e68046b..1eedbb8 100644
--- a/quickstep/res/values-de/strings.xml
+++ b/quickstep/res/values-de/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Nach unten / Nach rechts verschieben"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{# weitere App anzeigen.}other{# weitere Apps anzeigen.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> und <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Hinzufügen einer App zum Desktop"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Abbrechen"</string>
</resources>
diff --git a/quickstep/res/values-el/strings.xml b/quickstep/res/values-el/strings.xml
index d1c16ba..3ea7c1e 100644
--- a/quickstep/res/values-el/strings.xml
+++ b/quickstep/res/values-el/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Μετακίνηση κάτω/δεξιά"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Εμφάνιση # ακόμα εφαρμογής.}other{Εμφάνιση # ακόμα εφαρμογών.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> και <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Γίνεται προσθήκη εφαρμογής στον υπολογιστή"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Ακύρωση"</string>
</resources>
diff --git a/quickstep/res/values-en-rAU/strings.xml b/quickstep/res/values-en-rAU/strings.xml
index 9d6b73b..db6a6cc 100644
--- a/quickstep/res/values-en-rAU/strings.xml
+++ b/quickstep/res/values-en-rAU/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Move to bottom/right"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Show # more app.}other{Show # more apps.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> and <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Adding app to desktop"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Cancel"</string>
</resources>
diff --git a/quickstep/res/values-en-rCA/strings.xml b/quickstep/res/values-en-rCA/strings.xml
index 0def6cf..2e3010e 100644
--- a/quickstep/res/values-en-rCA/strings.xml
+++ b/quickstep/res/values-en-rCA/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Move to bottom/right"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Show # more app.}other{Show # more apps.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> and <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Adding app to Desktop"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Cancel"</string>
</resources>
diff --git a/quickstep/res/values-en-rGB/strings.xml b/quickstep/res/values-en-rGB/strings.xml
index 9d6b73b..db6a6cc 100644
--- a/quickstep/res/values-en-rGB/strings.xml
+++ b/quickstep/res/values-en-rGB/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Move to bottom/right"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Show # more app.}other{Show # more apps.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> and <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Adding app to desktop"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Cancel"</string>
</resources>
diff --git a/quickstep/res/values-en-rIN/strings.xml b/quickstep/res/values-en-rIN/strings.xml
index 9d6b73b..db6a6cc 100644
--- a/quickstep/res/values-en-rIN/strings.xml
+++ b/quickstep/res/values-en-rIN/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Move to bottom/right"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Show # more app.}other{Show # more apps.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> and <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Adding app to desktop"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Cancel"</string>
</resources>
diff --git a/quickstep/res/values-en-rXC/strings.xml b/quickstep/res/values-en-rXC/strings.xml
index 54b746b..342d580 100644
--- a/quickstep/res/values-en-rXC/strings.xml
+++ b/quickstep/res/values-en-rXC/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Move to bottom/right"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Show # more app.}other{Show # more apps.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> and <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Adding app to Desktop"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Cancel"</string>
</resources>
diff --git a/quickstep/res/values-es-rUS/strings.xml b/quickstep/res/values-es-rUS/strings.xml
index a522e50..98d662b 100644
--- a/quickstep/res/values-es-rUS/strings.xml
+++ b/quickstep/res/values-es-rUS/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mover a la parte inferior o derecha"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Mostrar # app más.}other{Mostrar # apps más.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> y <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Agregando app al escritorio"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Cancelar"</string>
</resources>
diff --git a/quickstep/res/values-es/strings.xml b/quickstep/res/values-es/strings.xml
index aef1258..3377381 100644
--- a/quickstep/res/values-es/strings.xml
+++ b/quickstep/res/values-es/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mover abajo/a la derecha"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Mostrar # aplicación más.}other{Mostrar # aplicaciones más.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> y <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Añadiendo aplicación al ordenador"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Cancelar"</string>
</resources>
diff --git a/quickstep/res/values-et/strings.xml b/quickstep/res/values-et/strings.xml
index e1aaa9a..ed17516 100644
--- a/quickstep/res/values-et/strings.xml
+++ b/quickstep/res/values-et/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Teisalda alla/paremale"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Kuva veel # rakendus.}other{Kuva veel # rakendust.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> ja <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Rakenduse lisamine arvutisse"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Tühista"</string>
</resources>
diff --git a/quickstep/res/values-eu/strings.xml b/quickstep/res/values-eu/strings.xml
index e3ac4ec..9293207 100644
--- a/quickstep/res/values-eu/strings.xml
+++ b/quickstep/res/values-eu/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Eraman behera, eskuinetara"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Erakutsi beste # aplikazio.}other{Erakutsi beste # aplikazio.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> eta <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Aplikazioa mahaigainean gehitzen"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Utzi"</string>
</resources>
diff --git a/quickstep/res/values-fa/strings.xml b/quickstep/res/values-fa/strings.xml
index 858c814..879796a 100644
--- a/quickstep/res/values-fa/strings.xml
+++ b/quickstep/res/values-fa/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"انتقال به پایین/ راست"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{نمایش # برنامه دیگر.}one{نمایش # برنامه دیگر.}other{نمایش # برنامه دیگر.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> و <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"درحال افزودن برنامه به رایانه"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"لغو"</string>
</resources>
diff --git a/quickstep/res/values-fi/strings.xml b/quickstep/res/values-fi/strings.xml
index 748a245..02c4ad3 100644
--- a/quickstep/res/values-fi/strings.xml
+++ b/quickstep/res/values-fi/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Siirrä alas tai oikealle"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Näytä # muu sovellus.}other{Näytä # muuta sovellusta.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> ja <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Sovelluksen lisääminen työpöydälle"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Peru"</string>
</resources>
diff --git a/quickstep/res/values-fr-rCA/strings.xml b/quickstep/res/values-fr-rCA/strings.xml
index 4029bb2..b280d48 100644
--- a/quickstep/res/values-fr-rCA/strings.xml
+++ b/quickstep/res/values-fr-rCA/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Déplacer vers le coin inférieur droit de l\'écran"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Afficher # autre application.}one{Afficher # autre application.}other{Afficher # autres applications.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> et <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Ajout de l\'application au bureau en cours…"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Annuler"</string>
</resources>
diff --git a/quickstep/res/values-fr/strings.xml b/quickstep/res/values-fr/strings.xml
index b86c18f..868188b 100644
--- a/quickstep/res/values-fr/strings.xml
+++ b/quickstep/res/values-fr/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Déplacer en bas ou à droite"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Afficher # autre appli.}one{Afficher # autre appli.}other{Afficher # autre applis.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> et <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Ajout de l\'appli au bureau"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Annuler"</string>
</resources>
diff --git a/quickstep/res/values-gl/strings.xml b/quickstep/res/values-gl/strings.xml
index 53bc4d9..8248aa1 100644
--- a/quickstep/res/values-gl/strings.xml
+++ b/quickstep/res/values-gl/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mover á parte inferior ou á dereita"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Mostrar # aplicación máis.}other{Mostrar # aplicacións máis.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> e <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Engadindo aplicación ao ordenador"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Cancelar"</string>
</resources>
diff --git a/quickstep/res/values-gu/strings.xml b/quickstep/res/values-gu/strings.xml
index 519affd..db41a1e 100644
--- a/quickstep/res/values-gu/strings.xml
+++ b/quickstep/res/values-gu/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"સૌથી નીચે જમણી બાજુએ ખસેડો"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{વધુ # ઍપ બતાવો.}one{વધુ # ઍપ બતાવો.}other{વધુ # ઍપ બતાવો.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> અને <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"ડેસ્કટૉપ પર ઍપ ઉમેરી રહ્યાં છીએ"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"રદ કરો"</string>
</resources>
diff --git a/quickstep/res/values-hi/strings.xml b/quickstep/res/values-hi/strings.xml
index 21b9996..2b58605 100644
--- a/quickstep/res/values-hi/strings.xml
+++ b/quickstep/res/values-hi/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"नीचे/दाईं तरफ़ ले जाएं"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{# और ऐप्लिकेशन दिखाएं.}one{# और ऐप्लिकेशन दिखाएं.}other{# और ऐप्लिकेशन दिखाएं.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> और <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"डेस्कटॉप पर ऐप्लिकेशन जोड़ा जा रहा है"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"रद्द करें"</string>
</resources>
diff --git a/quickstep/res/values-hr/strings.xml b/quickstep/res/values-hr/strings.xml
index 19786cd..6c9172f 100644
--- a/quickstep/res/values-hr/strings.xml
+++ b/quickstep/res/values-hr/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Premjesti dolje/desno"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Prikaži više aplikacija (još #).}one{Prikaži više aplikacija (još #).}few{Prikaži više aplikacija (još #).}other{Prikaži više aplikacija (još #).}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> i <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Dodavanje aplikacije na radnu površinu"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Odustani"</string>
</resources>
diff --git a/quickstep/res/values-hu/strings.xml b/quickstep/res/values-hu/strings.xml
index 6e4f860..3f4ad18 100644
--- a/quickstep/res/values-hu/strings.xml
+++ b/quickstep/res/values-hu/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mozgatás alulra vagy a jobb oldalra"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{# további alkalmazás megjelenítése.}other{# további alkalmazás megjelenítése.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> és <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Alkalmazás hozzáadása az asztalhoz"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Mégse"</string>
</resources>
diff --git a/quickstep/res/values-hy/strings.xml b/quickstep/res/values-hy/strings.xml
index 5240c82..1521c53 100644
--- a/quickstep/res/values-hy/strings.xml
+++ b/quickstep/res/values-hy/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Տեղափոխել ներքևի աջ անկյուն"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Ցուցադրել ևս # հավելված։}one{Ցուցադրել ևս # հավելված։}other{Ցուցադրել ևս # հավելված։}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> և <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Հավելվածն ավելացվում է աշխատասեղանին"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Չեղարկել"</string>
</resources>
diff --git a/quickstep/res/values-in/strings.xml b/quickstep/res/values-in/strings.xml
index 7945b26..02fead4 100644
--- a/quickstep/res/values-in/strings.xml
+++ b/quickstep/res/values-in/strings.xml
@@ -85,7 +85,7 @@
<string name="gesture_tutorial_try_again" msgid="65962545858556697">"Coba lagi"</string>
<string name="gesture_tutorial_nice" msgid="2936275692616928280">"Bagus!"</string>
<string name="gesture_tutorial_step" msgid="1279786122817620968">"Tutorial <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
- <string name="allset_title" msgid="5021126669778966707">"Semua siap."</string>
+ <string name="allset_title" msgid="5021126669778966707">"Selesai!"</string>
<string name="allset_hint" msgid="459504134589971527">"Geser ke atas untuk membuka Layar utama"</string>
<string name="allset_button_hint" msgid="2395219947744706291">"Ketuk tombol layar utama untuk membuka layar utama"</string>
<string name="allset_description_generic" msgid="5385500062202019855">"Anda sudah siap untuk mulai menggunakan <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Pindahkan ke bawah/kanan"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Tampilkan # aplikasi lain.}other{Tampilkan # aplikasi lain.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> dan <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Menambahkan aplikasi ke Desktop"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Batalkan"</string>
</resources>
diff --git a/quickstep/res/values-is/strings.xml b/quickstep/res/values-is/strings.xml
index 3ff7185..8b840ce 100644
--- a/quickstep/res/values-is/strings.xml
+++ b/quickstep/res/values-is/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Færa neðst/til hægri"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Sýna # forrit í viðbót.}one{Sýna # forrit í viðbót.}other{Sýna # forrit í viðbót.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> og <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Forriti bætt við skjáborð"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Hætta við"</string>
</resources>
diff --git a/quickstep/res/values-it/strings.xml b/quickstep/res/values-it/strings.xml
index 5777911..f5b4c5c 100644
--- a/quickstep/res/values-it/strings.xml
+++ b/quickstep/res/values-it/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Sposta in basso/a destra"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Mostra # altra app.}other{Mostra altre # app.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> e <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Aggiunta app a desktop in corso…"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Annulla"</string>
</resources>
diff --git a/quickstep/res/values-iw/strings.xml b/quickstep/res/values-iw/strings.xml
index 6ae6bec..27b053a 100644
--- a/quickstep/res/values-iw/strings.xml
+++ b/quickstep/res/values-iw/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"העברה לפינה הימנית/התחתונה"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{הצגת אפליקציה אחת (#) נוספת.}one{הצגת # אפליקציות נוספות.}two{הצגת # אפליקציות נוספות.}other{הצגת # אפליקציות נוספות.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> ו-<xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"האפליקציה מתווספת לשולחן העבודה"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"ביטול"</string>
</resources>
diff --git a/quickstep/res/values-ja/strings.xml b/quickstep/res/values-ja/strings.xml
index 13a3425..f8ad440 100644
--- a/quickstep/res/values-ja/strings.xml
+++ b/quickstep/res/values-ja/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"下 / 右に移動"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{他 # 件のアプリを表示できます。}other{他 # 件のアプリを表示できます。}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> と <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"アプリをデスクトップに追加する"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"キャンセル"</string>
</resources>
diff --git a/quickstep/res/values-ka/strings.xml b/quickstep/res/values-ka/strings.xml
index d987ac0..57b8947 100644
--- a/quickstep/res/values-ka/strings.xml
+++ b/quickstep/res/values-ka/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ქვემოთ/მარჯვნივ გადატანა"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{#-ით მეტი აპის ჩენება}other{#-ით მეტი აპის ჩვენება.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> და <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"მიმდინარეობს აპის დესკტოპზე დამატება"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"გაუქმება"</string>
</resources>
diff --git a/quickstep/res/values-kk/strings.xml b/quickstep/res/values-kk/strings.xml
index 34d68e4..fd8db85 100644
--- a/quickstep/res/values-kk/strings.xml
+++ b/quickstep/res/values-kk/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Төмен/оңға жылжыту"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Тағы # қолданбаны көрсету.}other{Тағы # қолданбаны көрсету.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> және <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Жұмыс үстеліне қолданба қосу"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Бас тарту"</string>
</resources>
diff --git a/quickstep/res/values-km/strings.xml b/quickstep/res/values-km/strings.xml
index 0b12d79..4f11f16 100644
--- a/quickstep/res/values-km/strings.xml
+++ b/quickstep/res/values-km/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ផ្លាស់ទីទៅខាងក្រោម/ស្ដាំ"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{បង្ហាញកម្មវិធី # ទៀត។}other{បង្ហាញកម្មវិធី # ទៀត។}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> និង <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"កំពុងបញ្ចូលកម្មវិធីទៅកុំព្យូទ័រ"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"បោះបង់"</string>
</resources>
diff --git a/quickstep/res/values-kn/strings.xml b/quickstep/res/values-kn/strings.xml
index 0bf25df..c155b38 100644
--- a/quickstep/res/values-kn/strings.xml
+++ b/quickstep/res/values-kn/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ಕೆಳಗಿನ/ಬಲಭಾಗಕ್ಕೆ ಸರಿಸಿ"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{ಇನ್ನೂ # ಆ್ಯಪ್ ಅನ್ನು ತೋರಿಸಿ.}one{ಇನ್ನೂ # ಆ್ಯಪ್ಗಳನ್ನು ತೋರಿಸಿ.}other{ಇನ್ನೂ # ಆ್ಯಪ್ಗಳನ್ನು ತೋರಿಸಿ.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> ಮತ್ತು <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"ಡೆಸ್ಕ್ಟಾಪ್ಗೆ ಆ್ಯಪ್ ಅನ್ನು ಸೇರಿಸಲಾಗುತ್ತಿದೆ"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"ರದ್ದುಮಾಡಿ"</string>
</resources>
diff --git a/quickstep/res/values-ko/strings.xml b/quickstep/res/values-ko/strings.xml
index 45618b9..5b23744 100644
--- a/quickstep/res/values-ko/strings.xml
+++ b/quickstep/res/values-ko/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"하단/오른쪽으로 이동"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{앱 #개 더 표시}other{앱 #개 더 표시}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> 및 <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"데스크톱에 앱 추가하기"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"취소"</string>
</resources>
diff --git a/quickstep/res/values-ky/strings.xml b/quickstep/res/values-ky/strings.xml
index 7c74fa6..3b3f92f 100644
--- a/quickstep/res/values-ky/strings.xml
+++ b/quickstep/res/values-ky/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Төмөнкү/оң бурчка жылдыруу"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Дагы # колдонмону көрсөтүү.}other{Дагы # колдонмону көрсөтүү.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> жана <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Колдонмону иш тактага кошуу"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Жокко чыгаруу"</string>
</resources>
diff --git a/quickstep/res/values-lo/strings.xml b/quickstep/res/values-lo/strings.xml
index 66db658..2ab0d74 100644
--- a/quickstep/res/values-lo/strings.xml
+++ b/quickstep/res/values-lo/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ຍ້າຍໄປຂວາ/ລຸ່ມ"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{ສະແດງອີກ # ແອັບ.}other{ສະແດງອີກ # ແອັບ.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> ແລະ <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"ການເພີ່ມແອັບໄປໃສ່ເດັສທັອບ"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"ຍົກເລີກ"</string>
</resources>
diff --git a/quickstep/res/values-lt/strings.xml b/quickstep/res/values-lt/strings.xml
index 91909e9..5dd1a3b 100644
--- a/quickstep/res/values-lt/strings.xml
+++ b/quickstep/res/values-lt/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Perkelti žemyn, dešinėn"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Rodyti dar # programą.}one{Rodyti dar # programą.}few{Rodyti dar # programas.}many{Rodyti dar # programos.}other{Rodyti dar # programų.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"„<xliff:g id="APP_NAME_1">%1$s</xliff:g>“ ir „<xliff:g id="APP_NAME_2">%2$s</xliff:g>“"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Pridedama programa prie darbalaukio"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Atšaukti"</string>
</resources>
diff --git a/quickstep/res/values-lv/strings.xml b/quickstep/res/values-lv/strings.xml
index c3cd847..a69fe5f 100644
--- a/quickstep/res/values-lv/strings.xml
+++ b/quickstep/res/values-lv/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Pārvietot uz apakšējo/labo stūri"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Rādīt vēl # lietotni}zero{Rādīt vēl # lietotnes}one{Rādīt vēl # lietotni}other{Rādīt vēl # lietotnes}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"“<xliff:g id="APP_NAME_1">%1$s</xliff:g>” un “<xliff:g id="APP_NAME_2">%2$s</xliff:g>”"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Notiek lietotnes pievienošana datoram"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Atcelt"</string>
</resources>
diff --git a/quickstep/res/values-mk/strings.xml b/quickstep/res/values-mk/strings.xml
index 0268d79..9b16ea9 100644
--- a/quickstep/res/values-mk/strings.xml
+++ b/quickstep/res/values-mk/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Премести долу десно"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Прикажи уште # апликација.}one{Прикажи уште # апликација.}other{Прикажи уште # апликации.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> и <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Додавање на апликацијата во „Работна површина“"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Откажи"</string>
</resources>
diff --git a/quickstep/res/values-ml/strings.xml b/quickstep/res/values-ml/strings.xml
index b6ad6ac..6d81f05 100644
--- a/quickstep/res/values-ml/strings.xml
+++ b/quickstep/res/values-ml/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"താഴേക്കോ വലത്തേക്കോ നീക്കുക"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{# ആപ്പ് കൂടി കാണിക്കുക.}other{# ആപ്പുകൾ കൂടി കാണിക്കുക.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g>, <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"ആപ്പ് ഡെസ്ക്ടോപ്പിലേക്ക് ചേർക്കുന്നു"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"റദ്ദാക്കുക"</string>
</resources>
diff --git a/quickstep/res/values-mn/strings.xml b/quickstep/res/values-mn/strings.xml
index 8f82ebd..703bf7b 100644
--- a/quickstep/res/values-mn/strings.xml
+++ b/quickstep/res/values-mn/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Баруун доод хэсэг рүү зөөх"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Өөр # аппыг харуулна уу.}other{Өөр # аппыг харуулна уу.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> болон <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Компьютерт апп нэмж байна"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Цуцлах"</string>
</resources>
diff --git a/quickstep/res/values-mr/strings.xml b/quickstep/res/values-mr/strings.xml
index 73260b2..8cefec4 100644
--- a/quickstep/res/values-mr/strings.xml
+++ b/quickstep/res/values-mr/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"तळाशी/उजवीकडे हलवा"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{आणखी # अॅप दाखवा.}other{आणखी # अॅप्स दाखवा.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> आणि <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"डेस्कटॉपवर ॲप जोडत आहे"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"रद्द करा"</string>
</resources>
diff --git a/quickstep/res/values-ms/strings.xml b/quickstep/res/values-ms/strings.xml
index 96857c9..a62a24c 100644
--- a/quickstep/res/values-ms/strings.xml
+++ b/quickstep/res/values-ms/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Alihkan ke bawah/kanan"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Tunjukkan # lagi apl.}other{Tunjukkan # lagi apl.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> dan <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Menambahkan apl pada Desktop"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Batal"</string>
</resources>
diff --git a/quickstep/res/values-my/strings.xml b/quickstep/res/values-my/strings.xml
index 9e3c739..a2927d4 100644
--- a/quickstep/res/values-my/strings.xml
+++ b/quickstep/res/values-my/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"အောက်ခြေ/ညာဘက်သို့ ရွှေ့ရန်"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{နောက်ထပ်အက်ပ် # ခု ပြပါ။}other{နောက်ထပ်အက်ပ် # ခု ပြပါ။}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> နှင့် <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"‘ဒက်စ်တော့’ တွင် အက်ပ်ကို ထည့်ခြင်း"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"မလုပ်တော့"</string>
</resources>
diff --git a/quickstep/res/values-nb/strings.xml b/quickstep/res/values-nb/strings.xml
index 5fe4abd..fe679e7 100644
--- a/quickstep/res/values-nb/strings.xml
+++ b/quickstep/res/values-nb/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Flytt til nederst/høyre"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Vis # app til.}other{Vis # apper til.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> og <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Legg til apper på datamaskin"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Avbryt"</string>
</resources>
diff --git a/quickstep/res/values-ne/strings.xml b/quickstep/res/values-ne/strings.xml
index 7a86f28..01b79d4 100644
--- a/quickstep/res/values-ne/strings.xml
+++ b/quickstep/res/values-ne/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"फेद/दायाँतिर सार्नुहोस्"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{थप # एप देखाइयोस्।}other{थप # वटा एप देखाइयोस्।}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> र <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"डेस्कटपमा एप हालिँदै छ"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"रद्द गर्नुहोस्"</string>
</resources>
diff --git a/quickstep/res/values-nl/strings.xml b/quickstep/res/values-nl/strings.xml
index 4e38c80..774a66c 100644
--- a/quickstep/res/values-nl/strings.xml
+++ b/quickstep/res/values-nl/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Naar beneden/rechts verplaatsen"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Nog # app tonen.}other{Nog # apps tonen.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> en <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"App toevoegen aan desktop"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Annuleren"</string>
</resources>
diff --git a/quickstep/res/values-or/strings.xml b/quickstep/res/values-or/strings.xml
index b73995c..203b149 100644
--- a/quickstep/res/values-or/strings.xml
+++ b/quickstep/res/values-or/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ନିମ୍ନ/ଡାହାଣକୁ ମୁଭ କରନ୍ତୁ"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{ଅଧିକ #ଟି ଆପ ଦେଖାନ୍ତୁ।}other{ଅଧିକ #ଟି ଆପ୍ସ ଦେଖାନ୍ତୁ।}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> ଏବଂ <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"ଡେସ୍କଟପରେ ଆପ ଯୋଗ କରାଯାଉଛି"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"ବାତିଲ କରନ୍ତୁ"</string>
</resources>
diff --git a/quickstep/res/values-pa/strings.xml b/quickstep/res/values-pa/strings.xml
index 88bc4eb..d0f17b0 100644
--- a/quickstep/res/values-pa/strings.xml
+++ b/quickstep/res/values-pa/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ਹੇਠਾਂ/ਸੱਜੇ ਪਾਸੇ ਲੈ ਕੇ ਜਾਓ"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{# ਹੋਰ ਐਪ ਦਿਖਾਓ।}one{# ਹੋਰ ਐਪ ਦਿਖਾਓ।}other{# ਹੋਰ ਐਪਾਂ ਦਿਖਾਓ।}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> ਅਤੇ <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"ਐਪ ਨੂੰ ਡੈਸਕਟਾਪ \'ਤੇ ਸ਼ਾਮਲ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"ਰੱਦ ਕਰੋ"</string>
</resources>
diff --git a/quickstep/res/values-pl/strings.xml b/quickstep/res/values-pl/strings.xml
index 031f2cd..40aba40 100644
--- a/quickstep/res/values-pl/strings.xml
+++ b/quickstep/res/values-pl/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Przesuń w dolny prawy róg"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Pokaż jeszcze # aplikację.}few{Pokaż jeszcze # aplikacje.}many{Pokaż jeszcze # aplikacji.}other{Pokaż jeszcze # aplikacji.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> i <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Dodaję aplikację do komputera"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Anuluj"</string>
</resources>
diff --git a/quickstep/res/values-pt-rPT/strings.xml b/quickstep/res/values-pt-rPT/strings.xml
index 1976cb4..19b7e4b 100644
--- a/quickstep/res/values-pt-rPT/strings.xml
+++ b/quickstep/res/values-pt-rPT/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mover para a part superior direita"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Mostrar mais # app.}other{Mostrar mais # apps.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> e <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"A adicionar a app ao computador"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Cancelar"</string>
</resources>
diff --git a/quickstep/res/values-pt/strings.xml b/quickstep/res/values-pt/strings.xml
index ac20911..75790ce 100644
--- a/quickstep/res/values-pt/strings.xml
+++ b/quickstep/res/values-pt/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mover para baixo/para a direita"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Mostrar mais # app.}one{Mostrar mais # app.}other{Mostrar mais # apps.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> e <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Adicionando app ao computador"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Cancelar"</string>
</resources>
diff --git a/quickstep/res/values-ro/strings.xml b/quickstep/res/values-ro/strings.xml
index 1948599..459abee 100644
--- a/quickstep/res/values-ro/strings.xml
+++ b/quickstep/res/values-ro/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mută în dreapta jos"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Afișează încă # aplicație}few{Afișează încă # aplicații}other{Afișează încă # de aplicații}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> și <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Se adaugă aplicația pe computer"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Anulează"</string>
</resources>
diff --git a/quickstep/res/values-ru/strings.xml b/quickstep/res/values-ru/strings.xml
index c62fcca..2cece97 100644
--- a/quickstep/res/values-ru/strings.xml
+++ b/quickstep/res/values-ru/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Переместить вниз или вправо"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Показать ещё # приложение}one{Показать ещё # приложение}few{Показать ещё # приложения}many{Показать ещё # приложений}other{Показать ещё # приложения}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> и <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Добавление приложения на компьютер"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Отмена"</string>
</resources>
diff --git a/quickstep/res/values-si/strings.xml b/quickstep/res/values-si/strings.xml
index 2c1e67d..b8d5110 100644
--- a/quickstep/res/values-si/strings.xml
+++ b/quickstep/res/values-si/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"පහළ/දකුණ වෙත ගෙන යන්න"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{තවත් # යෙදුමක් පෙන්වන්න.}one{තවත් යෙදුම් #ක් පෙන්වන්න.}other{තවත් යෙදුම් #ක් පෙන්වන්න.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> සහ <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"ඩෙස්ක්ටොප් වෙත යෙදුම එක් කිරීම"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"අවලංගු කරන්න"</string>
</resources>
diff --git a/quickstep/res/values-sk/strings.xml b/quickstep/res/values-sk/strings.xml
index 409f776..b34cf93 100644
--- a/quickstep/res/values-sk/strings.xml
+++ b/quickstep/res/values-sk/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Presunúť dole alebo doprava"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Zobraziť # ďalšiu aplikáciu.}few{Zobraziť # ďalšie aplikácie.}many{Show # more apps.}other{Zobraziť # ďalších aplikácií.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> a <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Pridanie aplikácie na plochu"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Zrušiť"</string>
</resources>
diff --git a/quickstep/res/values-sl/strings.xml b/quickstep/res/values-sl/strings.xml
index e72dac1..a1b2021 100644
--- a/quickstep/res/values-sl/strings.xml
+++ b/quickstep/res/values-sl/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Premakni na dno/desno"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Pokaži še # aplikacijo.}one{Pokaži še # aplikacijo.}two{Pokaži še # aplikaciji.}few{Pokaži še # aplikacije.}other{Pokaži še # aplikacij.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> in <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Dodajanje aplikacije na namizje"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Prekliči"</string>
</resources>
diff --git a/quickstep/res/values-sq/strings.xml b/quickstep/res/values-sq/strings.xml
index 6f661c3..392c7fd 100644
--- a/quickstep/res/values-sq/strings.xml
+++ b/quickstep/res/values-sq/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Lëviz në fund/djathtas"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Shfaq # aplikacion tjetër.}other{Shfaq # aplikacione të tjera.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> dhe <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Shtimi i aplikacionit te desktopi"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Anulo"</string>
</resources>
diff --git a/quickstep/res/values-sr/strings.xml b/quickstep/res/values-sr/strings.xml
index c985e08..cc12985 100644
--- a/quickstep/res/values-sr/strings.xml
+++ b/quickstep/res/values-sr/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Премести доле десно"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Прикажи још # апликацију.}one{Прикажи још # апликацију.}few{Прикажи још # апликације.}other{Прикажи још # апликација.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> и <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Додаје се апликација на радну поврршину"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Откажи"</string>
</resources>
diff --git a/quickstep/res/values-sv/strings.xml b/quickstep/res/values-sv/strings.xml
index 7e97556..6164140 100644
--- a/quickstep/res/values-sv/strings.xml
+++ b/quickstep/res/values-sv/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Flytta längst ned/till höger"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Visa # app till.}other{Visa # appar till.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> och <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Lägger till appen på skrivbordet"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Avbryt"</string>
</resources>
diff --git a/quickstep/res/values-sw/strings.xml b/quickstep/res/values-sw/strings.xml
index 983964b..5dbd510 100644
--- a/quickstep/res/values-sw/strings.xml
+++ b/quickstep/res/values-sw/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Sogeza chini/kulia"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Onyesha programu # zaidi.}other{Onyesha programu # zaidi.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> na <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Kuweka programu kwenye Eneo-kazi"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Ghairi"</string>
</resources>
diff --git a/quickstep/res/values-ta/strings.xml b/quickstep/res/values-ta/strings.xml
index 2016c9f..6846677 100644
--- a/quickstep/res/values-ta/strings.xml
+++ b/quickstep/res/values-ta/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"கீழே/வலதுபுறம் நகர்த்தும்"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{மேலும் # ஆப்ஸைக் காட்டு.}other{மேலும் # ஆப்ஸைக் காட்டு.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> மற்றும் <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"ஆப்ஸை டெஸ்க்டாப்பில் சேர்க்கிறது"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"ரத்துசெய்"</string>
</resources>
diff --git a/quickstep/res/values-te/strings.xml b/quickstep/res/values-te/strings.xml
index d7bd537..f80153c 100644
--- a/quickstep/res/values-te/strings.xml
+++ b/quickstep/res/values-te/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"దిగువ/కుడి వైపునకు తరలించండి"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{మరో # యాప్ను చూడండి.}other{మరో # యాప్లను చూడండి.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g>, <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"డెస్క్టాప్నకు యాప్ను జోడిస్తోంది"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"రద్దు చేయండి"</string>
</resources>
diff --git a/quickstep/res/values-th/strings.xml b/quickstep/res/values-th/strings.xml
index d6f1313..3202588 100644
--- a/quickstep/res/values-th/strings.xml
+++ b/quickstep/res/values-th/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ย้ายไปที่ด้านล่างหรือด้านขวา"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{แสดงเพิ่มเติมอีก # แอป}other{แสดงเพิ่มเติมอีก # แอป}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> และ <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"การเพิ่มแอปไปยังเดสก์ท็อป"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"ยกเลิก"</string>
</resources>
diff --git a/quickstep/res/values-tl/strings.xml b/quickstep/res/values-tl/strings.xml
index 7185cb0..f1f3d3c 100644
--- a/quickstep/res/values-tl/strings.xml
+++ b/quickstep/res/values-tl/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Ilipat sa ibaba/kanan"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Magpakita ng # pang app.}one{Magpakita ng # pang app.}other{Magpakita ng # pang app.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> at <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Idinaragdag ang app sa Desktop"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Kanselahin"</string>
</resources>
diff --git a/quickstep/res/values-tr/strings.xml b/quickstep/res/values-tr/strings.xml
index 8923cf3..606eb9b 100644
--- a/quickstep/res/values-tr/strings.xml
+++ b/quickstep/res/values-tr/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Sağ alta taşı"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{# uygulama daha göster.}other{# uygulama daha göster}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> ve <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Uygulama Masaüstü\'ne ekleniyor"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"İptal"</string>
</resources>
diff --git a/quickstep/res/values-uk/strings.xml b/quickstep/res/values-uk/strings.xml
index 1f91165..011aadb 100644
--- a/quickstep/res/values-uk/strings.xml
+++ b/quickstep/res/values-uk/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Перемістити вниз або вправо"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Показати ще # додаток.}one{Показати ще # додаток.}few{Показати ще # додатки.}many{Показати ще # додатків.}other{Показати ще # додатка.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> та <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Встановлення додатка на комп’ютер"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Скасувати"</string>
</resources>
diff --git a/quickstep/res/values-ur/strings.xml b/quickstep/res/values-ur/strings.xml
index 504f3aa..0d58d48 100644
--- a/quickstep/res/values-ur/strings.xml
+++ b/quickstep/res/values-ur/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"نیچے/دائیں طرف منتقل کریں"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{# مزید ایپ دکھائیں۔}other{# مزید ایپس دکھائیں۔}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> اور <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"ڈیسک ٹاپ پر ایپ شامل کرنا"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"منسوخ کریں"</string>
</resources>
diff --git a/quickstep/res/values-uz/strings.xml b/quickstep/res/values-uz/strings.xml
index 9f00663..b450c6f 100644
--- a/quickstep/res/values-uz/strings.xml
+++ b/quickstep/res/values-uz/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Pastga yoki oʻngga oʻtkazish"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Yana # ta ilovani chiqarish}other{Yana # ta ilovani chiqarish}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> va <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Ilova kompyuterga qoʻshilmoqda"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Bekor qilish"</string>
</resources>
diff --git a/quickstep/res/values-vi/strings.xml b/quickstep/res/values-vi/strings.xml
index 48bc1cb..2f0e0b9 100644
--- a/quickstep/res/values-vi/strings.xml
+++ b/quickstep/res/values-vi/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Chuyển xuống dưới cùng/sang bên phải"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Hiện thêm # ứng dụng.}other{Hiện thêm # ứng dụng.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> và <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Đang thêm ứng dụng vào máy tính"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Huỷ"</string>
</resources>
diff --git a/quickstep/res/values-zh-rCN/strings.xml b/quickstep/res/values-zh-rCN/strings.xml
index 2423ab7..b5798ba 100644
--- a/quickstep/res/values-zh-rCN/strings.xml
+++ b/quickstep/res/values-zh-rCN/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"移到底部/右侧"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{显示另外 # 个应用。}other{显示另外 # 个应用。}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g>和<xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"将应用添加到桌面"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"取消"</string>
</resources>
diff --git a/quickstep/res/values-zh-rHK/strings.xml b/quickstep/res/values-zh-rHK/strings.xml
index 5eaf95e..523ab65 100644
--- a/quickstep/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-zh-rHK/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"移至底部/右側"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{顯示另外 # 個應用程式。}other{顯示另外 # 個應用程式。}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"「<xliff:g id="APP_NAME_1">%1$s</xliff:g>」和「<xliff:g id="APP_NAME_2">%2$s</xliff:g>」"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"正在新增應用程式至桌面"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"取消"</string>
</resources>
diff --git a/quickstep/res/values-zh-rTW/strings.xml b/quickstep/res/values-zh-rTW/strings.xml
index b7e6e06..06ff8e0 100644
--- a/quickstep/res/values-zh-rTW/strings.xml
+++ b/quickstep/res/values-zh-rTW/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"移到底部/右側"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{顯示另外 # 個應用程式。}other{顯示另外 # 個應用程式。}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"「<xliff:g id="APP_NAME_1">%1$s</xliff:g>」和「<xliff:g id="APP_NAME_2">%2$s</xliff:g>」"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"新增應用程式至桌面"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"取消"</string>
</resources>
diff --git a/quickstep/res/values-zu/strings.xml b/quickstep/res/values-zu/strings.xml
index 9ca2106..fc9385f 100644
--- a/quickstep/res/values-zu/strings.xml
+++ b/quickstep/res/values-zu/strings.xml
@@ -128,4 +128,6 @@
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Hamba phansi/kwesokudla"</string>
<string name="quick_switch_overflow" msgid="6935266023013283353">"{count,plural, =1{Bonisa i-app e-# ngaphezulu.}one{Bonisa ama-app angu-# ngaphezulu.}other{Bonisa ama-app angu-# ngaphezulu.}}"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"I-<xliff:g id="APP_NAME_1">%1$s</xliff:g> ne-<xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
+ <string name="desktop_select_app_toast" msgid="2306057322833956910">"Yengeza i-app ku-Deskithophu"</string>
+ <string name="desktop_button_close_app_toast" msgid="5283096349579408560">"Khansela"</string>
</resources>
diff --git a/quickstep/res/values/colors.xml b/quickstep/res/values/colors.xml
index 36c7352..1b5b0ee 100644
--- a/quickstep/res/values/colors.xml
+++ b/quickstep/res/values/colors.xml
@@ -25,6 +25,10 @@
<!-- Taskbar -->
<color name="taskbar_nav_icon_selection_ripple">#E0E0E0</color>
+ <color name="taskbar_nav_icon_light_color_on_home">#ffffff</color>
+ <!-- The dark navigation button color is only used in the rare cases that taskbar isn't drawing
+ its background and the underlying app has requested dark buttons. -->
+ <color name="taskbar_nav_icon_dark_color_on_home">#99000000</color>
<color name="taskbar_stashed_handle_light_color">#EBffffff</color>
<color name="taskbar_stashed_handle_dark_color">#99000000</color>
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index bb4f74d..e4f6555 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -269,6 +269,9 @@
<dimen name="floating_rotation_button_taskbar_left_margin">20dp</dimen>
<dimen name="floating_rotation_button_taskbar_bottom_margin">10dp</dimen>
+ <!-- Copied from frameworks/base/packages/SystemUI -->
+ <dimen name="navigation_home_handle_width">108dp</dimen>
+
<!-- Taskbar -->
<dimen name="taskbar_size">@*android:dimen/taskbar_frame_height</dimen>
<dimen name="taskbar_ime_size">48dp</dimen>
@@ -360,6 +363,7 @@
<dimen name="bubblebar_icon_size">50dp</dimen>
<dimen name="bubblebar_badge_size">24dp</dimen>
<dimen name="bubblebar_icon_overlap">12dp</dimen>
+ <dimen name="bubblebar_overflow_inset">24dp</dimen>
<dimen name="bubblebar_icon_spacing">3dp</dimen>
<dimen name="bubblebar_icon_elevation">1dp</dimen>
@@ -383,4 +387,12 @@
<dimen name="keyboard_quick_switch_task_view_radius">16dp</dimen>
<dimen name="keyboard_quick_switch_no_recent_items_icon_size">24dp</dimen>
<dimen name="keyboard_quick_switch_no_recent_items_icon_margin">8dp</dimen>
+
+ <!-- Desktop mode -->
+ <dimen name="desktop_mode_floating_app_select_height">56dp</dimen>
+ <dimen name="desktop_mode_floating_app_select_elevation">4dp</dimen>
+ <dimen name="desktop_mode_floating_app_select_margin">16dp</dimen>
+ <dimen name="desktop_mode_floating_app_select_text_size">14sp</dimen>
+ <dimen name="desktop_mode_floating_app_select_text_margin">8dp</dimen>
+
</resources>
diff --git a/quickstep/res/values/override.xml b/quickstep/res/values/override.xml
index 4f472f0..67be0dd 100644
--- a/quickstep/res/values/override.xml
+++ b/quickstep/res/values/override.xml
@@ -25,6 +25,10 @@
<string name="model_delegate_class" translatable="false">com.android.launcher3.model.QuickstepModelDelegate</string>
+ <string name="nav_handle_long_press_handler_class" translatable="false"></string>
+
<string name="secondary_display_predictions_class" translatable="false">com.android.launcher3.secondarydisplay.SecondaryDisplayPredictionsImpl</string>
+ <string name="taskbar_model_callbacks_factory_class" translatable="false">com.android.launcher3.taskbar.TaskbarModelCallbacksFactory</string>
+
</resources>
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index 6d0dbae..77799e6 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -308,4 +308,10 @@
<!-- Accessibility label for quick switch tiles showing split tasks [CHAR LIMIT=NONE] -->
<string name="quick_switch_split_task"><xliff:g id="app_name_1" example="Chrome">%1$s</xliff:g> and <xliff:g id="app_name_2" example="Gmail">%2$s</xliff:g></string>
+
+ <!-- ******* Desktop ******* -->
+ <!-- Text shown in popup to choose a desktop app. [CHAR LIMIT=60] -->
+ <string name="desktop_select_app_toast">Adding app to Desktop</string>
+ <!-- Text shown on a button that closes the popup for choosing a desktop app. [CHAR_LIMIT=40] -->
+ <string name="desktop_button_close_app_toast">Cancel</string>
</resources>
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index e5fd605..c7325ba 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -30,6 +30,12 @@
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN;
import static android.window.TransitionFilter.CONTAINER_ORDER_TOP;
+import static com.android.app.animation.Interpolators.ACCELERATE_1_5;
+import static com.android.app.animation.Interpolators.AGGRESSIVE_EASE;
+import static com.android.app.animation.Interpolators.DECELERATE_1_5;
+import static com.android.app.animation.Interpolators.DECELERATE_1_7;
+import static com.android.app.animation.Interpolators.EXAGGERATED_EASE;
+import static com.android.app.animation.Interpolators.LINEAR;
import static com.android.launcher3.BaseActivity.INVISIBLE_ALL;
import static com.android.launcher3.BaseActivity.INVISIBLE_BY_APP_TRANSITIONS;
import static com.android.launcher3.BaseActivity.INVISIBLE_BY_PENDING_FLAGS;
@@ -41,12 +47,6 @@
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.Utilities.mapBoundToRange;
-import static com.android.launcher3.anim.Interpolators.ACCEL_1_5;
-import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
-import static com.android.launcher3.anim.Interpolators.DEACCEL_1_5;
-import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7;
-import static com.android.launcher3.anim.Interpolators.EXAGGERATED_EASE;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.config.FeatureFlags.ENABLE_BACK_SWIPE_HOME_ANIMATION;
import static com.android.launcher3.config.FeatureFlags.ENABLE_SCRIM_FOR_APP_LAUNCH;
import static com.android.launcher3.config.FeatureFlags.KEYGUARD_ANIMATION;
@@ -553,7 +553,7 @@
ObjectAnimator scaleAnim = ObjectAnimator.ofFloat(view, SCALE_PROPERTY, scales)
.setDuration(CONTENT_SCALE_DURATION);
- scaleAnim.setInterpolator(DEACCEL_1_5);
+ scaleAnim.setInterpolator(DECELERATE_1_5);
launcherAnimator.play(scaleAnim);
});
@@ -571,7 +571,7 @@
ObjectAnimator scrim = ObjectAnimator.ofArgb(scrimView, VIEW_BACKGROUND_COLOR,
colors);
scrim.setDuration(CONTENT_SCRIM_DURATION);
- scrim.setInterpolator(DEACCEL_1_5);
+ scrim.setInterpolator(DECELERATE_1_5);
launcherAnimator.play(scrim);
}
@@ -1462,11 +1462,11 @@
float startShadowRadius = areAllTargetsTranslucent(appTargets) ? 0 : mMaxShadowRadius;
closingAnimator.setDuration(duration);
closingAnimator.addUpdateListener(new MultiValueUpdateListener() {
- FloatProp mDy = new FloatProp(0, mClosingWindowTransY, 0, duration, DEACCEL_1_7);
- FloatProp mScale = new FloatProp(1f, 1f, 0, duration, DEACCEL_1_7);
+ FloatProp mDy = new FloatProp(0, mClosingWindowTransY, 0, duration, DECELERATE_1_7);
+ FloatProp mScale = new FloatProp(1f, 1f, 0, duration, DECELERATE_1_7);
FloatProp mAlpha = new FloatProp(1f, 0f, 25, 125, LINEAR);
FloatProp mShadowRadius = new FloatProp(startShadowRadius, 0, 0, duration,
- DEACCEL_1_7);
+ DECELERATE_1_7);
@Override
public void onUpdate(float percent, boolean initOnly) {
@@ -2032,7 +2032,7 @@
if (progress >= end) {
return 0f;
}
- return Utilities.mapToRange(progress, start, end, 1, 0, ACCEL_1_5);
+ return Utilities.mapToRange(progress, start, end, 1, 0, ACCELERATE_1_5);
}
}
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
index 048243e..b77c43f 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
@@ -95,9 +95,7 @@
}
}
if (pageId == -1) {
- pageId = LauncherSettings.Settings.call(mLauncher.getContentResolver(),
- LauncherSettings.Settings.METHOD_NEW_SCREEN_ID)
- .getInt(LauncherSettings.Settings.EXTRA_VALUE);
+ pageId = mLauncher.getModel().getModelDbController().getNewScreenId();
mNewScreens = IntArray.wrap(pageId);
}
boolean isPortrait = !mLauncher.getDeviceProfile().isVerticalBarLayout();
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
index 80bdb6f..bd47923 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
@@ -30,6 +30,7 @@
import android.widget.LinearLayout;
import android.widget.TextView;
+import com.android.app.animation.Interpolators;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.CellLayout;
import com.android.launcher3.DeviceProfile;
@@ -37,7 +38,6 @@
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
-import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.uioverrides.PredictedAppIcon;
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java
index 726abff..8c01d04 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java
@@ -22,9 +22,10 @@
import android.content.Context;
import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.LauncherModel;
import com.android.launcher3.model.GridBackupTable;
-import com.android.launcher3.provider.LauncherDbUtils;
+import com.android.launcher3.model.ModelDbController;
+import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
/**
* A helper class to manage migration revert restoration for hybrid hotseat
@@ -36,16 +37,13 @@
*/
public static void createBackup(Context context) {
MODEL_EXECUTOR.execute(() -> {
- try (LauncherDbUtils.SQLiteTransaction transaction = (LauncherDbUtils.SQLiteTransaction)
- LauncherSettings.Settings.call(
- context.getContentResolver(),
- LauncherSettings.Settings.METHOD_NEW_TRANSACTION)
- .getBinder(LauncherSettings.Settings.EXTRA_VALUE)) {
+ ModelDbController dbController = LauncherAppState.getInstance(context)
+ .getModel().getModelDbController();
+ try (SQLiteTransaction transaction = dbController.newTransaction()) {
GridBackupTable backupTable = new GridBackupTable(context, transaction.getDb());
backupTable.createCustomBackupTable(HYBRID_HOTSEAT_BACKUP_TABLE);
transaction.commit();
- LauncherSettings.Settings.call(context.getContentResolver(),
- LauncherSettings.Settings.METHOD_REFRESH_HOTSEAT_RESTORE_TABLE);
+ dbController.refreshHotseatRestoreTable();
}
});
}
@@ -55,18 +53,15 @@
*/
public static void restoreBackup(Context context) {
MODEL_EXECUTOR.execute(() -> {
- try (LauncherDbUtils.SQLiteTransaction transaction = (LauncherDbUtils.SQLiteTransaction)
- LauncherSettings.Settings.call(
- context.getContentResolver(),
- LauncherSettings.Settings.METHOD_NEW_TRANSACTION)
- .getBinder(LauncherSettings.Settings.EXTRA_VALUE)) {
+ LauncherModel model = LauncherAppState.getInstance(context).getModel();
+ try (SQLiteTransaction transaction = model.getModelDbController().newTransaction()) {
if (!tableExists(transaction.getDb(), HYBRID_HOTSEAT_BACKUP_TABLE)) {
return;
}
GridBackupTable backupTable = new GridBackupTable(context, transaction.getDb());
backupTable.restoreFromCustomBackupTable(HYBRID_HOTSEAT_BACKUP_TABLE, true);
transaction.commit();
- LauncherAppState.getInstance(context).getModel().forceReload();
+ model.forceReload();
}
});
}
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
index 70aa5d7..d379d6d 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
@@ -16,7 +16,7 @@
package com.android.launcher3.statehandlers;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.app.animation.Interpolators.LINEAR;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_DEPTH;
import static com.android.launcher3.states.StateAnimationConfig.SKIP_DEPTH_CONTROLLER;
import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
index d087d39..7283a18 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
@@ -15,14 +15,21 @@
*/
package com.android.launcher3.statehandlers;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+
import android.os.SystemProperties;
import android.util.Log;
import android.view.View;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.uioverrides.QuickstepLauncher;
+import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.views.DesktopAppSelectView;
+import com.android.wm.shell.desktopmode.IDesktopTaskListener;
/**
* Controls the visibility of the workspace and the resumed / paused state when desktop mode
@@ -39,11 +46,58 @@
private boolean mInOverviewState;
private boolean mGestureInProgress;
+ @Nullable
+ private IDesktopTaskListener mDesktopTaskListener;
+ private DesktopAppSelectView mSelectAppToast;
+
public DesktopVisibilityController(Launcher launcher) {
mLauncher = launcher;
}
/**
+ * Register a listener with System UI to receive updates about desktop tasks state
+ */
+ public void registerSystemUiListener() {
+ mDesktopTaskListener = new IDesktopTaskListener.Stub() {
+ @Override
+ public void onVisibilityChanged(int displayId, boolean visible) {
+ MAIN_EXECUTOR.execute(() -> {
+ if (displayId == mLauncher.getDisplayId()) {
+ if (DEBUG) {
+ Log.d(TAG, "desktop visibility changed value=" + visible);
+ }
+ setFreeformTasksVisible(visible);
+ }
+ });
+ }
+
+ @Override
+ public void onStashedChanged(int displayId, boolean stashed) {
+ MAIN_EXECUTOR.execute(() -> {
+ if (displayId == mLauncher.getDisplayId()) {
+ if (DEBUG) {
+ Log.d(TAG, "desktop stashed changed value=" + stashed);
+ }
+ if (stashed) {
+ showSelectAppToast();
+ } else {
+ hideSelectAppToast();
+ }
+ }
+ });
+ }
+ };
+ SystemUiProxy.INSTANCE.get(mLauncher).setDesktopTaskListener(mDesktopTaskListener);
+ }
+
+ /**
+ * Clear listener from System UI that was set with {@link #registerSystemUiListener()}
+ */
+ public void unregisterSystemUiListener() {
+ SystemUiProxy.INSTANCE.get(mLauncher).setDesktopTaskListener(null);
+ }
+
+ /**
* Whether desktop mode is supported.
*/
private boolean isDesktopModeSupported() {
@@ -68,6 +122,7 @@
if (!isDesktopModeSupported()) {
return;
}
+
if (freeformTasksVisible != mFreeformTasksVisible) {
mFreeformTasksVisible = freeformTasksVisible;
if (mFreeformTasksVisible) {
@@ -130,6 +185,15 @@
}
}
+ /**
+ * Handle launcher moving to home due to home gesture or home button press.
+ */
+ public void onHomeActionTriggered() {
+ if (areFreeformTasksVisible()) {
+ SystemUiProxy.INSTANCE.get(mLauncher).stashDesktopApps(mLauncher.getDisplayId());
+ }
+ }
+
private void setLauncherViewsVisibility(int visibility) {
if (DEBUG) {
Log.d(TAG, "setLauncherViewsVisibility: visibility=" + visibility);
@@ -168,4 +232,28 @@
activity.setResumed();
}
}
+
+ private void showSelectAppToast() {
+ if (mSelectAppToast != null) {
+ return;
+ }
+ if (DEBUG) {
+ Log.d(TAG, "show toast to select desktop apps");
+ }
+ Runnable onCloseCallback = () -> {
+ SystemUiProxy.INSTANCE.get(mLauncher).hideStashedDesktopApps(mLauncher.getDisplayId());
+ };
+ mSelectAppToast = DesktopAppSelectView.show(mLauncher, onCloseCallback);
+ }
+
+ private void hideSelectAppToast() {
+ if (mSelectAppToast == null) {
+ return;
+ }
+ if (DEBUG) {
+ Log.d(TAG, "hide toast to select desktop apps");
+ }
+ mSelectAppToast.hide();
+ mSelectAppToast = null;
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
index 7f655cf..072fc30 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
@@ -22,9 +22,13 @@
import androidx.annotation.Nullable;
import com.android.launcher3.R;
+import com.android.launcher3.statehandlers.DesktopVisibilityController;
import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
+import com.android.quickstep.LauncherActivityInterface;
import com.android.quickstep.RecentsModel;
+import com.android.quickstep.util.DesktopTask;
import com.android.quickstep.util.GroupTask;
+import com.android.quickstep.views.DesktopTaskView;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -102,21 +106,74 @@
mQuickSwitchViewController = new KeyboardQuickSwitchViewController(
mControllers, overlayContext, keyboardQuickSwitchView, mControllerCallbacks);
+ DesktopVisibilityController desktopController =
+ LauncherActivityInterface.INSTANCE.getDesktopVisibilityController();
+ final boolean onDesktop =
+ DesktopTaskView.DESKTOP_IS_PROTO2_ENABLED
+ && desktopController != null
+ && desktopController.areFreeformTasksVisible();
+
if (mModel.isTaskListValid(mTaskListChangeId)) {
- mQuickSwitchViewController.openQuickSwitchView(
- mTasks, mNumHiddenTasks, /* updateTasks= */ false, currentFocusedIndex);
+ mQuickSwitchViewController.openQuickSwitchView(mTasks,
+ mNumHiddenTasks, /* updateTasks= */ false, currentFocusedIndex, onDesktop);
return;
}
+
mTaskListChangeId = mModel.getTasks((tasks) -> {
- // Only store MAX_TASK tasks, from most to least recent
- Collections.reverse(tasks);
- mTasks = tasks.stream().limit(MAX_TASKS).collect(Collectors.toList());
- mNumHiddenTasks = Math.max(0, tasks.size() - MAX_TASKS);
- mQuickSwitchViewController.openQuickSwitchView(
- mTasks, mNumHiddenTasks, /* updateTasks= */ true, currentFocusedIndex);
+ if (onDesktop) {
+ processLoadedTasksOnDesktop(tasks);
+ } else {
+ processLoadedTasks(tasks);
+ }
+ mQuickSwitchViewController.openQuickSwitchView(mTasks,
+ mNumHiddenTasks, /* updateTasks= */ true, currentFocusedIndex, onDesktop);
});
}
+ private void processLoadedTasks(ArrayList<GroupTask> tasks) {
+ // Only store MAX_TASK tasks, from most to least recent
+ Collections.reverse(tasks);
+
+ // Hide all desktop tasks and show them on the hidden tile
+ int hiddenDesktopTasks = 0;
+ if (DesktopTaskView.DESKTOP_IS_PROTO2_ENABLED) {
+ DesktopTask desktopTask = findDesktopTask(tasks);
+ if (desktopTask != null) {
+ hiddenDesktopTasks = desktopTask.tasks.size();
+ tasks = tasks.stream()
+ .filter(t -> !(t instanceof DesktopTask))
+ .collect(Collectors.toCollection(ArrayList<GroupTask>::new));
+ }
+ }
+ mTasks = tasks.stream()
+ .limit(MAX_TASKS)
+ .collect(Collectors.toList());
+ mNumHiddenTasks = Math.max(0, tasks.size() - MAX_TASKS) + hiddenDesktopTasks;
+ }
+
+ private void processLoadedTasksOnDesktop(ArrayList<GroupTask> tasks) {
+ // Find the single desktop task that contains a grouping of desktop tasks
+ DesktopTask desktopTask = findDesktopTask(tasks);
+
+ if (desktopTask != null) {
+ mTasks = desktopTask.tasks.stream().map(GroupTask::new).collect(Collectors.toList());
+ // All other tasks, apart from the grouped desktop task, are hidden
+ mNumHiddenTasks = Math.max(0, tasks.size() - 1);
+ } else {
+ // Desktop tasks were visible, but the recents entry is missing. Fall back to empty list
+ mTasks = Collections.emptyList();
+ mNumHiddenTasks = tasks.size();
+ }
+ }
+
+ @Nullable
+ private DesktopTask findDesktopTask(ArrayList<GroupTask> tasks) {
+ return (DesktopTask) tasks.stream()
+ .filter(t -> t instanceof DesktopTask)
+ .findFirst()
+ .orElse(null);
+ }
+
void closeQuickSwitchView() {
if (mQuickSwitchViewController == null) {
return;
@@ -169,7 +226,7 @@
class ControllerCallbacks {
int getTaskCount() {
- return mNumHiddenTasks == 0 ? mTasks.size() : MAX_TASKS + 1;
+ return mTasks.size() + (mNumHiddenTasks == 0 ? 0 : 1);
}
@Nullable
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java
index 2cdfb18..4e9e301 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java
@@ -42,10 +42,10 @@
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
+import com.android.app.animation.Interpolators;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatedFloat;
-import com.android.launcher3.anim.Interpolators;
import com.android.quickstep.util.GroupTask;
import java.util.HashMap;
@@ -190,8 +190,12 @@
ConstraintLayout.LayoutParams lp = new ConstraintLayout.LayoutParams(
width, mTaskViewHeight);
- lp.endToEnd = PARENT_ID;
- lp.startToEnd = previousView.getId();
+ if (previousView == null) {
+ lp.startToStart = PARENT_ID;
+ } else {
+ lp.endToEnd = PARENT_ID;
+ lp.startToEnd = previousView.getId();
+ }
lp.topToTop = PARENT_ID;
lp.bottomToBottom = PARENT_ID;
lp.setMarginEnd(mSpacing);
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
index 3230c66..a293f74 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
@@ -27,6 +27,7 @@
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
import com.android.launcher3.taskbar.overlay.TaskbarOverlayDragLayer;
+import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.util.GroupTask;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -52,6 +53,8 @@
private int mCurrentFocusIndex = -1;
+ private boolean mOnDesktop;
+
protected KeyboardQuickSwitchViewController(
@NonNull TaskbarControllers controllers,
@NonNull TaskbarOverlayContext overlayContext,
@@ -71,10 +74,12 @@
@NonNull List<GroupTask> tasks,
int numHiddenTasks,
boolean updateTasks,
- int currentFocusIndexOverride) {
+ int currentFocusIndexOverride,
+ boolean onDesktop) {
TaskbarOverlayDragLayer dragLayer = mOverlayContext.getDragLayer();
dragLayer.addView(mKeyboardQuickSwitchView);
dragLayer.runOnClickOnce(v -> closeQuickSwitchView(true));
+ mOnDesktop = onDesktop;
mKeyboardQuickSwitchView.applyLoadPlan(
mOverlayContext,
@@ -136,6 +141,10 @@
GroupTask task = mControllerCallbacks.getTaskAt(index);
if (task == null) {
return Math.max(0, index);
+ } else if (mOnDesktop) {
+ UI_HELPER_EXECUTOR.execute(() ->
+ SystemUiProxy.INSTANCE.get(mKeyboardQuickSwitchView.getContext())
+ .showDesktopApp(task.task1.key.id));
} else if (task.task2 == null) {
UI_HELPER_EXECUTOR.execute(() ->
ActivityManagerWrapper.getInstance().startActivityFromRecents(
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index ba6f165..35b9957 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -312,6 +312,8 @@
.getTaskbarNavButtonTranslationYForInAppDisplay()
.updateValue(mLauncher.getDeviceProfile().getTaskbarOffsetY()
* mTaskbarInAppDisplayProgress.value);
+ mControllers.navbarButtonsViewController
+ .getOnTaskbarBackgroundNavButtonColorOverride().updateValue(progress);
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index 9a9e0ba..10ae97b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -148,8 +148,8 @@
// Used for IME+A11Y buttons
private final ViewGroup mEndContextualContainer;
private final ViewGroup mStartContextualContainer;
- private final int mLightIconColor;
- private final int mDarkIconColor;
+ private final int mLightIconColorOnHome;
+ private final int mDarkIconColorOnHome;
/** Color to use for navigation bar buttons, if they are on on a Taskbar surface background. */
private final int mOnBackgroundIconColor;
@@ -205,9 +205,11 @@
mEndContextualContainer = mNavButtonsView.findViewById(R.id.end_contextual_buttons);
mStartContextualContainer = mNavButtonsView.findViewById(R.id.start_contextual_buttons);
- mLightIconColor = context.getColor(R.color.taskbar_nav_icon_light_color);
- mDarkIconColor = context.getColor(R.color.taskbar_nav_icon_dark_color);
- mOnBackgroundIconColor = Utilities.isDarkTheme(context) ? mLightIconColor : mDarkIconColor;
+ mLightIconColorOnHome = context.getColor(R.color.taskbar_nav_icon_light_color_on_home);
+ mDarkIconColorOnHome = context.getColor(R.color.taskbar_nav_icon_dark_color_on_home);
+ mOnBackgroundIconColor = Utilities.isDarkTheme(context)
+ ? context.getColor(R.color.taskbar_nav_icon_light_color)
+ : context.getColor(R.color.taskbar_nav_icon_dark_color);
}
/**
@@ -630,18 +632,20 @@
private void updateNavButtonColor() {
final ArgbEvaluator argbEvaluator = ArgbEvaluator.getInstance();
- final int sysUiNavButtonIconColor = (int) argbEvaluator.evaluate(
+ final int sysUiNavButtonIconColorOnHome = (int) argbEvaluator.evaluate(
mTaskbarNavButtonDarkIntensity.value,
- mLightIconColor,
- mDarkIconColor);
+ mLightIconColorOnHome,
+ mDarkIconColorOnHome);
+
// Override the color from framework if nav buttons are over an opaque Taskbar surface.
final int iconColor = (int) argbEvaluator.evaluate(
mOnBackgroundNavButtonColorOverrideMultiplier.value
* Math.max(
mOnTaskbarBackgroundNavButtonColorOverride.value,
mSlideInViewVisibleNavButtonColorOverride.value),
- sysUiNavButtonIconColor,
+ sysUiNavButtonIconColorOnHome,
mOnBackgroundIconColor);
+
for (ImageView button : mAllButtons) {
button.setImageTintList(ColorStateList.valueOf(iconColor));
}
@@ -928,8 +932,6 @@
pw.println(prefix + "NavbarButtonsViewController:");
pw.println(prefix + "\tmState=" + getStateString(mState));
- pw.println(prefix + "\tmLightIconColor=" + Integer.toHexString(mLightIconColor));
- pw.println(prefix + "\tmDarkIconColor=" + Integer.toHexString(mDarkIconColor));
pw.println(prefix + "\tmFloatingRotationButtonBounds=" + mFloatingRotationButtonBounds);
pw.println(prefix + "\tmSysuiStateFlags=" + QuickStepContract.getSystemUiStateString(
mSysuiStateFlags));
@@ -940,6 +942,14 @@
+ mTaskbarNavButtonTranslationYForInAppDisplay.value);
pw.println(prefix + "\t\tmTaskbarNavButtonTranslationYForIme="
+ mTaskbarNavButtonTranslationYForIme.value);
+ pw.println(prefix + "\t\tmTaskbarNavButtonDarkIntensity="
+ + mTaskbarNavButtonDarkIntensity.value);
+ pw.println(prefix + "\t\tmSlideInViewVisibleNavButtonColorOverride="
+ + mSlideInViewVisibleNavButtonColorOverride.value);
+ pw.println(prefix + "\t\tmOnTaskbarBackgroundNavButtonColorOverride="
+ + mOnTaskbarBackgroundNavButtonColorOverride.value);
+ pw.println(prefix + "\t\tmOnBackgroundNavButtonColorOverrideMultiplier="
+ + mOnBackgroundNavButtonColorOverrideMultiplier.value);
}
private static String getStateString(int flags) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 43feec7..31af1ce 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -49,7 +49,6 @@
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.os.Process;
-import android.os.SystemProperties;
import android.os.Trace;
import android.provider.Settings;
import android.util.Log;
@@ -128,8 +127,6 @@
private static final String IME_DRAWS_IME_NAV_BAR_RES_NAME = "config_imeDrawsImeNavBar";
- private static final boolean ENABLE_THREE_BUTTON_TASKBAR =
- SystemProperties.getBoolean("persist.debug.taskbar_three_button", false);
private static final String TAG = "TaskbarActivityContext";
private static final String WINDOW_TITLE = "Taskbar";
@@ -169,30 +166,27 @@
TaskbarNavButtonController buttonController, ScopedUnfoldTransitionProgressProvider
unfoldTransitionProgressProvider) {
super(windowContext);
+
+ applyDeviceProfile(launcherDp);
+
final Resources resources = getResources();
- matchDeviceProfile(launcherDp, getResources());
-
- mNavMode = DisplayController.getNavigationMode(windowContext);
mImeDrawsImeNavBar = getBoolByName(IME_DRAWS_IME_NAV_BAR_RES_NAME, resources, false);
mIsSafeModeEnabled = TraceHelper.allowIpcs("isSafeMode",
() -> getPackageManager().isSafeMode());
+
+ // TODO(b/244231596) For shared Taskbar window, update this value in applyDeviceProfile()
+ // instead so to get correct value when recreating the taskbar
SettingsCache settingsCache = SettingsCache.INSTANCE.get(this);
mIsUserSetupComplete = settingsCache.getValue(
Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE), 0);
- mIsNavBarForceVisible = settingsCache.getValue(
- Settings.Secure.getUriFor(Settings.Secure.NAV_BAR_KIDS_MODE), 0);
-
- // TODO(b/244231596) For shared Taskbar window, update this value in init() instead so
- // to get correct value when recreating the taskbar
mIsNavBarKidsMode = settingsCache.getValue(
Settings.Secure.getUriFor(Settings.Secure.NAV_BAR_KIDS_MODE), 0);
+ mIsNavBarForceVisible = mIsNavBarKidsMode;
// Get display and corners first, as views might use them in constructor.
Display display = windowContext.getDisplay();
- Context c = display.getDisplayId() == Display.DEFAULT_DISPLAY
- ? windowContext.getApplicationContext()
- : windowContext.getApplicationContext().createDisplayContext(display);
+ Context c = getApplicationContext();
mWindowManager = c.getSystemService(WindowManager.class);
mLeftCorner = display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT);
mRightCorner = display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_RIGHT);
@@ -216,7 +210,7 @@
// If Bubble bar is present, TaskbarControllers depends on it so build it first.
Optional<BubbleControllers> bubbleControllersOptional = Optional.empty();
- if (BubbleBarController.BUBBLE_BAR_ENABLED) {
+ if (BubbleBarController.BUBBLE_BAR_ENABLED && bubbleBarView != null) {
bubbleControllersOptional = Optional.of(new BubbleControllers(
new BubbleBarController(this, bubbleBarView),
new BubbleBarViewController(this, bubbleBarView),
@@ -267,6 +261,38 @@
bubbleControllersOptional);
}
+ /** Updates {@link DeviceProfile} instances for any Taskbar windows. */
+ public void updateDeviceProfile(DeviceProfile launcherDp) {
+ applyDeviceProfile(launcherDp);
+
+ mControllers.taskbarOverlayController.updateLauncherDeviceProfile(launcherDp);
+ AbstractFloatingView.closeAllOpenViewsExcept(this, false, TYPE_REBIND_SAFE);
+ // Reapply fullscreen to take potential new screen size into account.
+ setTaskbarWindowFullscreen(mIsFullscreen);
+
+ dispatchDeviceProfileChanged();
+ }
+
+ /**
+ * Copy the original DeviceProfile, match the number of hotseat icons and qsb width and update
+ * the icon size
+ */
+ private void applyDeviceProfile(DeviceProfile originDeviceProfile) {
+ mDeviceProfile = originDeviceProfile.toBuilder(this)
+ .withDimensionsOverride(deviceProfile -> {
+ // Taskbar should match the number of icons of hotseat
+ deviceProfile.numShownHotseatIcons = originDeviceProfile.numShownHotseatIcons;
+ // Same QSB width to have a smooth animation
+ deviceProfile.hotseatQsbWidth = originDeviceProfile.hotseatQsbWidth;
+
+ // Update icon size
+ deviceProfile.iconSizePx = deviceProfile.taskbarIconSize;
+ deviceProfile.updateIconSize(1f, getResources());
+ }).build();
+ mNavMode = DisplayController.getNavigationMode(this);
+ }
+
+
public void init(@NonNull TaskbarSharedState sharedState) {
mLastRequestedNonFullscreenHeight = getDefaultTaskbarWindowHeight();
mWindowLayoutParams =
@@ -308,19 +334,6 @@
return mDeviceProfile;
}
- /** Updates {@link DeviceProfile} instances for any Taskbar windows. */
- public void updateDeviceProfile(DeviceProfile launcherDp, NavigationMode navMode) {
- mNavMode = navMode;
- mControllers.taskbarOverlayController.updateLauncherDeviceProfile(launcherDp);
- matchDeviceProfile(launcherDp, getResources());
-
- AbstractFloatingView.closeAllOpenViewsExcept(this, false, TYPE_REBIND_SAFE);
- // Reapply fullscreen to take potential new screen size into account.
- setTaskbarWindowFullscreen(mIsFullscreen);
-
- dispatchDeviceProfileChanged();
- }
-
@Override
public void dispatchDeviceProfileChanged() {
super.dispatchDeviceProfileChanged();
@@ -329,24 +342,6 @@
}
/**
- * Copy the original DeviceProfile, match the number of hotseat icons and qsb width and update
- * the icon size
- */
- private void matchDeviceProfile(DeviceProfile originDeviceProfile, Resources resources) {
- mDeviceProfile = originDeviceProfile.toBuilder(this)
- .withDimensionsOverride(deviceProfile -> {
- // Taskbar should match the number of icons of hotseat
- deviceProfile.numShownHotseatIcons = originDeviceProfile.numShownHotseatIcons;
- // Same QSB width to have a smooth animation
- deviceProfile.hotseatQsbWidth = originDeviceProfile.hotseatQsbWidth;
-
- // Update icon size
- deviceProfile.iconSizePx = deviceProfile.taskbarIconSize;
- deviceProfile.updateIconSize(1f, resources);
- }).build();
- }
-
- /**
* Returns the View bounds of transient taskbar.
*/
public Rect getTransientTaskbarBounds() {
@@ -448,6 +443,11 @@
return mControllers.taskbarDragController;
}
+ @Nullable
+ public BubbleControllers getBubbleControllers() {
+ return mControllers.bubbleControllers.orElse(null);
+ }
+
@Override
public ViewCache getViewCache() {
return mViewCache;
@@ -640,8 +640,12 @@
mControllers.taskbarForceVisibleImmersiveController.updateSysuiFlags(systemUiStateFlags);
mControllers.voiceInteractionWindowController.setIsVoiceInteractionWindowVisible(
(systemUiStateFlags & SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING) != 0, fromInit);
-
mControllers.uiController.updateStateForSysuiFlags(systemUiStateFlags);
+ mControllers.bubbleControllers.ifPresent(controllers -> {
+ controllers.bubbleBarController.updateStateForSysuiFlags(systemUiStateFlags);
+ controllers.bubbleStashedHandleViewController.setIsHomeButtonDisabled(
+ mControllers.navbarButtonsViewController.isHomeDisabled());
+ });
}
/**
@@ -737,7 +741,7 @@
}
}
mWindowLayoutParams.height = height;
- mControllers.taskbarInsetsController.onTaskbarWindowHeightOrInsetsChanged();
+ mControllers.taskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged();
mWindowManager.updateViewLayout(mDragLayer, mWindowLayoutParams);
}
@@ -994,10 +998,19 @@
* Called when we want to unstash taskbar when user performs swipes up gesture.
*/
public void onSwipeToUnstashTaskbar() {
- mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(false);
+ mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(/* stash= */ false);
mControllers.taskbarEduTooltipController.hide();
}
+ /**
+ * Called when we want to open bubblebar when user performs swipes up gesture.
+ */
+ public void onSwipeToOpenBubblebar() {
+ mControllers.bubbleControllers.ifPresent(controllers -> {
+ controllers.bubbleStashController.showBubbleBar(/* expandBubbles= */ true);
+ });
+ }
+
/** Returns {@code true} if Taskbar All Apps is open. */
public boolean isTaskbarAllAppsOpen() {
return mControllers.taskbarAllAppsController.isOpen();
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
index ca2d1f8..d237c1f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
@@ -22,12 +22,12 @@
import android.graphics.Paint
import android.graphics.Path
import android.graphics.RectF
+import com.android.app.animation.Interpolators
import com.android.launcher3.DeviceProfile
import com.android.launcher3.R
import com.android.launcher3.Utilities
import com.android.launcher3.Utilities.mapRange
import com.android.launcher3.Utilities.mapToRange
-import com.android.launcher3.anim.Interpolators
import com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound
import com.android.launcher3.util.DisplayController
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
index 66c2eb3..3cd151d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
@@ -226,6 +226,7 @@
taskbarPopupController.onDestroy();
taskbarForceVisibleImmersiveController.onDestroy();
taskbarOverlayController.onDestroy();
+ taskbarAllAppsController.onDestroy();
navButtonController.onDestroy();
taskbarInsetsController.onDestroy();
voiceInteractionWindowController.onDestroy();
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
index 040b8f7..64ba5aa 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
@@ -15,11 +15,11 @@
*/
package com.android.launcher3.taskbar;
+import static com.android.app.animation.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.AbstractFloatingView.TYPE_TASKBAR_ALL_APPS;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_ALL_APPS;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_PREDICTION;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
-import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -47,6 +47,7 @@
import androidx.annotation.Nullable;
+import com.android.app.animation.Interpolators;
import com.android.internal.logging.InstanceId;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BubbleTextView;
@@ -55,7 +56,6 @@
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
import com.android.launcher3.accessibility.DragViewStateAnnouncer;
-import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragDriver;
import com.android.launcher3.dragndrop.DragOptions;
@@ -642,7 +642,7 @@
final FloatProp mScale = new FloatProp(1f, toScale, 0,
ANIM_DURATION_RETURN_ICON_TO_TASKBAR, FAST_OUT_SLOW_IN);
final FloatProp mAlpha = new FloatProp(1f, toAlpha, 0,
- ANIM_DURATION_RETURN_ICON_TO_TASKBAR, Interpolators.ACCEL_2);
+ ANIM_DURATION_RETURN_ICON_TO_TASKBAR, Interpolators.ACCELERATE_2);
@Override
public void onUpdate(float percent, boolean initOnly) {
animListener.updateDragShadow(mDx.value, mDy.value, mScale.value, mAlpha.value);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
index d6e559a..07cd8ff 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
@@ -21,6 +21,7 @@
import android.os.IBinder
import android.view.InsetsFrameProvider
import android.view.InsetsFrameProvider.SOURCE_DISPLAY
+import android.view.InsetsSource.FLAG_INSETS_ROUNDED_CORNER
import android.view.InsetsSource.FLAG_SUPPRESS_SCRIM
import android.view.ViewTreeObserver
import android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME
@@ -55,13 +56,13 @@
private val touchableRegion: Region = Region()
private val insetsOwner: IBinder = Binder()
private val deviceProfileChangeListener = { _: DeviceProfile ->
- onTaskbarWindowHeightOrInsetsChanged()
+ onTaskbarOrBubblebarWindowHeightOrInsetsChanged()
}
private val gestureNavSettingsObserver =
GestureNavigationSettingsObserver(
context.mainThreadHandler,
context,
- this::onTaskbarWindowHeightOrInsetsChanged
+ this::onTaskbarOrBubblebarWindowHeightOrInsetsChanged
)
// Initialized in init.
@@ -71,7 +72,7 @@
fun init(controllers: TaskbarControllers) {
this.controllers = controllers
windowLayoutParams = context.windowLayoutParams
- onTaskbarWindowHeightOrInsetsChanged()
+ onTaskbarOrBubblebarWindowHeightOrInsetsChanged()
context.addOnDeviceProfileChangeListener(deviceProfileChangeListener)
gestureNavSettingsObserver.registerForCallingUser()
@@ -82,12 +83,24 @@
gestureNavSettingsObserver.unregister()
}
- fun onTaskbarWindowHeightOrInsetsChanged() {
+ fun onTaskbarOrBubblebarWindowHeightOrInsetsChanged() {
+ val tappableHeight = controllers.taskbarStashController.tappableHeightToReportToApps
+ // We only report tappableElement height for unstashed, persistent taskbar,
+ // which is also when we draw the rounded corners above taskbar.
+ val insetsRoundedCornerFlag =
+ if (tappableHeight > 0) {
+ FLAG_INSETS_ROUNDED_CORNER
+ } else {
+ 0
+ }
if (context.isGestureNav) {
windowLayoutParams.providedInsets =
arrayOf(
InsetsFrameProvider(insetsOwner, 0, navigationBars())
- .setFlags(FLAG_SUPPRESS_SCRIM, FLAG_SUPPRESS_SCRIM),
+ .setFlags(
+ FLAG_SUPPRESS_SCRIM or insetsRoundedCornerFlag,
+ FLAG_SUPPRESS_SCRIM or FLAG_INSETS_ROUNDED_CORNER
+ ),
InsetsFrameProvider(insetsOwner, 0, tappableElement()),
InsetsFrameProvider(insetsOwner, 0, mandatorySystemGestures()),
InsetsFrameProvider(insetsOwner, INDEX_LEFT, systemGestures())
@@ -98,21 +111,44 @@
} else {
windowLayoutParams.providedInsets =
arrayOf(
- InsetsFrameProvider(insetsOwner, 0, navigationBars()),
+ InsetsFrameProvider(insetsOwner, 0, navigationBars())
+ .setFlags(
+ insetsRoundedCornerFlag,
+ (FLAG_SUPPRESS_SCRIM or FLAG_INSETS_ROUNDED_CORNER)
+ ),
InsetsFrameProvider(insetsOwner, 0, tappableElement()),
InsetsFrameProvider(insetsOwner, 0, mandatorySystemGestures())
)
}
- val touchableHeight = controllers.taskbarStashController.touchableHeight
- touchableRegion.set(
- 0,
- windowLayoutParams.height - touchableHeight,
- context.deviceProfile.widthPx,
- windowLayoutParams.height
- )
+ val taskbarTouchableHeight = controllers.taskbarStashController.touchableHeight
+ val bubblesTouchableHeight =
+ if (controllers.bubbleControllers.isPresent)
+ controllers.bubbleControllers.get().bubbleStashController.touchableHeight
+ else 0
+ val touchableHeight = Math.max(taskbarTouchableHeight, bubblesTouchableHeight)
+
+ if (
+ controllers.bubbleControllers.isPresent &&
+ controllers.bubbleControllers.get().bubbleStashController.isBubblesShowingOnHome
+ ) {
+ val iconBounds =
+ controllers.bubbleControllers.get().bubbleBarViewController.bubbleBarBounds
+ touchableRegion.set(
+ iconBounds.left,
+ iconBounds.top,
+ iconBounds.right,
+ iconBounds.bottom
+ )
+ } else {
+ touchableRegion.set(
+ 0,
+ windowLayoutParams.height - touchableHeight,
+ context.deviceProfile.widthPx,
+ windowLayoutParams.height
+ )
+ }
val contentHeight = controllers.taskbarStashController.contentHeightToReportToApps
- val tappableHeight = controllers.taskbarStashController.tappableHeightToReportToApps
val res = context.resources
for (provider in windowLayoutParams.providedInsets) {
if (provider.type == navigationBars() || provider.type == mandatorySystemGestures()) {
@@ -162,10 +198,6 @@
}
}
- // We only report tappableElement height for unstashed, persistent taskbar,
- // which is also when we draw the rounded corners above taskbar.
- windowLayoutParams.insetsRoundedCornerFrame = tappableHeight > 0
-
context.notifyUpdateLayoutParams()
}
@@ -199,6 +231,9 @@
context.dragLayer,
insetsInfo.touchableRegion
)
+ val bubbleBarVisible =
+ controllers.bubbleControllers.isPresent &&
+ controllers.bubbleControllers.get().bubbleBarViewController.isBubbleBarVisible()
var insetsIsTouchableRegion = true
if (context.dragLayer.alpha < AlphaUpdateListener.ALPHA_CUTOFF_THRESHOLD) {
// Let touches pass through us.
@@ -219,7 +254,9 @@
insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_FRAME)
insetsIsTouchableRegion = false
} else if (
- controllers.taskbarViewController.areIconsVisible() || context.isNavBarKidsModeActive
+ controllers.taskbarViewController.areIconsVisible() ||
+ context.isNavBarKidsModeActive ||
+ bubbleBarVisible
) {
// Taskbar has some touchable elements, take over the full taskbar area
if (
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index 17e7e1b..0b822fb 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -404,6 +404,14 @@
+ ", mLauncherState: " + mLauncherState
+ ", toAlignment: " + toAlignment);
}
+ mControllers.bubbleControllers.ifPresent(controllers -> {
+ // Show the bubble bar when on launcher home or in overview.
+ boolean onHome = isInLauncher && mLauncherState == LauncherState.NORMAL;
+ boolean onOverview = mLauncherState == LauncherState.OVERVIEW;
+ controllers.bubbleStashController.setBubblesShowingOnHome(onHome);
+ controllers.bubbleStashController.setBubblesShowingOnOverview(onOverview);
+ });
+
AnimatorSet animatorSet = new AnimatorSet();
if (hasAnyFlag(changedFlags, FLAG_LAUNCHER_IN_STATE_TRANSITION)) {
@@ -474,7 +482,8 @@
public void onAnimationEnd(Animator animation) {
TaskbarStashController stashController =
mControllers.taskbarStashController;
- stashController.updateAndAnimateTransientTaskbar(/* stash */ true);
+ stashController.updateAndAnimateTransientTaskbar(
+ /* stash */ true, /* bubblesShouldFollow */ true);
}
});
} else {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index 738ff87..8f3898f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -22,13 +22,12 @@
import static com.android.launcher3.LauncherPrefs.TASKBAR_PINNING;
import static com.android.launcher3.LauncherPrefs.TASKBAR_PINNING_KEY;
-import static com.android.launcher3.util.DisplayController.CHANGE_DENSITY;
-import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
import static com.android.launcher3.util.DisplayController.TASKBAR_NOT_DESTROYED_TAG;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.launcher3.util.FlagDebugUtils.formatFlagChange;
import android.annotation.SuppressLint;
+import android.app.Activity;
import android.app.PendingIntent;
import android.content.ComponentCallbacks;
import android.content.Context;
@@ -50,14 +49,14 @@
import androidx.annotation.VisibleForTesting;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.InvariantDeviceProfile.OnIDPChangeListener;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.taskbar.unfold.NonDestroyableScopedUnfoldTransitionProgressProvider;
import com.android.launcher3.uioverrides.QuickstepLauncher;
-import com.android.launcher3.util.DisplayController;
-import com.android.launcher3.util.NavigationMode;
+import com.android.launcher3.util.ActivityLifecycleCallbacksAdapter;
import com.android.launcher3.util.SettingsCache;
import com.android.launcher3.util.SimpleBroadcastReceiver;
import com.android.quickstep.RecentsActivity;
@@ -77,6 +76,22 @@
private static final String TAG = "TaskbarManager";
private static final boolean DEBUG = false;
+ /**
+ * All the configurations which do not initiate taskbar recreation.
+ * This includes all the configurations defined in Launcher's manifest entry and
+ * ActivityController#filterConfigChanges
+ */
+ private static final int SKIP_RECREATE_CONFIG_CHANGES = ActivityInfo.CONFIG_WINDOW_CONFIGURATION
+ | ActivityInfo.CONFIG_KEYBOARD
+ | ActivityInfo.CONFIG_KEYBOARD_HIDDEN
+ | ActivityInfo.CONFIG_MCC
+ | ActivityInfo.CONFIG_MNC
+ | ActivityInfo.CONFIG_NAVIGATION
+ | ActivityInfo.CONFIG_ORIENTATION
+ | ActivityInfo.CONFIG_SCREEN_SIZE
+ | ActivityInfo.CONFIG_SCREEN_LAYOUT
+ | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
+
public static final boolean FLAG_HIDE_NAVBAR_WINDOW =
SystemProperties.getBoolean("persist.wm.debug.hide_navbar_window", false);
@@ -87,12 +102,11 @@
Settings.Secure.NAV_BAR_KIDS_MODE);
private final Context mContext;
- private final DisplayController mDisplayController;
private final TaskbarNavButtonController mNavButtonController;
- private final SettingsCache.OnChangeListener mUserSetupCompleteListener;
- private final SettingsCache.OnChangeListener mNavBarKidsModeListener;
private final ComponentCallbacks mComponentCallbacks;
- private final SimpleBroadcastReceiver mShutdownReceiver;
+
+ private final SimpleBroadcastReceiver mShutdownReceiver =
+ new SimpleBroadcastReceiver(i -> destroyExistingTaskbar());
// The source for this provider is set when Launcher is available
// We use 'non-destroyable' version here so the original provider won't be destroyed
@@ -100,7 +114,6 @@
// It's destruction/creation will be managed by the activity.
private final ScopedUnfoldTransitionProgressProvider mUnfoldProgressProvider =
new NonDestroyableScopedUnfoldTransitionProgressProvider();
- private NavigationMode mNavMode;
private TaskbarActivityContext mTaskbarActivityContext;
private StatefulActivity mActivity;
@@ -111,19 +124,11 @@
private final TaskbarSharedState mSharedState = new TaskbarSharedState();
/**
- * We use WindowManager's ComponentCallbacks() for most of the config changes, however for
- * navigation mode, that callback gets called too soon, before it's internal navigation mode
- * reflects the current one.
- * DisplayController's callback is delayed enough to get the correct nav mode value
- *
- * We also use density change here because DeviceProfile has had a chance to update it's state
- * whereas density for component callbacks registered in this class don't update DeviceProfile.
- * Confused? Me too. Make it less confusing (TODO: b/227669780)
- *
- * Flags used with {@link #mDispInfoChangeListener}
+ * We use WindowManager's ComponentCallbacks() for internal UI changes (similar to an Activity)
+ * which comes via a different channel
*/
- private static final int CHANGE_FLAGS = CHANGE_NAVIGATION_MODE | CHANGE_DENSITY;
- private final DisplayController.DisplayInfoChangeListener mDispInfoChangeListener;
+ private final OnIDPChangeListener mIdpChangeListener = c -> recreateTaskbar();
+ private final SettingsCache.OnChangeListener mOnSettingsChangeListener = c -> recreateTaskbar();
private boolean mUserUnlocked = false;
@@ -144,17 +149,32 @@
}
};
+ private final ActivityLifecycleCallbacksAdapter mLifecycleCallbacks =
+ new ActivityLifecycleCallbacksAdapter() {
+ @Override
+ public void onActivityDestroyed(Activity activity) {
+ if (mActivity != activity) return;
+ if (mActivity != null) {
+ mActivity.removeOnDeviceProfileChangeListener(
+ mDebugActivityDeviceProfileChanged);
+ mActivity.unregisterActivityLifecycleCallbacks(this);
+ }
+ mActivity = null;
+ debugWhyTaskbarNotDestroyed("clearActivity");
+ if (mTaskbarActivityContext != null) {
+ mTaskbarActivityContext.setUIController(TaskbarUIController.DEFAULT);
+ }
+ mUnfoldProgressProvider.setSourceProvider(null);
+ }
+ };
+
@SuppressLint("WrongConstant")
public TaskbarManager(TouchInteractionService service) {
- mDisplayController = DisplayController.INSTANCE.get(service);
Display display =
service.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY);
mContext = service.createWindowContext(display, TYPE_NAVIGATION_BAR_PANEL, null);
mNavButtonController = new TaskbarNavButtonController(service,
SystemUiProxy.INSTANCE.get(mContext), new Handler());
- mUserSetupCompleteListener = isUserSetupComplete -> recreateTaskbar();
- mNavBarKidsModeListener = isNavBarKidsMode -> recreateTaskbar();
- // TODO(b/227669780): Consolidate this w/ DisplayController callbacks
mComponentCallbacks = new ComponentCallbacks() {
private Configuration mOldConfig = mContext.getResources().getConfiguration();
@@ -165,80 +185,42 @@
DeviceProfile dp = mUserUnlocked
? LauncherAppState.getIDP(mContext).getDeviceProfile(mContext)
: null;
- int configDiff = mOldConfig.diff(newConfig);
- int configDiffForRecreate = configDiff;
- int configsRequiringRecreate = ActivityInfo.CONFIG_ASSETS_PATHS
- | ActivityInfo.CONFIG_LAYOUT_DIRECTION | ActivityInfo.CONFIG_UI_MODE
- | ActivityInfo.CONFIG_SCREEN_SIZE;
- if ((configDiff & ActivityInfo.CONFIG_SCREEN_SIZE) != 0
- && mTaskbarActivityContext != null && dp != null
- && !isPhoneMode(dp)) {
- // Additional check since this callback gets fired multiple times w/o
- // screen size changing, or when simply rotating the device.
- // In the case of phone device rotation, we do want to call recreateTaskbar()
- DeviceProfile oldDp = mTaskbarActivityContext.getDeviceProfile();
- boolean isOrientationChange =
- (configDiff & ActivityInfo.CONFIG_ORIENTATION) != 0;
+ int configDiff = mOldConfig.diff(newConfig) & ~SKIP_RECREATE_CONFIG_CHANGES;
- int newOrientation = newConfig.windowConfiguration.getRotation();
- int oldOrientation = mOldConfig.windowConfiguration.getRotation();
- int oldWidth = isOrientationChange ? oldDp.heightPx : oldDp.widthPx;
- int oldHeight = isOrientationChange ? oldDp.widthPx : oldDp.heightPx;
-
- if ((dp.widthPx == oldWidth && dp.heightPx == oldHeight)
- || (newOrientation == oldOrientation)) {
- configDiffForRecreate &= ~ActivityInfo.CONFIG_SCREEN_SIZE;
- }
- }
if ((configDiff & ActivityInfo.CONFIG_UI_MODE) != 0) {
// Only recreate for theme changes, not other UI mode changes such as docking.
int oldUiNightMode = (mOldConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK);
int newUiNightMode = (newConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK);
if (oldUiNightMode == newUiNightMode) {
- configDiffForRecreate &= ~ActivityInfo.CONFIG_UI_MODE;
+ configDiff &= ~ActivityInfo.CONFIG_UI_MODE;
}
}
debugWhyTaskbarNotDestroyed("ComponentCallbacks#onConfigurationChanged() "
- + "configDiffForRecreate="
- + Configuration.configurationDiffToString(configDiffForRecreate));
- if ((configDiffForRecreate & configsRequiringRecreate) != 0) {
+ + "configDiff=" + Configuration.configurationDiffToString(configDiff));
+ if (configDiff != 0 || mTaskbarActivityContext == null) {
recreateTaskbar();
} else {
// Config change might be handled without re-creating the taskbar
- if (mTaskbarActivityContext != null) {
- if (dp != null && !isTaskbarPresent(dp)) {
- destroyExistingTaskbar();
- } else {
- if (dp != null && isTaskbarPresent(dp)) {
- mTaskbarActivityContext.updateDeviceProfile(dp, mNavMode);
- }
- mTaskbarActivityContext.onConfigurationChanged(configDiff);
+ if (dp != null && !isTaskbarPresent(dp)) {
+ destroyExistingTaskbar();
+ } else {
+ if (dp != null && isTaskbarPresent(dp)) {
+ mTaskbarActivityContext.updateDeviceProfile(dp);
}
+ mTaskbarActivityContext.onConfigurationChanged(configDiff);
}
}
- mOldConfig = newConfig;
+ mOldConfig = new Configuration(newConfig);
}
@Override
public void onLowMemory() { }
};
- mShutdownReceiver = new SimpleBroadcastReceiver(i ->
- destroyExistingTaskbar());
- mDispInfoChangeListener = (context, info, flags) -> {
- if ((flags & CHANGE_FLAGS) != 0) {
- mNavMode = info.navigationMode;
- recreateTaskbar();
- }
- debugWhyTaskbarNotDestroyed("DisplayInfoChangeListener#"
- + mDisplayController.getChangeFlagsString(flags));
- };
- mNavMode = mDisplayController.getInfo().navigationMode;
- mDisplayController.addChangeListener(mDispInfoChangeListener);
- SettingsCache.INSTANCE.get(mContext).register(USER_SETUP_COMPLETE_URI,
- mUserSetupCompleteListener);
- SettingsCache.INSTANCE.get(mContext).register(NAV_BAR_KIDS_MODE,
- mNavBarKidsModeListener);
+ SettingsCache.INSTANCE.get(mContext)
+ .register(USER_SETUP_COMPLETE_URI, mOnSettingsChangeListener);
+ SettingsCache.INSTANCE.get(mContext)
+ .register(NAV_BAR_KIDS_MODE, mOnSettingsChangeListener);
mContext.registerComponentCallbacks(mComponentCallbacks);
mShutdownReceiver.register(mContext, Intent.ACTION_SHUTDOWN);
UI_HELPER_EXECUTOR.execute(() -> {
@@ -294,6 +276,7 @@
*/
public void onUserUnlocked() {
mUserUnlocked = true;
+ LauncherAppState.getIDP(mContext).addOnChangeListener(mIdpChangeListener);
recreateTaskbar();
}
@@ -306,10 +289,12 @@
}
if (mActivity != null) {
mActivity.removeOnDeviceProfileChangeListener(mDebugActivityDeviceProfileChanged);
+ mActivity.unregisterActivityLifecycleCallbacks(mLifecycleCallbacks);
}
mActivity = activity;
debugWhyTaskbarNotDestroyed("Set mActivity=" + mActivity);
mActivity.addOnDeviceProfileChangeListener(mDebugActivityDeviceProfileChanged);
+ mActivity.registerActivityLifecycleCallbacks(mLifecycleCallbacks);
UnfoldTransitionProgressProvider unfoldTransitionProgressProvider =
getUnfoldTransitionProgressProviderForActivity(activity);
mUnfoldProgressProvider.setSourceProvider(unfoldTransitionProgressProvider);
@@ -349,21 +334,6 @@
}
/**
- * Clears a previously set {@link StatefulActivity}
- */
- public void clearActivity(@NonNull StatefulActivity activity) {
- if (mActivity == activity) {
- mActivity.removeOnDeviceProfileChangeListener(mDebugActivityDeviceProfileChanged);
- mActivity = null;
- debugWhyTaskbarNotDestroyed("clearActivity");
- if (mTaskbarActivityContext != null) {
- mTaskbarActivityContext.setUIController(TaskbarUIController.DEFAULT);
- }
- mUnfoldProgressProvider.setSourceProvider(null);
- }
- }
-
- /**
* This method is called multiple times (ex. initial init, then when user unlocks) in which case
* we fully want to destroy an existing taskbar and create a new one.
* In other case (folding/unfolding) we don't need to remove and add window.
@@ -390,7 +360,7 @@
mTaskbarActivityContext = new TaskbarActivityContext(mContext, dp, mNavButtonController,
mUnfoldProgressProvider);
} else {
- mTaskbarActivityContext.updateDeviceProfile(dp, mNavMode);
+ mTaskbarActivityContext.updateDeviceProfile(dp);
}
mTaskbarActivityContext.init(mSharedState);
@@ -493,11 +463,13 @@
UI_HELPER_EXECUTOR.execute(
() -> mTaskbarBroadcastReceiver.unregisterReceiverSafely(mContext));
destroyExistingTaskbar();
- mDisplayController.removeChangeListener(mDispInfoChangeListener);
- SettingsCache.INSTANCE.get(mContext).unregister(USER_SETUP_COMPLETE_URI,
- mUserSetupCompleteListener);
- SettingsCache.INSTANCE.get(mContext).unregister(NAV_BAR_KIDS_MODE,
- mNavBarKidsModeListener);
+ if (mUserUnlocked) {
+ LauncherAppState.getIDP(mContext).removeOnChangeListener(mIdpChangeListener);
+ }
+ SettingsCache.INSTANCE.get(mContext)
+ .unregister(USER_SETUP_COMPLETE_URI, mOnSettingsChangeListener);
+ SettingsCache.INSTANCE.get(mContext)
+ .unregister(NAV_BAR_KIDS_MODE, mOnSettingsChangeListener);
mContext.unregisterComponentCallbacks(mComponentCallbacks);
mContext.unregisterReceiver(mShutdownReceiver);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java
index 6cf63a9..7692760 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java
@@ -57,7 +57,7 @@
private final TaskbarView mContainer;
// Initialized in init.
- private TaskbarControllers mControllers;
+ protected TaskbarControllers mControllers;
// Used to defer any UI updates during the SUW unstash animation.
private boolean mDeferUpdatesForSUW;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacksFactory.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacksFactory.kt
new file mode 100644
index 0000000..eb03b4a
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacksFactory.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.taskbar
+
+import android.content.Context
+import com.android.launcher3.R
+import com.android.launcher3.util.ResourceBasedOverride
+import com.android.launcher3.util.ResourceBasedOverride.Overrides
+
+/** Creates [TaskbarModelCallbacks] instances. */
+open class TaskbarModelCallbacksFactory : ResourceBasedOverride {
+
+ open fun create(
+ activityContext: TaskbarActivityContext,
+ container: TaskbarView,
+ ): TaskbarModelCallbacks = TaskbarModelCallbacks(activityContext, container)
+
+ companion object {
+ @JvmStatic
+ fun newInstance(context: Context): TaskbarModelCallbacksFactory {
+ return Overrides.getObject(
+ TaskbarModelCallbacksFactory::class.java,
+ context,
+ R.string.taskbar_model_callbacks_factory_class,
+ )
+ }
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
index 610efeb..0f8de34 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
@@ -43,12 +43,15 @@
import com.android.launcher3.R;
import com.android.launcher3.logging.StatsLogManager;
+import com.android.launcher3.statehandlers.DesktopVisibilityController;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
+import com.android.quickstep.LauncherActivityInterface;
import com.android.quickstep.OverviewCommandHelper;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TaskUtils;
import com.android.quickstep.TouchInteractionService;
+import com.android.quickstep.views.DesktopTaskView;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -267,6 +270,15 @@
private void navigateHome() {
TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY);
+
+ if (DesktopTaskView.DESKTOP_IS_PROTO2_ENABLED) {
+ DesktopVisibilityController desktopVisibilityController =
+ LauncherActivityInterface.INSTANCE.getDesktopVisibilityController();
+ if (desktopVisibilityController != null) {
+ desktopVisibilityController.onHomeActionTriggered();
+ }
+ }
+
mService.getOverviewCommandHelper().addCommand(OverviewCommandHelper.TYPE_HOME);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
index 5ea00cf..1c250bf 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
@@ -15,6 +15,7 @@
*/
package com.android.launcher3.taskbar;
+import static com.android.launcher3.taskbar.bubbles.BubbleBarController.BUBBLE_BAR_ENABLED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED;
@@ -23,6 +24,7 @@
import android.view.animation.PathInterpolator;
import com.android.launcher3.anim.AnimatedFloat;
+import com.android.launcher3.util.DisplayController;
import com.android.quickstep.SystemUiProxy;
import java.io.PrintWriter;
@@ -63,6 +65,10 @@
* Updates the scrim state based on the flags.
*/
public void updateStateForSysuiFlags(int stateFlags, boolean skipAnim) {
+ if (BUBBLE_BAR_ENABLED && DisplayController.isTransientTaskbar(mActivity)) {
+ // These scrims aren't used if bubble bar & transient taskbar are active.
+ return;
+ }
final boolean bubblesExpanded = (stateFlags & SYSUI_STATE_BUBBLES_EXPANDED) != 0;
final boolean manageMenuExpanded =
(stateFlags & SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED) != 0;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index daf9c47..5e37cf4 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -18,11 +18,11 @@
import static android.view.HapticFeedbackConstants.LONG_PRESS;
import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_CONTROLS;
+import static com.android.app.animation.Interpolators.EMPHASIZED;
+import static com.android.app.animation.Interpolators.FINAL_FRAME;
+import static com.android.app.animation.Interpolators.INSTANT;
+import static com.android.app.animation.Interpolators.LINEAR;
import static com.android.launcher3.LauncherPrefs.TASKBAR_PINNING_KEY;
-import static com.android.launcher3.anim.Interpolators.EMPHASIZED;
-import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
-import static com.android.launcher3.anim.Interpolators.INSTANT;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_PINNING;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_LONGPRESS_HIDE;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_LONGPRESS_SHOW;
@@ -257,14 +257,15 @@
private boolean mEnableBlockingTimeoutDuringTests = false;
// Evaluate whether the handle should be stashed
+ private final IntPredicate mIsStashedPredicate = flags -> {
+ boolean inApp = hasAnyFlag(flags, FLAGS_IN_APP);
+ boolean stashedInApp = hasAnyFlag(flags, FLAGS_STASHED_IN_APP);
+ boolean stashedLauncherState = hasAnyFlag(flags, FLAG_IN_STASHED_LAUNCHER_STATE);
+ boolean forceStashed = hasAnyFlag(flags, FLAGS_FORCE_STASHED);
+ return (inApp && stashedInApp) || (!inApp && stashedLauncherState) || forceStashed;
+ };
private final StatePropertyHolder mStatePropertyHolder = new StatePropertyHolder(
- flags -> {
- boolean inApp = hasAnyFlag(flags, FLAGS_IN_APP);
- boolean stashedInApp = hasAnyFlag(flags, FLAGS_STASHED_IN_APP);
- boolean stashedLauncherState = hasAnyFlag(flags, FLAG_IN_STASHED_LAUNCHER_STATE);
- boolean forceStashed = hasAnyFlag(flags, FLAGS_FORCE_STASHED);
- return (inApp && stashedInApp) || (!inApp && stashedLauncherState) || forceStashed;
- });
+ mIsStashedPredicate);
private boolean mIsTaskbarSystemActionRegistered = false;
private TaskbarSharedState mTaskbarSharedState;
@@ -504,9 +505,19 @@
}
/**
- * Stash or unstashes the transient taskbar.
+ * Stash or unstashes the transient taskbar, using the default TASKBAR_STASH_DURATION.
+ * If bubble bar exists, it will match taskbars stashing behavior.
*/
public void updateAndAnimateTransientTaskbar(boolean stash) {
+ updateAndAnimateTransientTaskbar(stash, /* shouldBubblesFollow= */ true);
+ }
+
+ /**
+ * Stash or unstashes the transient taskbar.
+ * @param stash whether transient taskbar should be stashed.
+ * @param shouldBubblesFollow whether bubbles should match taskbars behavior.
+ */
+ public void updateAndAnimateTransientTaskbar(boolean stash, boolean shouldBubblesFollow) {
if (!DisplayController.isTransientTaskbar(mActivity)) {
return;
}
@@ -525,6 +536,34 @@
updateStateForFlag(FLAG_STASHED_IN_APP_AUTO, stash);
applyState();
}
+
+ mControllers.bubbleControllers.ifPresent(controllers -> {
+ if (shouldBubblesFollow) {
+ final boolean willStash = mIsStashedPredicate.test(mState);
+ if (willStash != controllers.bubbleStashController.isStashed()) {
+ // Typically bubbles gets stashed / unstashed along with Taskbar, however, if
+ // taskbar is becoming stashed because bubbles is being expanded, we don't want
+ // to stash bubbles.
+ if (willStash) {
+ controllers.bubbleStashController.stashBubbleBar();
+ } else {
+ controllers.bubbleStashController.showBubbleBar(false /* expandBubbles */);
+ }
+ }
+ }
+ });
+ }
+
+ /**
+ * Stashes transient taskbar after it has timed out.
+ */
+ private void updateAndAnimateTransientTaskbarForTimeout() {
+ // If bubbles are expanded we shouldn't stash them when taskbar is hidden
+ // for the timeout.
+ boolean bubbleBarExpanded = mControllers.bubbleControllers.isPresent()
+ && mControllers.bubbleControllers.get().bubbleBarViewController.isExpanded();
+ updateAndAnimateTransientTaskbar(/* stash= */ true,
+ /* shouldBubblesFollow= */ !bubbleBarExpanded);
}
/**
@@ -830,8 +869,11 @@
.setDuration(isStashed ? duration / 2 : duration));
}
- private static void play(AnimatorSet as, Animator a, long startDelay, long duration,
+ private static void play(AnimatorSet as, @Nullable Animator a, long startDelay, long duration,
Interpolator interpolator) {
+ if (a == null) {
+ return;
+ }
a.setDuration(duration);
a.setStartDelay(startDelay);
a.setInterpolator(interpolator);
@@ -897,7 +939,7 @@
private void onIsStashedChanged(boolean isStashed) {
mControllers.runAfterInit(() -> {
mControllers.stashedHandleViewController.onIsStashedChanged(isStashed);
- mControllers.taskbarInsetsController.onTaskbarWindowHeightOrInsetsChanged();
+ mControllers.taskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged();
});
}
@@ -1147,7 +1189,7 @@
if (mControllers.taskbarAutohideSuspendController.isTransientTaskbarStashingSuspended()) {
return;
}
- updateAndAnimateTransientTaskbar(true);
+ updateAndAnimateTransientTaskbarForTimeout();
}
@Override
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashViaTouchController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashViaTouchController.kt
index 1cc6672..deaf024 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashViaTouchController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashViaTouchController.kt
@@ -16,9 +16,9 @@
package com.android.launcher3.taskbar
import android.view.MotionEvent
+import com.android.app.animation.Interpolators.LINEAR
import com.android.launcher3.R
import com.android.launcher3.Utilities
-import com.android.launcher3.anim.Interpolators.LINEAR
import com.android.launcher3.testing.shared.ResourceUtils
import com.android.launcher3.touch.SingleAxisSwipeDetector
import com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_NEGATIVE
@@ -108,7 +108,17 @@
}
override fun onControllerInterceptTouchEvent(ev: MotionEvent): Boolean {
- if (!enabled || controllers.taskbarStashController.isStashed) {
+ if (!enabled) {
+ return false
+ }
+ val bubbleControllers = controllers.bubbleControllers.orElse(null)
+ if (bubbleControllers != null && bubbleControllers.bubbleBarViewController.isExpanded) {
+ return false
+ }
+ if (
+ (bubbleControllers == null || bubbleControllers.bubbleStashController.isStashed) &&
+ controllers.taskbarStashController.isStashed
+ ) {
return false
}
@@ -122,7 +132,12 @@
return true
}
} else if (ev.action == MotionEvent.ACTION_DOWN) {
- if (screenCoordinatesEv.y < gestureHeightYThreshold) {
+ val isDownOnBubbleBar =
+ (bubbleControllers != null &&
+ bubbleControllers.bubbleBarViewController.isEventOverAnyItem(
+ screenCoordinatesEv
+ ))
+ if (!isDownOnBubbleBar && screenCoordinatesEv.y < gestureHeightYThreshold) {
controllers.taskbarStashController.updateAndAnimateTransientTaskbar(true)
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java
index 065d111..916b1e6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java
@@ -26,8 +26,8 @@
import androidx.annotation.Nullable;
import androidx.dynamicanimation.animation.SpringForce;
+import com.android.app.animation.Interpolators;
import com.android.launcher3.anim.AnimatedFloat;
-import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.SpringAnimationBuilder;
import com.android.launcher3.util.DisplayController;
@@ -92,6 +92,10 @@
mControllers.stashedHandleViewController.setTranslationYForSwipe(transY);
mControllers.taskbarViewController.setTranslationYForSwipe(transY);
mControllers.taskbarDragLayerController.setTranslationYForSwipe(transY);
+ mControllers.bubbleControllers.ifPresent(controllers -> {
+ controllers.bubbleBarViewController.setTranslationYForSwipe(transY);
+ controllers.bubbleStashedHandleViewController.setTranslationYForSwipe(transY);
+ });
}
/**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index 4abd995..29f4f38 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.taskbar;
+import static com.android.app.animation.Interpolators.FINAL_FRAME;
+import static com.android.app.animation.Interpolators.LINEAR;
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
@@ -22,8 +24,6 @@
import static com.android.launcher3.Utilities.squaredHypot;
import static com.android.launcher3.anim.AnimatedFloat.VALUE;
import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
-import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_ALLAPPS_BUTTON_TAP;
import static com.android.launcher3.taskbar.TaskbarManager.isPhoneMode;
import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
@@ -45,6 +45,7 @@
import androidx.core.graphics.ColorUtils;
import androidx.core.view.OneShotPreDrawListener;
+import com.android.app.animation.Interpolators;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
@@ -53,7 +54,6 @@
import com.android.launcher3.anim.AlphaUpdateListener;
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.RevealOutlineAnimation;
import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
@@ -133,7 +133,8 @@
mTaskbarView = taskbarView;
mTaskbarIconAlpha = new MultiValueAlpha(mTaskbarView, NUM_ALPHA_CHANNELS);
mTaskbarIconAlpha.setUpdateVisibility(true);
- mModelCallbacks = new TaskbarModelCallbacks(activity, mTaskbarView);
+ mModelCallbacks = TaskbarModelCallbacksFactory.newInstance(mActivity)
+ .create(mActivity, mTaskbarView);
mTaskbarBottomMargin = activity.getDeviceProfile().taskbarBottomMargin;
mStashedHandleHeight = activity.getResources()
.getDimensionPixelSize(R.dimen.taskbar_stashed_handle_height);
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java
index b4b83f6..5d91acd 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java
@@ -47,9 +47,9 @@
}
@Override
- protected View inflateSearchBox() {
+ protected View inflateSearchBar() {
if (isSearchSupported()) {
- return super.inflateSearchBox();
+ return super.inflateSearchBar();
}
// Remove top padding of header, since we do not have any search
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
index e0a502b..02d9d95 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
@@ -45,13 +45,16 @@
public final class TaskbarAllAppsController {
private TaskbarControllers mControllers;
+ private @Nullable TaskbarOverlayContext mOverlayContext;
private @Nullable TaskbarAllAppsSlideInView mSlideInView;
private @Nullable TaskbarAllAppsContainerView mAppsView;
+ private @Nullable TaskbarSearchSessionController mSearchSessionController;
// Application data models.
private AppInfo[] mApps;
private int mAppsModelFlags;
private List<ItemInfo> mPredictedApps;
+ private @Nullable List<ItemInfo> mZeroStateSearchSuggestions;
private boolean mDisallowGlobalDrag;
private boolean mDisallowLongClick;
@@ -70,6 +73,11 @@
}
}
+ /** Clean up the controller. */
+ public void onDestroy() {
+ cleanUpOverlay();
+ }
+
/** Updates the current {@link AppInfo} instances. */
public void setApps(AppInfo[] apps, int flags, Map<PackageUserKey, Integer> map) {
mApps = apps;
@@ -96,6 +104,17 @@
.findFixedRowByType(PredictionRowView.class)
.setPredictedApps(mPredictedApps);
}
+ if (mSearchSessionController != null) {
+ mSearchSessionController.setZeroStatePredictedItems(predictedApps);
+ }
+ }
+
+ /** Updates the current search suggestions. */
+ public void setZeroStateSearchSuggestions(List<ItemInfo> zeroStateSearchSuggestions) {
+ mZeroStateSearchSuggestions = zeroStateSearchSuggestions;
+ if (mSearchSessionController != null) {
+ mSearchSessionController.setZeroStateSearchSuggestions(zeroStateSearchSuggestions);
+ }
}
/** Updates the current notification dots. */
@@ -127,20 +146,28 @@
// to catch invalid states.
mControllers.getSharedState().allAppsVisible = true;
- TaskbarOverlayContext overlayContext =
- mControllers.taskbarOverlayController.requestWindow();
- mSlideInView = (TaskbarAllAppsSlideInView) overlayContext.getLayoutInflater().inflate(
- R.layout.taskbar_all_apps, overlayContext.getDragLayer(), false);
+ mOverlayContext = mControllers.taskbarOverlayController.requestWindow();
+
+ // Initialize search session for All Apps.
+ mSearchSessionController = TaskbarSearchSessionController.newInstance(mOverlayContext);
+ mOverlayContext.setSearchSessionController(mSearchSessionController);
+ mSearchSessionController.setZeroStatePredictedItems(mPredictedApps);
+ if (mZeroStateSearchSuggestions != null) {
+ mSearchSessionController.setZeroStateSearchSuggestions(mZeroStateSearchSuggestions);
+ }
+ mSearchSessionController.startLifecycle();
+
+ mSlideInView = (TaskbarAllAppsSlideInView) mOverlayContext.getLayoutInflater().inflate(
+ R.layout.taskbar_all_apps_sheet, mOverlayContext.getDragLayer(), false);
mSlideInView.addOnCloseListener(() -> {
mControllers.getSharedState().allAppsVisible = false;
- mSlideInView = null;
- mAppsView = null;
+ cleanUpOverlay();
});
TaskbarAllAppsViewController viewController = new TaskbarAllAppsViewController(
- overlayContext, mSlideInView, mControllers);
+ mOverlayContext, mSlideInView, mControllers);
viewController.show(animate);
- mAppsView = overlayContext.getAppsView();
+ mAppsView = mOverlayContext.getAppsView();
mAppsView.getAppsStore().setApps(mApps, mAppsModelFlags, mPackageUserKeytoUidMap);
mAppsView.getFloatingHeaderView()
.findFixedRowByType(PredictionRowView.class)
@@ -149,8 +176,28 @@
// Create a shared drag layer between taskbar and taskbarAllApps so that when dragging
// starts and taskbarAllApps can close, but the drag layer that the view is being dragged in
// doesn't also close
- overlayContext.getDragController().setDisallowGlobalDrag(mDisallowGlobalDrag);
- overlayContext.getDragController().setDisallowLongClick(mDisallowLongClick);
+ mOverlayContext.getDragController().setDisallowGlobalDrag(mDisallowGlobalDrag);
+ mOverlayContext.getDragController().setDisallowLongClick(mDisallowLongClick);
+ }
+
+ private void cleanUpOverlay() {
+ // Floating search bar is added to the drag layer in ActivityAllAppsContainerView onAttach;
+ // removed here as this is a special case that we remove the all apps panel.
+ if (mAppsView != null && mOverlayContext != null
+ && mAppsView.getSearchUiDelegate().isSearchBarFloating()) {
+ mOverlayContext.getDragLayer().removeView(mAppsView.getSearchView());
+ mAppsView.getSearchUiDelegate().onDestroySearchBar();
+ }
+ if (mSearchSessionController != null) {
+ mSearchSessionController.onDestroy();
+ mSearchSessionController = null;
+ }
+ if (mOverlayContext != null) {
+ mOverlayContext.setSearchSessionController(null);
+ mOverlayContext = null;
+ }
+ mSlideInView = null;
+ mAppsView = null;
}
@VisibleForTesting
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
index cfa1027..84cc002 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
@@ -15,7 +15,7 @@
*/
package com.android.launcher3.taskbar.allapps;
-import static com.android.launcher3.anim.Interpolators.EMPHASIZED;
+import static com.android.app.animation.Interpolators.EMPHASIZED;
import android.animation.PropertyValuesHolder;
import android.content.Context;
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarSearchSessionController.kt b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarSearchSessionController.kt
new file mode 100644
index 0000000..324c1a2
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarSearchSessionController.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.taskbar.allapps
+
+import android.content.Context
+import com.android.launcher3.R
+import com.android.launcher3.config.FeatureFlags
+import com.android.launcher3.model.data.ItemInfo
+import com.android.launcher3.util.ResourceBasedOverride
+import com.android.launcher3.util.ResourceBasedOverride.Overrides
+
+/** Stub for managing the Taskbar search session. */
+open class TaskbarSearchSessionController : ResourceBasedOverride {
+
+ /** Start the search session lifecycle. */
+ open fun startLifecycle() {}
+
+ /** Destroy the search session. */
+ open fun onDestroy() {}
+
+ /** Updates the predicted items shown in the zero-state. */
+ open fun setZeroStatePredictedItems(items: List<ItemInfo>) {}
+
+ /** Updates the search suggestions shown in the zero-state. */
+ open fun setZeroStateSearchSuggestions(items: List<ItemInfo>) {}
+
+ companion object {
+ @JvmStatic
+ fun newInstance(context: Context): TaskbarSearchSessionController {
+ if (!FeatureFlags.ENABLE_ALL_APPS_SEARCH_IN_TASKBAR.get()) {
+ return TaskbarSearchSessionController()
+ }
+
+ return Overrides.getObject(
+ TaskbarSearchSessionController::class.java,
+ context,
+ R.string.taskbar_search_session_controller_class,
+ )
+ }
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt
index 7397159..8a8e21f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt
@@ -21,10 +21,10 @@
import android.graphics.Paint
import android.graphics.drawable.Drawable
import android.graphics.drawable.ShapeDrawable
+import com.android.app.animation.Interpolators
import com.android.launcher3.R
import com.android.launcher3.Utilities
import com.android.launcher3.Utilities.mapToRange
-import com.android.launcher3.anim.Interpolators
import com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound
import com.android.launcher3.taskbar.TaskbarActivityContext
import com.android.wm.shell.common.TriangleShape
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
index 6d19692..6b5c962 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
@@ -38,11 +38,15 @@
import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.content.pm.ShortcutInfo;
+import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Path;
+import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.InsetDrawable;
import android.os.Bundle;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -51,6 +55,8 @@
import android.util.PathParser;
import android.view.LayoutInflater;
+import androidx.appcompat.content.res.AppCompatResources;
+
import com.android.internal.graphics.ColorUtils;
import com.android.launcher3.R;
import com.android.launcher3.icons.BitmapInfo;
@@ -113,7 +119,8 @@
private final LauncherApps mLauncherApps;
private final BubbleIconFactory mIconFactory;
- private BubbleBarBubble mSelectedBubble;
+ private BubbleBarItem mSelectedBubble;
+ private BubbleBarOverflow mOverflowBubble;
private BubbleBarViewController mBubbleBarViewController;
private BubbleStashController mBubbleStashController;
@@ -181,6 +188,26 @@
}
/**
+ * Creates and adds the overflow bubble to the bubble bar if it hasn't been created yet.
+ *
+ * <p>This should be called on the {@link #BUBBLE_STATE_EXECUTOR} executor to avoid inflating
+ * the overflow multiple times.
+ */
+ private void createAndAddOverflowIfNeeded() {
+ if (mOverflowBubble == null) {
+ BubbleBarOverflow overflow = createOverflow(mContext);
+ mMainExecutor.execute(() -> {
+ // we're on the main executor now, so check that the overflow hasn't been created
+ // again to avoid races.
+ if (mOverflowBubble == null) {
+ mBubbleBarViewController.addBubble(overflow);
+ mOverflowBubble = overflow;
+ }
+ });
+ }
+ }
+
+ /**
* Updates the bubble bar, handle bar, and stash controllers based on sysui state flags.
*/
public void updateStateForSysuiFlags(int flags) {
@@ -209,6 +236,7 @@
|| !update.currentBubbleList.isEmpty()) {
// We have bubbles to load
BUBBLE_STATE_EXECUTOR.execute(() -> {
+ createAndAddOverflowIfNeeded();
if (update.addedBubble != null) {
viewUpdate.addedBubble = populateBubble(update.addedBubble, mContext, mBarView);
}
@@ -329,7 +357,7 @@
* WMShell that the selection has changed, that should go through
* {@link SystemUiProxy#showBubble}.
*/
- public void setSelectedBubble(BubbleBarBubble b) {
+ public void setSelectedBubble(BubbleBarItem b) {
if (!Objects.equals(b, mSelectedBubble)) {
if (DEBUG) Log.w(TAG, "selectingBubble: " + b.getKey());
mSelectedBubble = b;
@@ -424,7 +452,6 @@
dotColor = ColorUtils.blendARGB(badgeBitmapInfo.color,
Color.WHITE, WHITE_SCRIM_ALPHA);
-
LayoutInflater inflater = LayoutInflater.from(context);
BubbleView bubbleView = (BubbleView) inflater.inflate(
R.layout.bubblebar_item_view, bbv, false /* attachToRoot */);
@@ -434,4 +461,37 @@
bubbleView.setBubble(bubble);
return bubble;
}
+
+ private BubbleBarOverflow createOverflow(Context context) {
+ Bitmap bitmap = createOverflowBitmap(context);
+ LayoutInflater inflater = LayoutInflater.from(context);
+ BubbleView bubbleView = (BubbleView) inflater.inflate(
+ R.layout.bubblebar_item_view, mBarView, false /* attachToRoot */);
+ BubbleBarOverflow overflow = new BubbleBarOverflow(bubbleView);
+ bubbleView.setOverflow(overflow, bitmap);
+ return overflow;
+ }
+
+ private Bitmap createOverflowBitmap(Context context) {
+ Drawable iconDrawable = AppCompatResources.getDrawable(mContext,
+ R.drawable.bubble_ic_overflow_button);
+
+ final TypedArray ta = mContext.obtainStyledAttributes(
+ new int[]{
+ com.android.internal.R.attr.materialColorOnPrimaryFixed,
+ com.android.internal.R.attr.materialColorPrimaryFixed
+ });
+ int overflowIconColor = ta.getColor(0, Color.WHITE);
+ int overflowBackgroundColor = ta.getColor(1, Color.BLACK);
+ ta.recycle();
+
+ iconDrawable.setTint(overflowIconColor);
+
+ int inset = context.getResources().getDimensionPixelSize(R.dimen.bubblebar_overflow_inset);
+ Drawable foreground = new InsetDrawable(iconDrawable, inset);
+ Drawable drawable = new AdaptiveIconDrawable(new ColorDrawable(overflowBackgroundColor),
+ foreground);
+
+ return mIconFactory.createBadgedIconBitmap(drawable).icon;
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBubble.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarItem.kt
similarity index 75%
rename from quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBubble.kt
rename to quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarItem.kt
index 3cd5f75..582dcc7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBubble.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarItem.kt
@@ -19,16 +19,19 @@
import android.graphics.Path
import com.android.wm.shell.common.bubbles.BubbleInfo
+/** An entity in the bubble bar. */
+sealed class BubbleBarItem(open val key: String, open val view: BubbleView)
+
/** Contains state info about a bubble in the bubble bar as well as presentation information. */
data class BubbleBarBubble(
val info: BubbleInfo,
- val view: BubbleView,
+ override val view: BubbleView,
val badge: Bitmap,
val icon: Bitmap,
val dotColor: Int,
val dotPath: Path,
val appName: String
-) {
+) : BubbleBarItem(info.key, view)
- val key: String = info.key
-}
+/** Represents the overflow bubble in the bubble bar. */
+data class BubbleBarOverflow(override val view: BubbleView) : BubbleBarItem("Overflow", view)
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
index 0e1e0e1..8d20705 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
@@ -150,7 +150,9 @@
@Override
public void addView(View child, int index, ViewGroup.LayoutParams params) {
if (getChildCount() + 1 > MAX_BUBBLES) {
- removeViewInLayout(getChildAt(getChildCount() - 1));
+ // the last child view is the overflow bubble and we shouldn't remove that. remove the
+ // second to last child view.
+ removeViewInLayout(getChildAt(getChildCount() - 2));
}
super.addView(child, index, params);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
index 82494c6..52c144e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
@@ -28,6 +28,8 @@
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.launcher3.taskbar.TaskbarControllers;
+import com.android.launcher3.taskbar.TaskbarInsetsController;
+import com.android.launcher3.taskbar.TaskbarStashController;
import com.android.launcher3.util.MultiPropertyFactory;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.quickstep.SystemUiProxy;
@@ -51,6 +53,8 @@
// Initialized in init.
private BubbleStashController mBubbleStashController;
private BubbleBarController mBubbleBarController;
+ private TaskbarStashController mTaskbarStashController;
+ private TaskbarInsetsController mTaskbarInsetsController;
private View.OnClickListener mBubbleClickListener;
private View.OnClickListener mBubbleBarClickListener;
@@ -80,6 +84,8 @@
public void init(TaskbarControllers controllers, BubbleControllers bubbleControllers) {
mBubbleStashController = bubbleControllers.bubbleStashController;
mBubbleBarController = bubbleControllers.bubbleBarController;
+ mTaskbarStashController = controllers.taskbarStashController;
+ mTaskbarInsetsController = controllers.taskbarInsetsController;
mActivity.addOnDeviceProfileChangeListener(dp ->
mBarView.getLayoutParams().height = mActivity.getDeviceProfile().taskbarHeight
@@ -89,11 +95,13 @@
mBubbleClickListener = v -> onBubbleClicked(v);
mBubbleBarClickListener = v -> setExpanded(true);
mBarView.setOnClickListener(mBubbleBarClickListener);
- // TODO: when barView layout changes tell taskbarInsetsController the insets have changed.
+ mBarView.addOnLayoutChangeListener((view, i, i1, i2, i3, i4, i5, i6, i7) ->
+ mTaskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged()
+ );
}
private void onBubbleClicked(View v) {
- BubbleBarBubble bubble = ((BubbleView) v).getBubble();
+ BubbleBarItem bubble = ((BubbleView) v).getBubble();
if (bubble == null) {
Log.e(TAG, "bubble click listener, bubble was null");
}
@@ -228,7 +236,7 @@
/**
* Removes the provided bubble from the bubble bar.
*/
- public void removeBubble(BubbleBarBubble b) {
+ public void removeBubble(BubbleBarItem b) {
if (b != null) {
mBarView.removeView(b.getView());
} else {
@@ -239,7 +247,7 @@
/**
* Adds the provided bubble to the bubble bar.
*/
- public void addBubble(BubbleBarBubble b) {
+ public void addBubble(BubbleBarItem b) {
if (b != null) {
mBarView.addView(b.getView(), 0, new FrameLayout.LayoutParams(mIconSize, mIconSize));
b.getView().setOnClickListener(mBubbleClickListener);
@@ -260,7 +268,7 @@
/**
* Updates the selected bubble.
*/
- public void updateSelectedBubble(BubbleBarBubble newlySelected) {
+ public void updateSelectedBubble(BubbleBarItem newlySelected) {
mBarView.setSelectedBubble(newlySelected.getView());
}
@@ -283,7 +291,8 @@
} else {
Log.w(TAG, "trying to expand bubbles when there isn't one selected");
}
- // TODO: Tell taskbar stash controller to stash without bubbles following
+ mTaskbarStashController.updateAndAnimateTransientTaskbar(true /* stash */,
+ false /* shouldBubblesFollow */);
}
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java
index 0ab53b0..b3c7d41 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java
@@ -25,6 +25,7 @@
import com.android.launcher3.taskbar.StashedHandleViewController;
import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.launcher3.taskbar.TaskbarControllers;
+import com.android.launcher3.taskbar.TaskbarInsetsController;
import com.android.launcher3.taskbar.TaskbarStashController;
import com.android.launcher3.util.MultiPropertyFactory;
@@ -50,6 +51,7 @@
// Initialized in init.
private TaskbarControllers mControllers;
+ private TaskbarInsetsController mTaskbarInsetsController;
private BubbleBarViewController mBarViewController;
private BubbleStashedHandleViewController mHandleViewController;
private TaskbarStashController mTaskbarStashController;
@@ -77,6 +79,7 @@
public void init(TaskbarControllers controllers, BubbleControllers bubbleControllers) {
mControllers = controllers;
+ mTaskbarInsetsController = controllers.taskbarInsetsController;
mBarViewController = bubbleControllers.bubbleBarViewController;
mHandleViewController = bubbleControllers.bubbleStashedHandleViewController;
mTaskbarStashController = controllers.taskbarStashController;
@@ -271,7 +274,7 @@
private void onIsStashedChanged() {
mControllers.runAfterInit(() -> {
mHandleViewController.onIsStashedChanged();
- // TODO: when stash changes tell taskbarInsetsController the insets have changed.
+ mTaskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged();
});
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java
index 2170a5d..26756d4 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java
@@ -203,12 +203,14 @@
private void updateRegionSampling() {
boolean handleVisible = mStashedHandleView.getVisibility() == VISIBLE
&& mBubbleStashController.isStashed();
- mRegionSamplingHelper.setWindowVisible(handleVisible);
- if (handleVisible) {
- mStashedHandleView.updateSampledRegion(mStashedHandleBounds);
- mRegionSamplingHelper.start(mStashedHandleView.getSampledRegion());
- } else {
- mRegionSamplingHelper.stop();
+ if (mRegionSamplingHelper != null) {
+ mRegionSamplingHelper.setWindowVisible(handleVisible);
+ if (handleVisible) {
+ mStashedHandleView.updateSampledRegion(mStashedHandleBounds);
+ mRegionSamplingHelper.start(mStashedHandleView.getSampledRegion());
+ } else {
+ mRegionSamplingHelper.stop();
+ }
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java
index e22e63a..92b76a6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java
@@ -32,6 +32,7 @@
// TODO: (b/276978250) This is will be similar to WMShell's BadgedImageView, it'd be nice to share.
// TODO: (b/269670235) currently this doesn't show the 'update dot'
+
/**
* View that displays a bubble icon, along with an app badge on either the left or
* right side of the view.
@@ -48,7 +49,7 @@
// TODO: (b/273310265) handle RTL
private boolean mOnLeft = false;
- private BubbleBarBubble mBubble;
+ private BubbleBarItem mBubble;
public BubbleView(Context context) {
this(context, null);
@@ -97,15 +98,27 @@
mAppIcon.setImageBitmap(bubble.getBadge());
}
+ void setOverflow(BubbleBarOverflow overflow, Bitmap bitmap) {
+ mBubble = overflow;
+ mBubbleIcon.setImageBitmap(bitmap);
+ hideBadge();
+ }
+
/** Returns the bubble being rendered in this view. */
@Nullable
- BubbleBarBubble getBubble() {
+ BubbleBarItem getBubble() {
return mBubble;
}
/** Shows the app badge on this bubble. */
void showBadge() {
- Bitmap appBadgeBitmap = mBubble.getBadge();
+ if (mBubble instanceof BubbleBarOverflow) {
+ // The overflow bubble does not have a badge, so just bail.
+ return;
+ }
+ BubbleBarBubble bubble = (BubbleBarBubble) mBubble;
+
+ Bitmap appBadgeBitmap = bubble.getBadge();
if (appBadgeBitmap == null) {
mAppIcon.setVisibility(GONE);
return;
@@ -113,7 +126,7 @@
int translationX;
if (mOnLeft) {
- translationX = -(mBubble.getIcon().getWidth() - appBadgeBitmap.getWidth());
+ translationX = -(bubble.getIcon().getWidth() - appBadgeBitmap.getWidth());
} else {
translationX = 0;
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/OWNERS b/quickstep/src/com/android/launcher3/taskbar/bubbles/OWNERS
new file mode 100644
index 0000000..7af0389
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/OWNERS
@@ -0,0 +1 @@
+madym@google.com
diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java
index a642693..cfcc1a0 100644
--- a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java
@@ -18,6 +18,8 @@
import android.content.Context;
import android.view.View;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.dot.DotInfo;
@@ -29,6 +31,7 @@
import com.android.launcher3.taskbar.TaskbarDragController;
import com.android.launcher3.taskbar.TaskbarUIController;
import com.android.launcher3.taskbar.allapps.TaskbarAllAppsContainerView;
+import com.android.launcher3.taskbar.allapps.TaskbarSearchSessionController;
import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource;
/**
@@ -47,6 +50,8 @@
private final int mStashedTaskbarHeight;
private final TaskbarUIController mUiController;
+ private @Nullable TaskbarSearchSessionController mSearchSessionController;
+
public TaskbarOverlayContext(
Context windowContext,
TaskbarActivityContext taskbarContext,
@@ -62,6 +67,15 @@
mUiController = controllers.uiController;
}
+ public @Nullable TaskbarSearchSessionController getSearchSessionController() {
+ return mSearchSessionController;
+ }
+
+ public void setSearchSessionController(
+ @Nullable TaskbarSearchSessionController searchSessionController) {
+ mSearchSessionController = searchSessionController;
+ }
+
int getStashedTaskbarHeight() {
return mStashedTaskbarHeight;
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
index 955440b..d241260 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
@@ -16,11 +16,11 @@
package com.android.launcher3.uioverrides;
+import static com.android.app.animation.Interpolators.AGGRESSIVE_EASE_IN_OUT;
+import static com.android.app.animation.Interpolators.FINAL_FRAME;
+import static com.android.app.animation.Interpolators.INSTANT;
+import static com.android.app.animation.Interpolators.LINEAR;
import static com.android.launcher3.LauncherState.QUICK_SWITCH_FROM_HOME;
-import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE_IN_OUT;
-import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
-import static com.android.launcher3.anim.Interpolators.INSTANT;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_MODAL;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
@@ -46,6 +46,7 @@
import com.android.launcher3.LauncherState;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.statemanager.StateManager.StateHandler;
import com.android.launcher3.states.StateAnimationConfig;
@@ -113,7 +114,9 @@
setter.setFloat(mRecentsView, TASK_SECONDARY_TRANSLATION, 0f,
config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, LINEAR));
- if (mRecentsView.isSplitSelectionActive()) {
+ boolean exitingOverview = !FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()
+ || !toState.overviewUi;
+ if (mRecentsView.isSplitSelectionActive() && exitingOverview) {
// TODO (b/238651489): Refactor state management to avoid need for double check
FloatingTaskView floatingTask = mRecentsView.getFirstFloatingTaskView();
if (floatingTask != null) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/DejankBinderTracker.java b/quickstep/src/com/android/launcher3/uioverrides/DejankBinderTracker.java
deleted file mode 100644
index d8aa235..0000000
--- a/quickstep/src/com/android/launcher3/uioverrides/DejankBinderTracker.java
+++ /dev/null
@@ -1,159 +0,0 @@
-/**
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.uioverrides;
-
-import static android.os.IBinder.FLAG_ONEWAY;
-
-import android.os.Binder;
-import android.os.Build;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.RemoteException;
-import android.util.Log;
-
-import androidx.annotation.MainThread;
-
-import java.util.HashSet;
-import java.util.Locale;
-import java.util.function.BiConsumer;
-import java.util.function.Supplier;
-
-/**
- * A binder proxy transaction listener for tracking non-whitelisted binder calls.
- */
-public class DejankBinderTracker implements Binder.ProxyTransactListener {
- private static final String TAG = "DejankBinderTracker";
-
- private static final Object sLock = new Object();
- private static final HashSet<String> sWhitelistedFrameworkClasses = new HashSet<>();
- static {
- // Common IPCs that are ok to block the main thread.
- sWhitelistedFrameworkClasses.add("android.view.IWindowSession");
- sWhitelistedFrameworkClasses.add("android.os.IPowerManager");
- }
- private static boolean sTemporarilyIgnoreTracking = false;
-
- // Used by the client to limit binder tracking to specific regions
- private static boolean sTrackingAllowed = false;
-
- private BiConsumer<String, Integer> mUnexpectedTransactionCallback;
- private boolean mIsTracking = false;
-
- /**
- * Temporarily ignore blocking binder calls for the duration of this {@link Runnable}.
- */
- @MainThread
- public static void whitelistIpcs(Runnable runnable) {
- sTemporarilyIgnoreTracking = true;
- runnable.run();
- sTemporarilyIgnoreTracking = false;
- }
-
- /**
- * Temporarily ignore blocking binder calls for the duration of this {@link Supplier}.
- */
- @MainThread
- public static <T> T whitelistIpcs(Supplier<T> supplier) {
- sTemporarilyIgnoreTracking = true;
- T value = supplier.get();
- sTemporarilyIgnoreTracking = false;
- return value;
- }
-
- /**
- * Enables binder tracking during a test.
- */
- @MainThread
- public static void allowBinderTrackingInTests() {
- sTrackingAllowed = true;
- }
-
- /**
- * Disables binder tracking during a test.
- */
- @MainThread
- public static void disallowBinderTrackingInTests() {
- sTrackingAllowed = false;
- }
-
- public DejankBinderTracker(BiConsumer<String, Integer> unexpectedTransactionCallback) {
- mUnexpectedTransactionCallback = unexpectedTransactionCallback;
- }
-
- @MainThread
- public void startTracking() {
- if (!Build.TYPE.toLowerCase(Locale.ROOT).contains("debug")
- && !Build.TYPE.toLowerCase(Locale.ROOT).equals("eng")) {
- Log.wtf(TAG, "Unexpected use of binder tracker in non-debug build", new Exception());
- return;
- }
- if (mIsTracking) {
- return;
- }
- mIsTracking = true;
- Binder.setProxyTransactListener(this);
- }
-
- @MainThread
- public void stopTracking() {
- if (!mIsTracking) {
- return;
- }
- mIsTracking = false;
- Binder.setProxyTransactListener(null);
- }
-
- // Override the hidden Binder#onTransactStarted method
- public synchronized Object onTransactStarted(IBinder binder, int transactionCode, int flags) {
- if (!mIsTracking
- || !sTrackingAllowed
- || sTemporarilyIgnoreTracking
- || (flags & FLAG_ONEWAY) == FLAG_ONEWAY
- || !isMainThread()) {
- return null;
- }
-
- String descriptor;
- try {
- descriptor = binder.getInterfaceDescriptor();
- if (sWhitelistedFrameworkClasses.contains(descriptor)) {
- return null;
- }
- } catch (RemoteException e) {
- e.printStackTrace();
- descriptor = binder.getClass().getSimpleName();
- }
-
- mUnexpectedTransactionCallback.accept(descriptor, transactionCode);
- return null;
- }
-
- @Override
- public Object onTransactStarted(IBinder binder, int transactionCode) {
- // Do nothing
- return null;
- }
-
- @Override
- public void onTransactEnded(Object session) {
- // Do nothing
- }
-
- public static boolean isMainThread() {
- return Thread.currentThread() == Looper.getMainLooper().getThread();
- }
-}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
index a8b7698..2064fe2 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
@@ -15,7 +15,7 @@
*/
package com.android.launcher3.uioverrides;
-import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
+import static com.android.app.animation.Interpolators.ACCELERATE_DECELERATE;
import static com.android.launcher3.icons.BitmapInfo.FLAG_THEMED;
import static com.android.launcher3.icons.FastBitmapDrawable.getDisabledColorFilter;
@@ -189,7 +189,7 @@
} else {
float[] hctPlateColor = new float[3];
ColorUtils.colorToM3HCT(mDotParams.appColor, hctPlateColor);
- newPlateColor = ColorUtils.M3HCTtoColor(hctPlateColor[0], 36, 85);
+ newPlateColor = ColorUtils.M3HCTToColor(hctPlateColor[0], 36, 85);
}
if (!animate) {
@@ -260,8 +260,8 @@
Keyframe.ofFloat(0.82f, finalTrans - getOutlineOffsetY() / 2f), // Overshoot
Keyframe.ofFloat(1f, finalTrans) // Ease back into the final position
};
- keyframes[1].setInterpolator(ACCEL_DEACCEL);
- keyframes[2].setInterpolator(ACCEL_DEACCEL);
+ keyframes[1].setInterpolator(ACCELERATE_DECELERATE);
+ keyframes[2].setInterpolator(ACCELERATE_DECELERATE);
mSlotMachineAnim = ObjectAnimator.ofPropertyValuesHolder(this,
PropertyValuesHolder.ofKeyframe(SLOT_MACHINE_TRANSLATION_Y, keyframes));
@@ -337,7 +337,6 @@
if (getTag() instanceof WorkspaceItemInfo) {
WorkspaceItemInfo info = (WorkspaceItemInfo) getTag();
isBadged = !Process.myUserHandle().equals(info.user)
- || info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT
|| info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 658bf2d..512d5f4 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -20,19 +20,18 @@
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_OPTIMIZE_MEASURE;
import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED;
+import static com.android.app.animation.Interpolators.EMPHASIZED;
import static com.android.launcher3.LauncherSettings.Animation.DEFAULT_NO_ICON;
import static com.android.launcher3.LauncherSettings.Animation.VIEW_BACKGROUND;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
-import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.NO_OFFSET;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK;
import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
-import static com.android.launcher3.anim.Interpolators.EMPHASIZED;
import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE;
import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE;
@@ -211,8 +210,6 @@
private QuickstepTransitionManager mAppTransitionManager;
private OverviewActionsView mActionsView;
private TISBindHelper mTISBindHelper;
- private @Nullable TaskbarManager mTaskbarManager;
- private @Nullable OverviewCommandHelper mOverviewCommandHelper;
private @Nullable LauncherTaskbarUIController mTaskbarUIController;
// Will be updated when dragging from taskbar.
private @Nullable DragOptions mNextWorkspaceDragOptions = null;
@@ -258,6 +255,9 @@
mTISBindHelper = new TISBindHelper(this, this::onTISConnected);
mDepthController = new DepthController(this);
mDesktopVisibilityController = new DesktopVisibilityController(this);
+ if (DesktopTaskView.DESKTOP_MODE_SUPPORTED) {
+ mDesktopVisibilityController.registerSystemUiListener();
+ }
mHotseatPredictionController = new HotseatPredictionController(this);
mEnableWidgetDepth = SystemProperties.getBoolean("ro.launcher.depth.widget", true);
@@ -279,7 +279,6 @@
if (mAllAppsPredictions != null
&& (info.itemType == ITEM_TYPE_APPLICATION
- || info.itemType == ITEM_TYPE_SHORTCUT
|| info.itemType == ITEM_TYPE_DEEP_SHORTCUT)) {
int count = mAllAppsPredictions.items.size();
for (int i = 0; i < count; i++) {
@@ -470,18 +469,23 @@
public void onDestroy() {
mAppTransitionManager.onActivityDestroyed();
if (mUnfoldTransitionProgressProvider != null) {
+ if (FeatureFlags.RECEIVE_UNFOLD_EVENTS_FROM_SYSUI.get()) {
+ SystemUiProxy.INSTANCE.get(this).setUnfoldAnimationListener(null);
+ }
+
mUnfoldTransitionProgressProvider.destroy();
}
mTISBindHelper.onDestroy();
- if (mTaskbarManager != null) {
- mTaskbarManager.clearActivity(this);
- }
if (mLauncherUnfoldAnimationController != null) {
mLauncherUnfoldAnimationController.onDestroy();
}
+ if (mDesktopVisibilityController != null) {
+ mDesktopVisibilityController.unregisterSystemUiListener();
+ }
+
super.onDestroy();
mHotseatPredictionController.destroy();
mSplitWithKeyboardShortcutController.onDestroy();
@@ -515,7 +519,7 @@
}
case QUICK_SWITCH_STATE_ORDINAL: {
RecentsView rv = getOverviewPanel();
- TaskView tasktolaunch = rv.getTaskViewAt(0);
+ TaskView tasktolaunch = rv.getCurrentPageTaskView();
if (tasktolaunch != null) {
tasktolaunch.launchTask(success -> {
if (!success) {
@@ -553,8 +557,14 @@
list.add(new PortraitStatesTouchController(this));
break;
case THREE_BUTTONS:
+ list.add(new NoButtonQuickSwitchTouchController(this));
+ list.add(new NavBarToHomeTouchController(this));
+ list.add(new NoButtonNavbarToOverviewTouchController(this));
+ list.add(new PortraitStatesTouchController(this));
+ break;
default:
list.add(new PortraitStatesTouchController(this));
+ break;
}
if (!getDeviceProfile().isMultiWindowMode) {
@@ -605,9 +615,10 @@
mSplitSelectStateController.findLastActiveTaskAndRunCallback(
splitSelectSource.itemInfo.getComponentKey(),
foundTask -> {
- splitSelectSource.alreadyRunningTaskId = foundTask == null
- ? INVALID_TASK_ID
- : foundTask.key.id;
+ boolean taskWasFound = foundTask != null;
+ splitSelectSource.alreadyRunningTaskId = taskWasFound
+ ? foundTask.key.id
+ : INVALID_TASK_ID;
if (ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
startSplitToHome(splitSelectSource);
} else {
@@ -675,9 +686,9 @@
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
-
- if (mOverviewCommandHelper != null) {
- mOverviewCommandHelper.clearPendingCommands();
+ OverviewCommandHelper overviewCommandHelper = mTISBindHelper.getOverviewCommandHelper();
+ if (overviewCommandHelper != null) {
+ overviewCommandHelper.clearPendingCommands();
}
}
@@ -800,8 +811,9 @@
}
private void onTaskbarInAppDisplayProgressUpdate(float progress, int flag) {
- if (mTaskbarManager == null
- || mTaskbarManager.getCurrentActivityContext() == null
+ TaskbarManager taskbarManager = mTISBindHelper.getTaskbarManager();
+ if (taskbarManager == null
+ || taskbarManager.getCurrentActivityContext() == null
|| mTaskbarUIController == null) {
return;
}
@@ -873,11 +885,10 @@
}
private void onTISConnected(TISBinder binder) {
- mTaskbarManager = binder.getTaskbarManager();
- if (mTaskbarManager != null) {
- mTaskbarManager.setActivity(this);
+ TaskbarManager taskbarManager = mTISBindHelper.getTaskbarManager();
+ if (taskbarManager != null) {
+ taskbarManager.setActivity(this);
}
- mOverviewCommandHelper = binder.getOverviewCommandHelper();
}
@Override
@@ -1153,7 +1164,6 @@
}
switch (info.itemType) {
case Favorites.ITEM_TYPE_APPLICATION:
- case Favorites.ITEM_TYPE_SHORTCUT:
case Favorites.ITEM_TYPE_DEEP_SHORTCUT:
case Favorites.ITEM_TYPE_APPWIDGET:
// Fall through and continue if it's an app, shortcut, or widget
@@ -1262,8 +1272,9 @@
Trace.instantForTrack(TRACE_TAG_APP, "QuickstepLauncher#DeviceProfileChanged",
getDeviceProfile().toSmallString());
SystemUiProxy.INSTANCE.get(this).setLauncherAppIconSize(mDeviceProfile.iconSizePx);
- if (mTaskbarManager != null) {
- mTaskbarManager.debugWhyTaskbarNotDestroyed("QuickstepLauncher#onDeviceProfileChanged");
+ TaskbarManager taskbarManager = mTISBindHelper.getTaskbarManager();
+ if (taskbarManager != null) {
+ taskbarManager.debugWhyTaskbarNotDestroyed("QuickstepLauncher#onDeviceProfileChanged");
}
}
@@ -1285,7 +1296,7 @@
groupTask.task1.key.id,
groupTask.task2.key.id,
SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT,
- /* callback= */ success -> {},
+ /* callback= */ success -> mSplitSelectStateController.resetState(),
/* freezeTaskList= */ true,
groupTask.mSplitBounds == null
? DEFAULT_SPLIT_RATIO
@@ -1294,6 +1305,13 @@
: groupTask.mSplitBounds.leftTaskPercent);
}
+ @Override
+ public boolean isCommandQueueEmpty() {
+ OverviewCommandHelper overviewCommandHelper = mTISBindHelper.getOverviewCommandHelper();
+ return super.isCommandQueueEmpty()
+ && (overviewCommandHelper == null || overviewCommandHelper.isCommandQueueEmpty());
+ }
+
private static final class LauncherTaskViewController extends
TaskViewTouchController<Launcher> {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java
index 39543b0..f7bef03 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java
@@ -243,6 +243,7 @@
} else {
widgetView = new LauncherAppWidgetHostView(context);
}
+ widgetView.setIsWidgetCachingDisabled(true);
widgetView.setInteractionHandler(mInteractionHandler);
widgetView.setAppWidget(appWidgetId, appWidget);
mViews.put(appWidgetId, widgetView);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index f16b43d..23e922c 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -15,10 +15,10 @@
*/
package com.android.launcher3.uioverrides;
+import static com.android.app.animation.Interpolators.LINEAR;
import static com.android.launcher3.LauncherState.CLEAR_ALL_BUTTON;
import static com.android.launcher3.LauncherState.OVERVIEW_ACTIONS;
import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_ACTIONS_FADE;
import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/flags/DeveloperOptionsFragment.java b/quickstep/src/com/android/launcher3/uioverrides/flags/DeveloperOptionsFragment.java
index b901a87..a76eb43 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/flags/DeveloperOptionsFragment.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/flags/DeveloperOptionsFragment.java
@@ -23,6 +23,7 @@
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
+import static com.android.launcher3.LauncherPrefs.ALL_APPS_OVERVIEW_THRESHOLD;
import static com.android.launcher3.settings.SettingsActivity.EXTRA_FRAGMENT_ARG_KEY;
import static com.android.launcher3.uioverrides.plugins.PluginManagerWrapper.PLUGIN_CHANGED;
import static com.android.launcher3.uioverrides.plugins.PluginManagerWrapper.pluginEnabledKey;
@@ -59,6 +60,7 @@
import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceScreen;
import androidx.preference.PreferenceViewHolder;
+import androidx.preference.SeekBarPreference;
import androidx.preference.SwitchPreference;
import com.android.launcher3.LauncherPrefs;
@@ -106,6 +108,9 @@
loadPluginPrefs();
maybeAddSandboxCategory();
addOnboardingPrefsCatergory();
+ if (FeatureFlags.ENABLE_ALL_APPS_FROM_OVERVIEW.get()) {
+ addAllAppsFromOverviewCatergory();
+ }
if (getActivity() != null) {
getActivity().setTitle("Developer Options");
@@ -393,6 +398,33 @@
}
}
+ private void addAllAppsFromOverviewCatergory() {
+ PreferenceCategory category = newCategory("All Apps from Overview Config");
+
+ SeekBarPreference thresholdPref = new SeekBarPreference(getContext());
+ thresholdPref.setTitle("Threshold to open All Apps from Overview");
+ thresholdPref.setSingleLineTitle(false);
+
+ // These values are 100x swipe up shift value (100 = where overview sits).
+ thresholdPref.setMax(500);
+ thresholdPref.setMin(105);
+ thresholdPref.setUpdatesContinuously(true);
+ thresholdPref.setIconSpaceReserved(false);
+ // Don't directly save to shared prefs, use LauncherPrefs instead.
+ thresholdPref.setPersistent(false);
+ thresholdPref.setOnPreferenceChangeListener((preference, newValue) -> {
+ LauncherPrefs.get(getContext()).put(ALL_APPS_OVERVIEW_THRESHOLD, newValue);
+ preference.setSummary(String.valueOf((int) newValue / 100f));
+ return true;
+ });
+ int value = LauncherPrefs.get(getContext()).get(ALL_APPS_OVERVIEW_THRESHOLD);
+ thresholdPref.setValue(value);
+ // For some reason the initial value is not triggering the summary update, so call manually.
+ thresholdPref.getOnPreferenceChangeListener().onPreferenceChange(thresholdPref, value);
+
+ category.addPreference(thresholdPref);
+ }
+
private String toName(String action) {
String str = action.replace("com.android.systemui.action.PLUGIN_", "")
.replace("com.android.launcher3.action.PLUGIN_", "");
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
index 2a42175..ed0a0d5 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -15,14 +15,16 @@
*/
package com.android.launcher3.uioverrides.states;
-import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
+import static com.android.app.animation.Interpolators.DECELERATE_2;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_ALLAPPS;
import android.content.Context;
+import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ActivityContext;
@@ -91,7 +93,7 @@
@Override
public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) {
PageAlphaProvider superPageAlphaProvider = super.getWorkspacePageAlphaProvider(launcher);
- return new PageAlphaProvider(DEACCEL_2) {
+ return new PageAlphaProvider(DECELERATE_2) {
@Override
public float getPageAlpha(int pageIndex) {
return launcher.getDeviceProfile().isTablet
@@ -103,14 +105,52 @@
@Override
public int getVisibleElements(Launcher launcher) {
- // Don't add HOTSEAT_ICONS for non-tablets in ALL_APPS state.
- return launcher.getDeviceProfile().isTablet ? ALL_APPS_CONTENT | HOTSEAT_ICONS
- : ALL_APPS_CONTENT;
+ int elements = ALL_APPS_CONTENT | FLOATING_SEARCH_BAR;
+ // Only add HOTSEAT_ICONS for tablets in ALL_APPS state.
+ if (launcher.getDeviceProfile().isTablet) {
+ elements |= HOTSEAT_ICONS;
+ }
+ return elements;
+ }
+
+ @Override
+ public int getFloatingSearchBarRestingMarginBottom(Launcher launcher) {
+ return 0;
+ }
+
+ @Override
+ public int getFloatingSearchBarRestingMarginStart(Launcher launcher) {
+ DeviceProfile dp = launcher.getDeviceProfile();
+ return dp.allAppsLeftRightMargin + dp.getAllAppsIconStartMargin();
+ }
+
+ @Override
+ public int getFloatingSearchBarRestingMarginEnd(Launcher launcher) {
+ DeviceProfile dp = launcher.getDeviceProfile();
+ return dp.allAppsLeftRightMargin + dp.getAllAppsIconStartMargin();
+ }
+
+ @Override
+ public boolean shouldFloatingSearchBarUsePillWhenUnfocused(Launcher launcher) {
+ DeviceProfile dp = launcher.getDeviceProfile();
+ return dp.isPhone && !dp.isLandscape;
}
@Override
public LauncherState getHistoryForState(LauncherState previousState) {
- return previousState == OVERVIEW ? OVERVIEW : NORMAL;
+ return previousState == BACKGROUND_APP ? QUICK_SWITCH_FROM_HOME
+ : previousState == OVERVIEW ? OVERVIEW : NORMAL;
+ }
+
+ @Override
+ public float[] getOverviewScaleAndOffset(Launcher launcher) {
+ if (!FeatureFlags.ENABLE_ALL_APPS_FROM_OVERVIEW.get()) {
+ return super.getOverviewScaleAndOffset(launcher);
+ }
+ // This handles the case of returning to the previous app from Overview -> All Apps gesture.
+ // This is the start scale/offset of overview that will be used for that transition.
+ // TODO (b/283336332): Translate in Y direction (ideally with overview resistance).
+ return new float[] {0.5f /* scale */, NO_OFFSET};
}
@Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
index 214679a..396d0ab 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -15,7 +15,7 @@
*/
package com.android.launcher3.uioverrides.states;
-import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
+import static com.android.app.animation.Interpolators.DECELERATE_2;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
import android.content.Context;
@@ -97,7 +97,7 @@
@Override
public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) {
- return new PageAlphaProvider(DEACCEL_2) {
+ return new PageAlphaProvider(DECELERATE_2) {
@Override
public float getPageAlpha(int pageIndex) {
return 0;
@@ -107,7 +107,32 @@
@Override
public int getVisibleElements(Launcher launcher) {
- return CLEAR_ALL_BUTTON | OVERVIEW_ACTIONS;
+ int elements = CLEAR_ALL_BUTTON | OVERVIEW_ACTIONS;
+ DeviceProfile dp = launcher.getDeviceProfile();
+ boolean showFloatingSearch;
+ if (dp.isPhone) {
+ // Only show search in phone overview in portrait mode.
+ showFloatingSearch = !dp.isLandscape;
+ } else {
+ // Only show search in tablet overview if taskbar is not visible.
+ showFloatingSearch = !dp.isTaskbarPresent || isTaskbarStashed(launcher);
+ }
+ if (showFloatingSearch) {
+ elements |= FLOATING_SEARCH_BAR;
+ }
+ return elements;
+ }
+
+ @Override
+ public int getFloatingSearchBarRestingMarginBottom(Launcher launcher) {
+ return areElementsVisible(launcher, FLOATING_SEARCH_BAR) ? 0
+ : super.getFloatingSearchBarRestingMarginBottom(launcher);
+ }
+
+ @Override
+ public boolean shouldFloatingSearchBarUsePillWhenUnfocused(Launcher launcher) {
+ DeviceProfile dp = launcher.getDeviceProfile();
+ return dp.isPhone && !dp.isLandscape;
}
@Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
index a8d7538..41bcb79 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
@@ -17,6 +17,20 @@
import static android.view.View.VISIBLE;
+import static com.android.app.animation.Interpolators.ACCELERATE;
+import static com.android.app.animation.Interpolators.ACCELERATE_DECELERATE;
+import static com.android.app.animation.Interpolators.DECELERATE;
+import static com.android.app.animation.Interpolators.DECELERATE_1_7;
+import static com.android.app.animation.Interpolators.DECELERATE_3;
+import static com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE;
+import static com.android.app.animation.Interpolators.EMPHASIZED_DECELERATE;
+import static com.android.app.animation.Interpolators.FAST_OUT_SLOW_IN;
+import static com.android.app.animation.Interpolators.FINAL_FRAME;
+import static com.android.app.animation.Interpolators.INSTANT;
+import static com.android.app.animation.Interpolators.LINEAR;
+import static com.android.app.animation.Interpolators.OVERSHOOT_0_75;
+import static com.android.app.animation.Interpolators.OVERSHOOT_1_2;
+import static com.android.app.animation.Interpolators.clampToProgress;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.HINT_STATE;
import static com.android.launcher3.LauncherState.HINT_STATE_TWO_BUTTON;
@@ -25,20 +39,6 @@
import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
import static com.android.launcher3.QuickstepTransitionManager.TASKBAR_TO_HOME_DURATION;
import static com.android.launcher3.WorkspaceStateTransitionAnimation.getWorkspaceSpringScaleAnimator;
-import static com.android.launcher3.anim.Interpolators.ACCEL;
-import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
-import static com.android.launcher3.anim.Interpolators.DEACCEL;
-import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7;
-import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
-import static com.android.launcher3.anim.Interpolators.EMPHASIZED_ACCELERATE;
-import static com.android.launcher3.anim.Interpolators.EMPHASIZED_DECELERATE;
-import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
-import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
-import static com.android.launcher3.anim.Interpolators.INSTANT;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.anim.Interpolators.OVERSHOOT_0_75;
-import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
-import static com.android.launcher3.anim.Interpolators.clampToProgress;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_DEPTH;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_ACTIONS_FADE;
@@ -112,8 +112,8 @@
fromState == OVERVIEW_SPLIT_SELECT
? clampToProgress(LINEAR, 0.33f, 1)
: LINEAR);
- config.setInterpolator(ANIM_WORKSPACE_SCALE, DEACCEL);
- config.setInterpolator(ANIM_WORKSPACE_FADE, ACCEL);
+ config.setInterpolator(ANIM_WORKSPACE_SCALE, DECELERATE);
+ config.setInterpolator(ANIM_WORKSPACE_FADE, ACCELERATE);
if (DisplayController.getNavigationMode(mActivity).hasGestures
&& overview.getTaskViewCount() > 0) {
@@ -139,9 +139,9 @@
}
overview.snapToPage(DEFAULT_PAGE, Math.toIntExact(config.duration));
} else {
- config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, ACCEL_DEACCEL);
- config.setInterpolator(ANIM_OVERVIEW_SCALE, clampToProgress(ACCEL, 0, 0.9f));
- config.setInterpolator(ANIM_OVERVIEW_FADE, DEACCEL_1_7);
+ config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, ACCELERATE_DECELERATE);
+ config.setInterpolator(ANIM_OVERVIEW_SCALE, clampToProgress(ACCELERATE, 0, 0.9f));
+ config.setInterpolator(ANIM_OVERVIEW_FADE, DECELERATE_1_7);
}
Workspace<?> workspace = mActivity.getWorkspace();
@@ -167,8 +167,8 @@
|| fromState == HINT_STATE_TWO_BUTTON) && toState == OVERVIEW) {
if (DisplayController.getNavigationMode(mActivity).hasGestures) {
config.setInterpolator(ANIM_WORKSPACE_SCALE,
- fromState == NORMAL ? ACCEL : OVERSHOOT_1_2);
- config.setInterpolator(ANIM_WORKSPACE_TRANSLATE, ACCEL);
+ fromState == NORMAL ? ACCELERATE : OVERSHOOT_1_2);
+ config.setInterpolator(ANIM_WORKSPACE_TRANSLATE, ACCELERATE);
// Scrolling in tasks, so show straight away
if (overview.getTaskViewCount() > 0) {
@@ -196,7 +196,7 @@
config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, OVERSHOOT_1_2);
config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, OVERSHOOT_1_2);
} else if (fromState == HINT_STATE && toState == NORMAL) {
- config.setInterpolator(ANIM_DEPTH, DEACCEL_3);
+ config.setInterpolator(ANIM_DEPTH, DECELERATE_3);
if (mHintToNormalDuration == -1) {
ValueAnimator va = getWorkspaceSpringScaleAnimator(mActivity,
mActivity.getWorkspace(),
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
index 8cbd6e8..be53220 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
@@ -15,17 +15,19 @@
*/
package com.android.launcher3.uioverrides.touchcontrollers;
+import static com.android.app.animation.Interpolators.DECELERATE_3;
import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
import static com.android.launcher3.AbstractFloatingView.TYPE_ALL_APPS_EDU;
import static com.android.launcher3.LauncherAnimUtils.SUCCESS_TRANSITION_PROGRESS;
import static com.android.launcher3.LauncherAnimUtils.newCancelListener;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.MotionEventsUtils.isTrackpadMotionEvent;
import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PULL_BACK_ALPHA;
import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PULL_BACK_TRANSLATION;
import static com.android.launcher3.anim.AnimatorListeners.forSuccessCallback;
-import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_GESTURE;
+import static com.android.launcher3.util.NavigationMode.THREE_BUTTONS;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
import android.animation.ValueAnimator;
@@ -42,6 +44,7 @@
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.touch.SingleAxisSwipeDetector;
+import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.TouchController;
import com.android.quickstep.TaskUtils;
import com.android.quickstep.util.AnimatorControllerWithResistance;
@@ -54,7 +57,7 @@
public class NavBarToHomeTouchController implements TouchController,
SingleAxisSwipeDetector.Listener {
- private static final Interpolator PULLBACK_INTERPOLATOR = DEACCEL_3;
+ private static final Interpolator PULLBACK_INTERPOLATOR = DECELERATE_3;
// The min amount of overview scrim we keep during the transition.
private static final float OVERVIEW_TO_HOME_SCRIM_MULTIPLIER = 0.5f;
@@ -95,6 +98,10 @@
}
private boolean canInterceptTouch(MotionEvent ev) {
+ if (!isTrackpadMotionEvent(ev) && DisplayController.getNavigationMode(mLauncher)
+ == THREE_BUTTONS) {
+ return false;
+ }
boolean cameFromNavBar = (ev.getEdgeFlags() & Utilities.EDGE_NAV_BAR) != 0;
if (!cameFromNavBar) {
return false;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
index b7bafd8..2f5467e 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
@@ -16,15 +16,17 @@
package com.android.launcher3.uioverrides.touchcontrollers;
+import static com.android.app.animation.Interpolators.ACCELERATE_DECELERATE;
import static com.android.launcher3.LauncherAnimUtils.VIEW_BACKGROUND_COLOR;
import static com.android.launcher3.LauncherAnimUtils.newCancelListener;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.HINT_STATE;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.MotionEventsUtils.isTrackpadMotionEvent;
import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
import static com.android.launcher3.anim.AnimatorListeners.forSuccessCallback;
-import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
+import static com.android.launcher3.util.NavigationMode.THREE_BUTTONS;
import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ONE_HANDED_ACTIVE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
@@ -43,6 +45,7 @@
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.taskbar.LauncherTaskbarUIController;
import com.android.launcher3.uioverrides.QuickstepLauncher;
+import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.VibratorWrapper;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.util.AnimatorControllerWithResistance;
@@ -89,6 +92,10 @@
@Override
protected boolean canInterceptTouch(MotionEvent ev) {
+ if (!isTrackpadMotionEvent(ev) && DisplayController.getNavigationMode(mLauncher)
+ == THREE_BUTTONS) {
+ return false;
+ }
mDidTouchStartInNavBar = (ev.getEdgeFlags() & EDGE_NAV_BAR) != 0;
boolean isOneHandedModeActive = (SystemUiProxy.INSTANCE.get(mLauncher)
.getLastSystemUiStateFlags() & SYSUI_STATE_ONE_HANDED_ACTIVE) != 0;
@@ -273,7 +280,7 @@
mRecentsView.animate()
.translationX(0)
.translationY(0)
- .setInterpolator(ACCEL_DEACCEL)
+ .setInterpolator(ACCELERATE_DECELERATE)
.setDuration(duration)
.withEndAction(goToHomeInsteadOfOverview
? null
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
index 80f5558..d3ef589 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
@@ -18,19 +18,20 @@
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_MOVE;
+import static com.android.app.animation.Interpolators.ACCELERATE_0_75;
+import static com.android.app.animation.Interpolators.DECELERATE_3;
+import static com.android.app.animation.Interpolators.LINEAR;
+import static com.android.app.animation.Interpolators.scrollInterpolatorForVelocity;
import static com.android.launcher3.LauncherAnimUtils.newCancelListener;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.LauncherState.OVERVIEW_ACTIONS;
import static com.android.launcher3.LauncherState.QUICK_SWITCH_FROM_HOME;
import static com.android.launcher3.MotionEventsUtils.isTrackpadFourFingerSwipe;
+import static com.android.launcher3.MotionEventsUtils.isTrackpadMotionEvent;
import static com.android.launcher3.MotionEventsUtils.isTrackpadMultiFingerSwipe;
import static com.android.launcher3.anim.AlphaUpdateListener.ALPHA_CUTOFF_THRESHOLD;
import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
-import static com.android.launcher3.anim.Interpolators.ACCEL_0_75;
-import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_QUICKSWITCH_RIGHT;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_UNKNOWN_SWIPEDOWN;
@@ -46,6 +47,7 @@
import static com.android.launcher3.states.StateAnimationConfig.SKIP_SCRIM;
import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_RIGHT;
import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_UP;
+import static com.android.launcher3.util.NavigationMode.THREE_BUTTONS;
import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
@@ -74,6 +76,7 @@
import com.android.launcher3.touch.BaseSwipeDetector;
import com.android.launcher3.touch.BothAxesSwipeDetector;
import com.android.launcher3.uioverrides.QuickstepLauncher;
+import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.TouchController;
import com.android.launcher3.util.VibratorWrapper;
import com.android.quickstep.SystemUiProxy;
@@ -93,8 +96,8 @@
BothAxesSwipeDetector.Listener {
private static final float Y_ANIM_MIN_PROGRESS = 0.25f;
- private static final Interpolator FADE_OUT_INTERPOLATOR = DEACCEL_3;
- private static final Interpolator TRANSLATE_OUT_INTERPOLATOR = ACCEL_0_75;
+ private static final Interpolator FADE_OUT_INTERPOLATOR = DECELERATE_3;
+ private static final Interpolator TRANSLATE_OUT_INTERPOLATOR = ACCELERATE_0_75;
private static final Interpolator SCALE_DOWN_INTERPOLATOR = LINEAR;
private static final long ATOMIC_DURATION_FROM_PAUSED_TO_OVERVIEW = 300;
@@ -110,7 +113,6 @@
newCancelListener(this::clearState);
private boolean mNoIntercept;
- private Boolean mIsTrackpadFourFingerSwipe;
private LauncherState mStartState;
private boolean mIsHomeScreenVisible = true;
@@ -136,9 +138,7 @@
@Override
public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
- int action = ev.getActionMasked();
- if (action == ACTION_DOWN) {
- mIsTrackpadFourFingerSwipe = null;
+ if (ev.getActionMasked() == ACTION_DOWN) {
mNoIntercept = !canInterceptTouch(ev);
if (mNoIntercept) {
return false;
@@ -147,13 +147,6 @@
// Only detect horizontal swipe for intercept, then we will allow swipe up as well.
mSwipeDetector.setDetectableScrollConditions(DIRECTION_RIGHT,
false /* ignoreSlopWhenSettling */);
- } else if (isTrackpadMultiFingerSwipe(ev) && mIsTrackpadFourFingerSwipe == null
- && action == ACTION_MOVE) {
- mIsTrackpadFourFingerSwipe = isTrackpadFourFingerSwipe(ev);
- mNoIntercept = !mIsTrackpadFourFingerSwipe;
- if (mNoIntercept) {
- return false;
- }
}
if (mNoIntercept) {
@@ -170,6 +163,10 @@
}
private boolean canInterceptTouch(MotionEvent ev) {
+ if (!isTrackpadMotionEvent(ev) && DisplayController.getNavigationMode(mLauncher)
+ == THREE_BUTTONS) {
+ return false;
+ }
if (!mLauncher.isInState(LauncherState.NORMAL)) {
return false;
}
@@ -184,6 +181,9 @@
// TODO(b/268075592): add support for quickswitch to/from desktop
return false;
}
+ if (isTrackpadMultiFingerSwipe(ev)) {
+ return isTrackpadFourFingerSwipe(ev);
+ }
return true;
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
index 8368f9c..454a1f5 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
@@ -24,11 +24,12 @@
import android.view.MotionEvent;
+import com.android.app.animation.Interpolators;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.allapps.AllAppsTransitionController;
-import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.touch.AbstractStateChangeTouchController;
import com.android.launcher3.touch.AllAppsSwipeController;
@@ -92,7 +93,9 @@
@Override
protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) {
if (fromState == ALL_APPS && !isDragTowardPositive) {
- return NORMAL;
+ return FeatureFlags.ENABLE_ALL_APPS_FROM_OVERVIEW.get()
+ ? mLauncher.getStateManager().getLastState()
+ : NORMAL;
} else if (fromState == OVERVIEW) {
return isDragTowardPositive ? OVERVIEW : NORMAL;
} else if (fromState == NORMAL && isDragTowardPositive) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
index f941b02..9a35bb2 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
@@ -15,12 +15,12 @@
*/
package com.android.launcher3.uioverrides.touchcontrollers;
+import static com.android.app.animation.Interpolators.ACCELERATE_2;
+import static com.android.app.animation.Interpolators.DECELERATE_2;
+import static com.android.app.animation.Interpolators.INSTANT;
+import static com.android.app.animation.Interpolators.LINEAR;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.QUICK_SWITCH_FROM_HOME;
-import static com.android.launcher3.anim.Interpolators.ACCEL_2;
-import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
-import static com.android.launcher3.anim.Interpolators.INSTANT;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE;
@@ -128,14 +128,14 @@
}
private void setupInterpolators(StateAnimationConfig stateAnimationConfig) {
- stateAnimationConfig.setInterpolator(ANIM_WORKSPACE_FADE, DEACCEL_2);
- stateAnimationConfig.setInterpolator(ANIM_ALL_APPS_FADE, DEACCEL_2);
+ stateAnimationConfig.setInterpolator(ANIM_WORKSPACE_FADE, DECELERATE_2);
+ stateAnimationConfig.setInterpolator(ANIM_ALL_APPS_FADE, DECELERATE_2);
if (DisplayController.getNavigationMode(mLauncher) == NavigationMode.NO_BUTTON) {
// Overview lives to the left of workspace, so translate down later than over
- stateAnimationConfig.setInterpolator(ANIM_WORKSPACE_TRANSLATE, ACCEL_2);
- stateAnimationConfig.setInterpolator(ANIM_VERTICAL_PROGRESS, ACCEL_2);
- stateAnimationConfig.setInterpolator(ANIM_OVERVIEW_SCALE, ACCEL_2);
- stateAnimationConfig.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, ACCEL_2);
+ stateAnimationConfig.setInterpolator(ANIM_WORKSPACE_TRANSLATE, ACCELERATE_2);
+ stateAnimationConfig.setInterpolator(ANIM_VERTICAL_PROGRESS, ACCELERATE_2);
+ stateAnimationConfig.setInterpolator(ANIM_OVERVIEW_SCALE, ACCELERATE_2);
+ stateAnimationConfig.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, ACCELERATE_2);
stateAnimationConfig.setInterpolator(ANIM_OVERVIEW_FADE, INSTANT);
} else {
stateAnimationConfig.setInterpolator(ANIM_WORKSPACE_TRANSLATE, LINEAR);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java
index 395833f..26ab3d6 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java
@@ -76,7 +76,7 @@
private void dispatchTouchEvent(MotionEvent ev) {
if (mSystemUiProxy.isActive()) {
mLastAction = ev.getActionMasked();
- mSystemUiProxy.onStatusBarMotionEvent(ev);
+ mSystemUiProxy.onStatusBarTouchEvent(ev);
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
index eddc50c..3d94857 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
@@ -27,13 +27,13 @@
import android.view.View;
import android.view.animation.Interpolator;
+import com.android.app.animation.Interpolators;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.LauncherAnimUtils;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.touch.BaseSwipeDetector;
import com.android.launcher3.touch.PagedOrientationHandler;
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 43db7c3..95672f3 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -21,12 +21,13 @@
import static android.view.Surface.ROTATION_90;
import static android.widget.Toast.LENGTH_SHORT;
+import static com.android.app.animation.Interpolators.ACCELERATE_DECELERATE;
+import static com.android.app.animation.Interpolators.DECELERATE;
+import static com.android.app.animation.Interpolators.OVERSHOOT_1_2;
import static com.android.launcher3.BaseActivity.INVISIBLE_BY_STATE_HANDLER;
import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS;
+import static com.android.launcher3.LauncherPrefs.ALL_APPS_OVERVIEW_THRESHOLD;
import static com.android.launcher3.PagedView.INVALID_PAGE;
-import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
-import static com.android.launcher3.anim.Interpolators.DEACCEL;
-import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.IGNORE;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_GESTURE;
@@ -94,6 +95,7 @@
import com.android.internal.util.LatencyTracker;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimationSuccessListener;
@@ -101,6 +103,7 @@
import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.logging.StatsLogManager.StatsLogger;
+import com.android.launcher3.statehandlers.DesktopVisibilityController;
import com.android.launcher3.statemanager.BaseState;
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.taskbar.TaskbarUIController;
@@ -109,6 +112,7 @@
import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.launcher3.util.ActivityLifecycleCallbacksAdapter;
import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.util.TraceHelper;
import com.android.launcher3.util.VibratorWrapper;
import com.android.launcher3.util.WindowBounds;
@@ -163,9 +167,6 @@
private static final ArrayList<String> STATE_NAMES = new ArrayList<>();
- /** Shift distance to transition to All Apps if ENABLE_ALL_APPS_FROM_OVERVIEW. */
- public static final float ALL_APPS_SHIFT_THRESHOLD = 2f;
-
protected final BaseActivityInterface<S, T> mActivityInterface;
protected final InputConsumerProxy mInputConsumerProxy;
protected final ActivityInitListener mActivityInitListener;
@@ -293,7 +294,8 @@
private boolean mContinuingLastGesture;
- private ThumbnailData mTaskSnapshot;
+ // Cache of recently-updated task snapshots, mapping task id to ThumbnailData
+ private HashMap<Integer, ThumbnailData> mTaskSnapshotCache = new HashMap<>();
// Used to control launcher components throughout the swipe gesture.
private AnimatorControllerWithResistance mLauncherTransitionController;
@@ -587,7 +589,7 @@
if (mWasLauncherAlreadyVisible) {
mStateCallback.setState(STATE_LAUNCHER_DRAWN);
} else {
- Object traceToken = TraceHelper.INSTANCE.beginSection("WTS-init");
+ SafeCloseable traceToken = TraceHelper.INSTANCE.beginAsyncSection("WTS-init");
View dragLayer = activity.getDragLayer();
dragLayer.getViewTreeObserver().addOnDrawListener(new OnDrawListener() {
boolean mHandled = false;
@@ -599,7 +601,7 @@
}
mHandled = true;
- TraceHelper.INSTANCE.endSection(traceToken);
+ traceToken.close();
dragLayer.post(() ->
dragLayer.getViewTreeObserver().removeOnDrawListener(this));
if (activity != mActivity) {
@@ -681,11 +683,10 @@
private void initializeLauncherAnimationController() {
buildAnimationController();
- Object traceToken = TraceHelper.INSTANCE.beginSection("logToggleRecents",
- TraceHelper.FLAG_IGNORE_BINDERS);
- LatencyTracker.getInstance(mContext).logAction(LatencyTracker.ACTION_TOGGLE_RECENTS,
- (int) (mLauncherFrameDrawnTime - mTouchTimeMs));
- TraceHelper.INSTANCE.endSection(traceToken);
+ try (SafeCloseable c = TraceHelper.INSTANCE.allowIpcs("logToggleRecents")) {
+ LatencyTracker.getInstance(mContext).logAction(LatencyTracker.ACTION_TOGGLE_RECENTS,
+ (int) (mLauncherFrameDrawnTime - mTouchTimeMs));
+ }
// This method is only called when STATE_GESTURE_STARTED is set, so we can enable the
// high-res thumbnail loader here once we are sure that we will end up in an overview state
@@ -731,7 +732,8 @@
* @param moveRunningTask whether to move running task to front when attaching
*/
private void maybeUpdateRecentsAttachedState(boolean animate, boolean moveRunningTask) {
- if (!mDeviceState.isFullyGesturalNavMode() || mRecentsView == null) {
+ if ((!mDeviceState.isFullyGesturalNavMode() && !mGestureState.isTrackpadGesture())
+ || mRecentsView == null) {
return;
}
RemoteAnimationTarget runningTaskTarget = mRecentsAnimationTargets != null
@@ -810,6 +812,10 @@
VibratorWrapper.INSTANCE.get(mContext).vibrate(OVERVIEW_HAPTIC);
maybeUpdateRecentsAttachedState(true);
+ if (mActivity != null) {
+ mActivity.getAppsView().getSearchUiManager().prepareToFocusEditText(mIsInAllAppsRegion);
+ }
+
// Draw active task below Launcher so that All Apps can appear over it.
runActionOnRemoteHandles(remoteTargetHandle ->
remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(isInAllAppsRegion));
@@ -873,7 +879,8 @@
@UiThread
@Override
public void onCurrentShiftUpdated() {
- setIsInAllAppsRegion(mCurrentShift.value >= ALL_APPS_SHIFT_THRESHOLD);
+ float threshold = LauncherPrefs.get(mContext).get(ALL_APPS_OVERVIEW_THRESHOLD) / 100f;
+ setIsInAllAppsRegion(mCurrentShift.value >= threshold);
updateSysUiFlags(mCurrentShift.value);
applyScrollAndTransform();
@@ -1148,6 +1155,14 @@
mStateCallback.setState(STATE_SCALED_CONTROLLER_HOME | STATE_CAPTURE_SCREENSHOT);
// Notify the SysUI to use fade-in animation when entering PiP
SystemUiProxy.INSTANCE.get(mContext).setPipAnimationTypeToAlpha();
+ if (DesktopTaskView.DESKTOP_IS_PROTO2_ENABLED) {
+ // Notify the SysUI to stash desktop apps if they are visible
+ DesktopVisibilityController desktopVisibilityController =
+ mActivityInterface.getDesktopVisibilityController();
+ if (desktopVisibilityController != null) {
+ desktopVisibilityController.onHomeActionTriggered();
+ }
+ }
break;
case RECENTS:
mStateCallback.setState(STATE_SCALED_CONTROLLER_RECENTS | STATE_CAPTURE_SCREENSHOT
@@ -1324,11 +1339,11 @@
Interpolator interpolator;
S state = mActivityInterface.stateFromGestureEndTarget(endTarget);
if (state.displayOverviewTasksAsGrid(mDp)) {
- interpolator = ACCEL_DEACCEL;
+ interpolator = ACCELERATE_DECELERATE;
} else if (endTarget == RECENTS) {
interpolator = OVERSHOOT_1_2;
} else {
- interpolator = DEACCEL;
+ interpolator = DECELERATE;
}
if (endTarget.isLauncher) {
@@ -1518,8 +1533,9 @@
// grab a screenshot before the PipContentOverlay gets parented on top of the task
UI_HELPER_EXECUTOR.execute(() -> {
- mTaskSnapshot = mRecentsAnimationController.screenshotTask(
- mGestureState.getRunningTaskId());
+ final int taskId = mGestureState.getRunningTaskId();
+ mTaskSnapshotCache.put(taskId,
+ mRecentsAnimationController.screenshotTask(taskId));
});
// let SystemUi reparent the overlay leash as soon as possible
@@ -1908,7 +1924,7 @@
mActivityInitListener.unregister();
TaskStackChangeListeners.getInstance().unregisterTaskStackListener(
mActivityRestartListener);
- mTaskSnapshot = null;
+ mTaskSnapshotCache.clear();
}
private void invalidateHandler() {
@@ -1926,7 +1942,7 @@
mActivityInitListener.unregister();
TaskStackChangeListeners.getInstance().unregisterTaskStackListener(
mActivityRestartListener);
- mTaskSnapshot = null;
+ mTaskSnapshotCache.clear();
}
private void invalidateHandlerWithLauncher() {
@@ -1983,35 +1999,40 @@
} else {
final int runningTaskId = mGestureState.getRunningTaskId();
boolean finishTransitionPosted = false;
+ // If we already have cached screenshot(s) from running tasks, skip update
+ boolean shouldUpdate = false;
+ int[] runningTaskIds = mIsSwipeForSplit
+ ? TopTaskTracker.INSTANCE.get(mContext).getRunningSplitTaskIds()
+ : new int[]{runningTaskId};
+ for (int id : runningTaskIds) {
+ if (!mTaskSnapshotCache.containsKey(id)) {
+ shouldUpdate = true;
+ break;
+ }
+ }
+
if (mRecentsAnimationController != null) {
// Update the screenshot of the task
- if (mTaskSnapshot == null) {
+ if (shouldUpdate) {
UI_HELPER_EXECUTOR.execute(() -> {
if (mRecentsAnimationController == null) return;
- final ThumbnailData taskSnapshot =
- mRecentsAnimationController.screenshotTask(runningTaskId);
- // If split case, we should update all split tasks snapshot
- if (mIsSwipeForSplit) {
- int[] splitTaskIds = TopTaskTracker.INSTANCE.get(
- mContext).getRunningSplitTaskIds();
- for (int i = 0; i < splitTaskIds.length; i++) {
- // Skip running one because done above.
- if (splitTaskIds[i] == runningTaskId) continue;
-
- mRecentsAnimationController.screenshotTask(splitTaskIds[i]);
- }
+ for (int id : runningTaskIds) {
+ mTaskSnapshotCache.put(
+ id, mRecentsAnimationController.screenshotTask(id));
}
+
MAIN_EXECUTOR.execute(() -> {
- mTaskSnapshot = taskSnapshot;
- if (!updateThumbnail(runningTaskId, false /* refreshView */)) {
+ if (!updateThumbnail(false /* refreshView */)) {
setScreenshotCapturedState();
}
});
});
return;
}
- finishTransitionPosted = updateThumbnail(runningTaskId, false /* refreshView */);
+
+ finishTransitionPosted = updateThumbnail(false /* refreshView */);
}
+
if (!finishTransitionPosted) {
setScreenshotCapturedState();
}
@@ -2019,35 +2040,34 @@
}
// Returns whether finish transition was posted.
- private boolean updateThumbnail(int runningTaskId, boolean refreshView) {
- boolean finishTransitionPosted = false;
- final TaskView taskView;
+ private boolean updateThumbnail(boolean refreshView) {
if (mGestureState.getEndTarget() == HOME
|| mGestureState.getEndTarget() == NEW_TASK
|| mGestureState.getEndTarget() == ALL_APPS
|| mRecentsView == null) {
// Capture the screenshot before finishing the transition to home or quickswitching to
// ensure it's taken in the correct orientation, but no need to update the thumbnail.
- taskView = null;
- } else {
- taskView = mRecentsView.updateThumbnail(runningTaskId, mTaskSnapshot, refreshView);
+ return false;
}
- if (taskView != null && refreshView && !mCanceled) {
+
+ boolean finishTransitionPosted = false;
+ TaskView updatedTaskView = mRecentsView.updateThumbnail(mTaskSnapshotCache, refreshView);
+ if (updatedTaskView != null && refreshView && !mCanceled) {
// Defer finishing the animation until the next launcher frame with the
// new thumbnail
- finishTransitionPosted = ViewUtils.postFrameDrawn(taskView,
+ finishTransitionPosted = ViewUtils.postFrameDrawn(updatedTaskView,
() -> mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED),
this::isCanceled);
}
+
return finishTransitionPosted;
}
private void setScreenshotCapturedState() {
// If we haven't posted a draw callback, set the state immediately.
- Object traceToken = TraceHelper.INSTANCE.beginSection(SCREENSHOT_CAPTURED_EVT,
- TraceHelper.FLAG_CHECK_FOR_RACE_CONDITIONS);
+ TraceHelper.INSTANCE.beginSection(SCREENSHOT_CAPTURED_EVT);
mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
- TraceHelper.INSTANCE.endSection(traceToken);
+ TraceHelper.INSTANCE.endSection();
}
private void finishCurrentTransitionToRecents() {
@@ -2428,11 +2448,11 @@
if (scrollOffset < mQuickSwitchScaleScrollThreshold) {
scaleProgress = Utilities.mapToRange(scrollOffset, 0, mQuickSwitchScaleScrollThreshold,
- 0, maxScaleProgress, ACCEL_DEACCEL);
+ 0, maxScaleProgress, ACCELERATE_DECELERATE);
} else if (scrollOffset > (maxScrollOffset - mQuickSwitchScaleScrollThreshold)) {
scaleProgress = Utilities.mapToRange(scrollOffset,
(maxScrollOffset - mQuickSwitchScaleScrollThreshold), maxScrollOffset,
- maxScaleProgress, 0, ACCEL_DEACCEL);
+ maxScaleProgress, 0, ACCELERATE_DECELERATE);
}
return scaleProgress;
@@ -2461,7 +2481,7 @@
// "Catch up" with the displacement at mTaskbarCatchUpThreshold.
if (displacement < mTaskbarCatchUpThreshold) {
return Utilities.mapToRange(displacement, mTaskbarAppWindowThreshold,
- mTaskbarCatchUpThreshold, 0, mTaskbarCatchUpThreshold, ACCEL_DEACCEL);
+ mTaskbarCatchUpThreshold, 0, mTaskbarCatchUpThreshold, ACCELERATE_DECELERATE);
}
return displacement;
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index 60083c6..5a9d80d 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -15,11 +15,11 @@
*/
package com.android.quickstep;
+import static com.android.app.animation.Interpolators.ACCELERATE_2;
+import static com.android.app.animation.Interpolators.INSTANT;
+import static com.android.app.animation.Interpolators.LINEAR;
import static com.android.launcher3.LauncherAnimUtils.VIEW_BACKGROUND_COLOR;
import static com.android.launcher3.MotionEventsUtils.isTrackpadMultiFingerSwipe;
-import static com.android.launcher3.anim.Interpolators.ACCEL_2;
-import static com.android.launcher3.anim.Interpolators.INSTANT;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.quickstep.AbsSwipeUpHandler.RECENTS_ATTACH_DURATION;
import static com.android.quickstep.GestureState.GestureEndTarget.LAST_TASK;
import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
@@ -553,7 +553,7 @@
long animationDuration = animate ? RECENTS_ATTACH_DURATION : 0;
Animator fadeAnim = mActivity.getStateManager()
.createStateElementAnimation(INDEX_RECENTS_FADE_ANIM, attached ? 1 : 0);
- fadeAnim.setInterpolator(attached ? INSTANT : ACCEL_2);
+ fadeAnim.setInterpolator(attached ? INSTANT : ACCELERATE_2);
fadeAnim.setDuration(animationDuration);
animatorSet.play(fadeAnim);
diff --git a/quickstep/src/com/android/quickstep/BinderTracker.java b/quickstep/src/com/android/quickstep/BinderTracker.java
new file mode 100644
index 0000000..a876cd8
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/BinderTracker.java
@@ -0,0 +1,187 @@
+/**
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep;
+
+import static android.os.IBinder.FLAG_ONEWAY;
+
+import android.os.Binder;
+import android.os.Binder.ProxyTransactListener;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.Trace;
+import android.util.Log;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.util.SafeCloseable;
+import com.android.launcher3.util.TraceHelper;
+
+import java.util.LinkedList;
+import java.util.Set;
+import java.util.function.Consumer;
+
+import kotlin.random.Random;
+
+/**
+ * A binder proxy transaction listener for tracking binder calls on main thread.
+ */
+public class BinderTracker {
+
+ private static final String TAG = "BinderTracker";
+
+ // Common IPCs that are ok to block the main thread.
+ private static final Set<String> sAllowedFrameworkClasses = Set.of(
+ "android.view.IWindowSession",
+ "android.os.IPowerManager",
+ "android.os.IServiceManager");
+
+ /**
+ * Starts tracking binder class and returns a {@link SafeCloseable} to end tracking
+ */
+ public static SafeCloseable startTracking(Consumer<BinderCallSite> callback) {
+ TraceHelper current = TraceHelper.INSTANCE;
+
+ TraceHelperExtension helper = new TraceHelperExtension(callback);
+ TraceHelper.INSTANCE = helper;
+ Binder.setProxyTransactListener(helper);
+
+ return () -> {
+ Binder.setProxyTransactListener(null);
+ TraceHelper.INSTANCE = current;
+ };
+ }
+
+ private static final LinkedList<String> mMainThreadTraceStack = new LinkedList<>();
+ private static final LinkedList<String> mMainThreadIgnoreIpcStack = new LinkedList<>();
+
+ private static class TraceHelperExtension extends TraceHelper implements ProxyTransactListener {
+
+ private final Consumer<BinderCallSite> mUnexpectedTransactionCallback;
+
+ TraceHelperExtension(Consumer<BinderCallSite> unexpectedTransactionCallback) {
+ mUnexpectedTransactionCallback = unexpectedTransactionCallback;
+ }
+
+ @Override
+ public void beginSection(String sectionName) {
+ if (isMainThread()) {
+ mMainThreadTraceStack.add(sectionName);
+ }
+ super.beginSection(sectionName);
+ }
+
+ @Override
+ public SafeCloseable beginAsyncSection(String sectionName) {
+ if (!isMainThread()) {
+ return super.beginAsyncSection(sectionName);
+ }
+
+ mMainThreadTraceStack.add(sectionName);
+ int cookie = Random.Default.nextInt();
+ Trace.beginAsyncSection(sectionName, cookie);
+ return () -> {
+ Trace.endAsyncSection(sectionName, cookie);
+ mMainThreadTraceStack.remove(sectionName);
+ };
+ }
+
+ @Override
+ public void endSection() {
+ super.endSection();
+ if (isMainThread()) {
+ mMainThreadTraceStack.pollLast();
+ }
+ }
+
+ @Override
+ public SafeCloseable allowIpcs(String rpcName) {
+ if (!isMainThread()) {
+ return super.allowIpcs(rpcName);
+ }
+
+ mMainThreadTraceStack.add(rpcName);
+ mMainThreadIgnoreIpcStack.add(rpcName);
+ int cookie = Random.Default.nextInt();
+ Trace.beginAsyncSection(rpcName, cookie);
+ return () -> {
+ Trace.endAsyncSection(rpcName, cookie);
+ mMainThreadTraceStack.remove(rpcName);
+ mMainThreadIgnoreIpcStack.remove(rpcName);
+ };
+ }
+
+ @Override
+ public Object onTransactStarted(IBinder binder, int transactionCode, int flags) {
+ if (!isMainThread() || (flags & FLAG_ONEWAY) == FLAG_ONEWAY) {
+ return null;
+ }
+
+ String ipcBypass = mMainThreadIgnoreIpcStack.peekLast();
+ String descriptor;
+ try {
+ descriptor = binder.getInterfaceDescriptor();
+ if (sAllowedFrameworkClasses.contains(descriptor)) {
+ return null;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error getting IPC descriptor", e);
+ descriptor = binder.getClass().getSimpleName();
+ }
+
+ if (ipcBypass == null) {
+ mUnexpectedTransactionCallback.accept(new BinderCallSite(
+ mMainThreadTraceStack.peekLast(), descriptor, transactionCode));
+ } else {
+ Log.d(TAG, "MainThread-IPC " + descriptor + " ignored due to " + ipcBypass);
+ }
+ return null;
+ }
+
+ @Override
+ public Object onTransactStarted(IBinder binder, int transactionCode) {
+ // Do nothing
+ return null;
+ }
+
+ @Override
+ public void onTransactEnded(Object session) {
+ // Do nothing
+ }
+ }
+
+ private static boolean isMainThread() {
+ return Thread.currentThread() == Looper.getMainLooper().getThread();
+ }
+
+ /**
+ * Information about a binder call
+ */
+ public static class BinderCallSite {
+
+ @Nullable
+ public final String activeTrace;
+ public final String descriptor;
+ public final int transactionCode;
+
+ BinderCallSite(String activeTrace, String descriptor, int transactionCode) {
+ this.activeTrace = activeTrace;
+ this.descriptor = descriptor;
+ this.transactionCode = transactionCode;
+ }
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
index 1913091..ab37493 100644
--- a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -19,13 +19,13 @@
import static android.content.Intent.EXTRA_COMPONENT_NAME;
import static android.content.Intent.EXTRA_USER;
+import static com.android.app.animation.Interpolators.ACCELERATE;
import static com.android.launcher3.GestureNavContract.EXTRA_GESTURE_CONTRACT;
import static com.android.launcher3.GestureNavContract.EXTRA_ICON_POSITION;
import static com.android.launcher3.GestureNavContract.EXTRA_ICON_SURFACE;
import static com.android.launcher3.GestureNavContract.EXTRA_ON_FINISH_CALLBACK;
import static com.android.launcher3.GestureNavContract.EXTRA_REMOTE_CALLBACK;
import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
-import static com.android.launcher3.anim.Interpolators.ACCEL;
import static com.android.quickstep.OverviewComponentObserver.startHomeIntentSafely;
import android.animation.ObjectAnimator;
@@ -295,7 +295,7 @@
@Override
public AnimatorPlaybackController createActivityAnimationToHome() {
PendingAnimation pa = new PendingAnimation(mDuration);
- pa.setFloat(mRecentsAlpha, AnimatedFloat.VALUE, 0, ACCEL);
+ pa.setFloat(mRecentsAlpha, AnimatedFloat.VALUE, 0, ACCELERATE);
return pa.createPlaybackController();
}
@@ -323,7 +323,7 @@
@Override
public void playAtomicAnimation(float velocity) {
ObjectAnimator alphaAnim = mHomeAlpha.animateToValue(mHomeAlpha.value, 1);
- alphaAnim.setDuration(mDuration).setInterpolator(ACCEL);
+ alphaAnim.setDuration(mDuration).setInterpolator(ACCELERATE);
alphaAnim.start();
if (mRunningOverHome) {
diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
index 9d7ccb4..3d0f6d5 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -150,15 +150,10 @@
public enum TrackpadGestureType {
NONE,
- // Assigned before we know whether it's a 3-finger or 4-finger gesture.
- MULTI_FINGER,
THREE_FINGER,
FOUR_FINGER;
public static TrackpadGestureType getTrackpadGestureType(MotionEvent event) {
- if (!isTrackpadMultiFingerSwipe(event)) {
- return TrackpadGestureType.NONE;
- }
if (isTrackpadThreeFingerSwipe(event)) {
return TrackpadGestureType.THREE_FINGER;
}
@@ -166,7 +161,7 @@
return TrackpadGestureType.FOUR_FINGER;
}
- return TrackpadGestureType.MULTI_FINGER;
+ return TrackpadGestureType.NONE;
}
}
diff --git a/quickstep/src/com/android/quickstep/InputConsumer.java b/quickstep/src/com/android/quickstep/InputConsumer.java
index 6b189cf..2071103 100644
--- a/quickstep/src/com/android/quickstep/InputConsumer.java
+++ b/quickstep/src/com/android/quickstep/InputConsumer.java
@@ -42,6 +42,7 @@
int TYPE_TASKBAR_STASH = 1 << 12;
int TYPE_STATUS_BAR = 1 << 13;
int TYPE_CURSOR_HOVER = 1 << 14;
+ int TYPE_NAV_HANDLE_LONG_PRESS = 1 << 15;
String[] NAMES = new String[] {
"TYPE_NO_OP", // 0
@@ -59,6 +60,7 @@
"TYPE_TASKBAR_STASH", // 12
"TYPE_STATUS_BAR", // 13
"TYPE_CURSOR_HOVER", // 14
+ "TYPE_NAV_HANDLE_LONG_PRESS", // 15
};
InputConsumer NO_OP = () -> TYPE_NO_OP;
diff --git a/quickstep/src/com/android/quickstep/InstantAppResolverImpl.java b/quickstep/src/com/android/quickstep/InstantAppResolverImpl.java
index 7638541..529213c 100644
--- a/quickstep/src/com/android/quickstep/InstantAppResolverImpl.java
+++ b/quickstep/src/com/android/quickstep/InstantAppResolverImpl.java
@@ -16,10 +16,13 @@
package com.android.quickstep;
+import android.app.ActivityThread;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.os.RemoteException;
+import android.util.Log;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.util.InstantAppResolver;
@@ -49,4 +52,14 @@
ComponentName cn = info.getTargetComponent();
return cn != null && cn.getClassName().equals(COMPONENT_CLASS_MARKER);
}
+
+ @Override
+ public boolean isInstantApp(String packageName, int userId) {
+ try {
+ return ActivityThread.getPackageManager().isInstantApp(packageName, userId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to determine whether package is instant app " + packageName, e);
+ return false;
+ }
+ }
}
diff --git a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
index 0e0b022..13da40a 100644
--- a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
@@ -15,12 +15,12 @@
*/
package com.android.quickstep;
+import static com.android.app.animation.Interpolators.LINEAR;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
diff --git a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
index fd5c1a7..a9d8afc 100644
--- a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
+++ b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
@@ -15,10 +15,10 @@
*/
package com.android.quickstep;
+import static com.android.app.animation.Interpolators.EXAGGERATED_EASE;
+import static com.android.app.animation.Interpolators.LINEAR;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.Utilities.mapBoundToRange;
-import static com.android.launcher3.anim.Interpolators.EXAGGERATED_EASE;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID;
import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
import static com.android.launcher3.views.FloatingIconView.getFloatingIconView;
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index 07db194..4a60566 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -37,7 +37,6 @@
import com.android.launcher3.taskbar.TaskbarUIController;
import com.android.launcher3.util.RunnableList;
import com.android.quickstep.RecentsAnimationCallbacks.RecentsAnimationListener;
-import com.android.quickstep.views.DesktopTaskView;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -141,6 +140,11 @@
mPendingCommands.clear();
}
+ @UiThread
+ public boolean isCommandQueueEmpty() {
+ return mPendingCommands.isEmpty();
+ }
+
@Nullable
private TaskView getNextTask(RecentsView view) {
final TaskView runningTaskView = view.getRunningTaskView();
@@ -186,11 +190,6 @@
&& dp != null
&& (dp.isTablet || dp.isTwoPanels);
- if (DesktopTaskView.DESKTOP_MODE_SUPPORTED) {
- // TODO(b/268075592): add support for quickswitch to/from desktop
- allowQuickSwitch = false;
- }
-
if (cmd.type == TYPE_HIDE) {
if (!allowQuickSwitch) {
return true;
diff --git a/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java b/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
index 5f589bf..d1939ef 100644
--- a/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
+++ b/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
@@ -19,6 +19,8 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;
+import android.os.Looper;
+import android.os.Trace;
import android.os.UserManager;
import android.util.Log;
import android.view.ThreadedRenderer;
@@ -60,5 +62,14 @@
// Elevate GPU priority for Quickstep and Remote animations.
ThreadedRenderer.setContextPriority(
ThreadedRenderer.EGL_CONTEXT_PRIORITY_HIGH_IMG);
+
+ // Enable Looper trace points.
+ // This allows us to see Handler callbacks on traces.
+ Looper.getMainLooper().setTraceTag(Trace.TRACE_TAG_APP);
+
+ if (BuildConfig.IS_STUDIO_BUILD) {
+ BinderTracker.startTracking(call -> Log.e("BinderCall",
+ call.descriptor + " called on mainthread under " + call.activeTrace));
+ }
}
}
diff --git a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
index 4c9cf8b..031d409 100644
--- a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
+++ b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
@@ -80,8 +80,7 @@
}
case TestProtocol.REQUEST_HAS_TIS: {
- response.putBoolean(
- TestProtocol.REQUEST_HAS_TIS, true);
+ response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD, true);
return response;
}
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index 38ac5bb..34817c0 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -308,7 +308,6 @@
task.setLastSnapshotData(taskInfo);
task.positionInParent = taskInfo.positionInParent;
task.appBounds = taskInfo.configuration.windowConfiguration.getAppBounds();
- // TODO(b/244348395): tasks should be sorted from oldest to most recently used
tasks.add(task);
}
return new DesktopTask(tasks);
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 39af7fd..e282d1f 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -22,7 +22,6 @@
import static com.android.launcher3.QuickstepTransitionManager.RECENTS_LAUNCH_DURATION;
import static com.android.launcher3.QuickstepTransitionManager.STATUS_BAR_TRANSITION_DURATION;
import static com.android.launcher3.QuickstepTransitionManager.STATUS_BAR_TRANSITION_PRE_DELAY;
-import static com.android.launcher3.graphics.SysUiScrim.SYSUI_PROGRESS;
import static com.android.launcher3.testing.shared.TestProtocol.OVERVIEW_STATE_ORDINAL;
import static com.android.quickstep.OverviewComponentObserver.startHomeIntentSafely;
import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
@@ -48,6 +47,7 @@
import androidx.annotation.Nullable;
+import com.android.app.animation.Interpolators;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
@@ -56,7 +56,6 @@
import com.android.launcher3.LauncherAnimationRunner.RemoteAnimationFactory;
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.model.data.ItemInfo;
@@ -107,7 +106,6 @@
private FallbackRecentsView mFallbackRecentsView;
private OverviewActionsView mActionsView;
private TISBindHelper mTISBindHelper;
- private @Nullable TaskbarManager mTaskbarManager;
private @Nullable FallbackTaskbarUIController mTaskbarUIController;
private StateManager<RecentsState> mStateManager;
@@ -130,7 +128,7 @@
mScrimView = findViewById(R.id.scrim_view);
mFallbackRecentsView = findViewById(R.id.overview_panel);
mActionsView = findViewById(R.id.overview_actions_view);
- SYSUI_PROGRESS.set(getRootView().getSysUiScrim(), 0f);
+ getRootView().getSysUiScrim().getSysUIProgress().updateValue(0);
SplitSelectStateController controller =
new SplitSelectStateController(this, mHandler, getStateManager(),
@@ -143,9 +141,9 @@
}
private void onTISConnected(TouchInteractionService.TISBinder binder) {
- mTaskbarManager = binder.getTaskbarManager();
- if (mTaskbarManager != null) {
- mTaskbarManager.setActivity(this);
+ TaskbarManager taskbarManager = binder.getTaskbarManager();
+ if (taskbarManager != null) {
+ taskbarManager.setActivity(this);
}
}
@@ -299,7 +297,7 @@
if (activityClosing) {
Animator adjacentAnimation = mFallbackRecentsView
.createAdjacentPageAnimForTaskLaunch(taskView);
- adjacentAnimation.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR);
+ adjacentAnimation.setInterpolator(Interpolators.TOUCH_RESPONSE);
adjacentAnimation.setDuration(RECENTS_LAUNCH_DURATION);
adjacentAnimation.addListener(resetStateListener());
target.play(adjacentAnimation);
@@ -386,9 +384,6 @@
mActivityLaunchAnimationRunner = null;
mTISBindHelper.onDestroy();
- if (mTaskbarManager != null) {
- mTaskbarManager.clearActivity(this);
- }
}
@Override
@@ -471,4 +466,11 @@
}
};
}
+
+ @Override
+ public boolean isCommandQueueEmpty() {
+ OverviewCommandHelper overviewCommandHelper = mTISBindHelper.getOverviewCommandHelper();
+ return super.isCommandQueueEmpty()
+ && (overviewCommandHelper == null || overviewCommandHelper.isCommandQueueEmpty());
+ }
}
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index 64ec1d8..7693e69 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -17,7 +17,6 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.content.Intent.ACTION_USER_UNLOCKED;
import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.launcher3.util.DisplayController.CHANGE_ALL;
@@ -52,10 +51,8 @@
import android.graphics.Region;
import android.inputmethodservice.InputMethodService;
import android.net.Uri;
-import android.os.Process;
import android.os.RemoteException;
import android.os.SystemProperties;
-import android.os.UserManager;
import android.provider.Settings;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
@@ -68,7 +65,6 @@
import com.android.launcher3.util.DisplayController.Info;
import com.android.launcher3.util.NavigationMode;
import com.android.launcher3.util.SettingsCache;
-import com.android.launcher3.util.SimpleBroadcastReceiver;
import com.android.quickstep.TopTaskTracker.CachedTaskInfo;
import com.android.quickstep.util.NavBarPosition;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -116,15 +112,6 @@
private final boolean mIsOneHandedModeSupported;
private boolean mPipIsActive;
- private boolean mIsUserUnlocked;
- private final ArrayList<Runnable> mUserUnlockedActions = new ArrayList<>();
- private final SimpleBroadcastReceiver mUserUnlockedReceiver = new SimpleBroadcastReceiver(i -> {
- if (ACTION_USER_UNLOCKED.equals(i.getAction())) {
- mIsUserUnlocked = true;
- notifyUserUnlocked();
- }
- });
-
private int mGestureBlockingTaskId = -1;
private @NonNull Region mExclusionRegion = new Region();
private SystemGestureExclusionListenerCompat mExclusionListener;
@@ -150,14 +137,6 @@
runOnDestroy(mRotationTouchHelper::destroy);
}
- // Register for user unlocked if necessary
- mIsUserUnlocked = context.getSystemService(UserManager.class)
- .isUserUnlocked(Process.myUserHandle());
- if (!mIsUserUnlocked) {
- mUserUnlockedReceiver.register(mContext, ACTION_USER_UNLOCKED);
- }
- runOnDestroy(() -> mUserUnlockedReceiver.unregisterReceiverSafely(mContext));
-
// Register for exclusion updates
mExclusionListener = new SystemGestureExclusionListenerCompat(mDisplayId) {
@Override
@@ -260,7 +239,7 @@
public void onDisplayInfoChanged(Context context, Info info, int flags) {
if ((flags & (CHANGE_ROTATION | CHANGE_NAVIGATION_MODE)) != 0) {
mMode = info.navigationMode;
- mNavBarPosition = new NavBarPosition(mMode, info);
+ mNavBarPosition = new NavBarPosition(mContext, mMode, info);
if (mMode == NO_BUTTON) {
mExclusionListener.register();
@@ -317,39 +296,12 @@
}
/**
- * Adds a callback for when a user is unlocked. If the user is already unlocked, this listener
- * will be called back immediately.
- */
- public void runOnUserUnlocked(Runnable action) {
- if (mIsUserUnlocked) {
- action.run();
- } else {
- mUserUnlockedActions.add(action);
- }
- }
-
- /**
- * @return whether the user is unlocked.
- */
- public boolean isUserUnlocked() {
- return mIsUserUnlocked;
- }
-
- /**
* @return whether the user has completed setup wizard
*/
public boolean isUserSetupComplete() {
return mIsUserSetupComplete;
}
- private void notifyUserUnlocked() {
- for (Runnable action : mUserUnlockedActions) {
- action.run();
- }
- mUserUnlockedActions.clear();
- mUserUnlockedReceiver.unregisterReceiverSafely(mContext);
- }
-
/**
* Sets the task id where gestures should be blocked
*/
@@ -386,8 +338,16 @@
boolean canStartWithNavHidden = (mSystemUiStateFlags & SYSUI_STATE_NAV_BAR_HIDDEN) == 0
|| (mSystemUiStateFlags & SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY) != 0
|| mRotationTouchHelper.isTaskListFrozen();
- return canStartWithNavHidden
- && (mSystemUiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) == 0
+ return canStartWithNavHidden && canStartTrackpadGesture();
+ }
+
+ /**
+ * @return whether SystemUI is in a state where we can start a system gesture from the trackpad.
+ * Trackpad gestures can start even when the nav bar / task bar is hidden in sticky immersive
+ * mode.
+ */
+ public boolean canStartTrackpadGesture() {
+ return (mSystemUiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) == 0
&& (mSystemUiStateFlags & SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING) == 0
&& (mSystemUiStateFlags & SYSUI_STATE_QUICK_SETTINGS_EXPANDED) == 0
&& (mSystemUiStateFlags & SYSUI_STATE_MAGNIFICATION_OVERLAP) == 0
@@ -607,7 +567,6 @@
pw.println(" assistantAvailable=" + mAssistantAvailable);
pw.println(" assistantDisabled="
+ QuickStepContract.isAssistantGestureDisabled(mSystemUiStateFlags));
- pw.println(" isUserUnlocked=" + mIsUserUnlocked);
pw.println(" isOneHandedModeEnabled=" + mIsOneHandedModeEnabled);
pw.println(" isSwipeToNotificationEnabled=" + mIsSwipeToNotificationEnabled);
pw.println(" deferredGestureRegion=" + mDeferredGestureRegion.getBounds());
diff --git a/quickstep/src/com/android/quickstep/RotationTouchHelper.java b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
index 8626c40..2d47097 100644
--- a/quickstep/src/com/android/quickstep/RotationTouchHelper.java
+++ b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
@@ -19,6 +19,7 @@
import static android.view.Surface.ROTATION_0;
import static com.android.launcher3.MotionEventsUtils.isTrackpadMultiFingerSwipe;
+import static com.android.launcher3.MotionEventsUtils.isTrackpadScroll;
import static com.android.launcher3.util.DisplayController.CHANGE_ACTIVE_SCREEN;
import static com.android.launcher3.util.DisplayController.CHANGE_ALL;
import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
@@ -232,16 +233,18 @@
/**
* @return whether the coordinates of the {@param event} is in the swipe up gesture region.
*/
- public boolean isInSwipeUpTouchRegion(MotionEvent event, BaseActivityInterface activity) {
- return isInSwipeUpTouchRegion(event, 0, activity);
+ public boolean isInSwipeUpTouchRegion(MotionEvent event) {
+ return isInSwipeUpTouchRegion(event, 0);
}
/**
* @return whether the coordinates of the {@param event} with the given {@param pointerIndex}
* is in the swipe up gesture region.
*/
- public boolean isInSwipeUpTouchRegion(MotionEvent event, int pointerIndex,
- BaseActivityInterface activity) {
+ public boolean isInSwipeUpTouchRegion(MotionEvent event, int pointerIndex) {
+ if (isTrackpadScroll(event)) {
+ return false;
+ }
if (isTrackpadMultiFingerSwipe(event)) {
return true;
}
diff --git a/quickstep/src/com/android/quickstep/SplitSelectionListener.kt b/quickstep/src/com/android/quickstep/SplitSelectionListener.kt
new file mode 100644
index 0000000..5025c1c
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/SplitSelectionListener.kt
@@ -0,0 +1,17 @@
+package com.android.quickstep
+
+interface SplitSelectionListener {
+ /** Called when the first app has been selected with the intention to launch split screen */
+ fun onSplitSelectionActive()
+
+ /** Called when the second app has been selected with the intention to launch split screen */
+ fun onSplitSelectionConfirmed()
+
+ /**
+ * Called when the user no longer is in the process of selecting apps for split screen.
+ * [launchedSplit] will be true if selected apps have launched successfully (either in
+ * split screen or fullscreen), false if the user canceled/exited the selection process
+ */
+ fun onSplitSelectionExit(launchedSplit: Boolean) {
+ }
+}
\ No newline at end of file
diff --git a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
index 25ac47a..e481165 100644
--- a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
+++ b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
@@ -15,8 +15,8 @@
*/
package com.android.quickstep;
-import static com.android.launcher3.anim.Interpolators.ACCEL_1_5;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.app.animation.Interpolators.ACCELERATE_1_5;
+import static com.android.app.animation.Interpolators.LINEAR;
import android.animation.Animator;
import android.content.Context;
@@ -218,7 +218,7 @@
if (progress >= end) {
return 0f;
}
- return Utilities.mapToRange(progress, start, end, 1, 0, ACCEL_1_5);
+ return Utilities.mapToRange(progress, start, end, 1, 0, ACCELERATE_1_5);
}
}
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index 9350c72..dd6499b 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -74,6 +74,7 @@
import com.android.wm.shell.bubbles.IBubbles;
import com.android.wm.shell.bubbles.IBubblesListener;
import com.android.wm.shell.desktopmode.IDesktopMode;
+import com.android.wm.shell.desktopmode.IDesktopTaskListener;
import com.android.wm.shell.draganddrop.IDragAndDrop;
import com.android.wm.shell.onehanded.IOneHanded;
import com.android.wm.shell.pip.IPip;
@@ -130,6 +131,7 @@
private ILauncherUnlockAnimationController mLauncherUnlockAnimationController;
private IRecentTasksListener mRecentTasksListener;
private IUnfoldTransitionListener mUnfoldAnimationListener;
+ private IDesktopTaskListener mDesktopTaskListener;
private final LinkedHashMap<RemoteTransition, TransitionFilter> mRemoteTransitions =
new LinkedHashMap<>();
private IBinder mOriginalTransactionToken = null;
@@ -243,6 +245,7 @@
registerRecentTasksListener(mRecentTasksListener);
setBackToLauncherCallback(mBackToLauncherCallback, mBackToLauncherRunner);
setUnfoldAnimationListener(mUnfoldAnimationListener);
+ setDesktopTaskListener(mDesktopTaskListener);
}
/**
@@ -311,13 +314,24 @@
@MainThread
@Override
- public void onStatusBarMotionEvent(MotionEvent event) {
+ public void onStatusBarTouchEvent(MotionEvent event) {
Preconditions.assertUIThread();
if (mSystemUiProxy != null) {
try {
- mSystemUiProxy.onStatusBarMotionEvent(event);
+ mSystemUiProxy.onStatusBarTouchEvent(event);
} catch (RemoteException e) {
- Log.w(TAG, "Failed call onStatusBarMotionEvent", e);
+ Log.w(TAG, "Failed call onStatusBarTouchEvent with arg: " + event, e);
+ }
+ }
+ }
+
+ @Override
+ public void onStatusBarTrackpadEvent(MotionEvent event) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.onStatusBarTrackpadEvent(event);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call onStatusBarTrackpadEvent with arg: " + event, e);
}
}
}
@@ -1161,6 +1175,41 @@
}
}
+ /** Call shell to stash desktop apps */
+ public void stashDesktopApps(int displayId) {
+ if (mDesktopMode != null) {
+ try {
+ mDesktopMode.stashDesktopApps(displayId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call stashDesktopApps", e);
+ }
+ }
+ }
+
+ /** Call shell to hide desktop apps that may be stashed */
+ public void hideStashedDesktopApps(int displayId) {
+ if (mDesktopMode != null) {
+ try {
+ mDesktopMode.hideStashedDesktopApps(displayId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call hideStashedDesktopApps", e);
+ }
+ }
+ }
+
+ /**
+ * If task with the given id is on the desktop, bring it to front
+ */
+ public void showDesktopApp(int taskId) {
+ if (mDesktopMode != null) {
+ try {
+ mDesktopMode.showDesktopApp(taskId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call showDesktopApp", e);
+ }
+ }
+ }
+
/** Call shell to get number of visible freeform tasks */
public int getVisibleDesktopTaskCount(int displayId) {
if (mDesktopMode != null) {
@@ -1173,6 +1222,18 @@
return 0;
}
+ /** Set a listener on shell to get updates about desktop task state */
+ public void setDesktopTaskListener(@Nullable IDesktopTaskListener listener) {
+ mDesktopTaskListener = listener;
+ if (mDesktopMode != null) {
+ try {
+ mDesktopMode.setTaskListener(listener);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call setDesktopTaskListener", e);
+ }
+ }
+ }
+
//
// Unfold transition
//
diff --git a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
index 208f99a..56f407c 100644
--- a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
@@ -396,11 +396,12 @@
@Override
public List<SystemShortcut> getShortcuts(BaseDraggingActivity activity,
TaskIdAttributeContainer taskContainer) {
- return InstantAppResolver.newInstance(activity).isInstantApp(activity,
- taskContainer.getTask().getTopComponent().getPackageName()) ?
- Collections.singletonList(new SystemShortcut.Install(activity,
- taskContainer.getItemInfo(), taskContainer.getTaskView())) :
- null;
+ Task t = taskContainer.getTask();
+ return InstantAppResolver.newInstance(activity).isInstantApp(
+ t.getTopComponent().getPackageName(), t.getKey().userId)
+ ? Collections.singletonList(new SystemShortcut.Install(activity,
+ taskContainer.getItemInfo(), taskContainer.getTaskView()))
+ : null;
}
};
diff --git a/quickstep/src/com/android/quickstep/TaskUtils.java b/quickstep/src/com/android/quickstep/TaskUtils.java
index 67360c4..80a449b 100644
--- a/quickstep/src/com/android/quickstep/TaskUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskUtils.java
@@ -33,6 +33,7 @@
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.util.TraceHelper;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -51,7 +52,8 @@
* TODO: remove this once we switch to getting the icon and label from IconCache.
*/
public static CharSequence getTitle(Context context, Task task) {
- return getTitle(context, task.key.userId, task.getTopComponent().getPackageName());
+ return TraceHelper.allowIpcs("TaskUtils.getTitle", () ->
+ getTitle(context, task.key.userId, task.getTopComponent().getPackageName()));
}
public static CharSequence getTitle(
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index 499a260..bfe52dd 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -21,6 +21,9 @@
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static com.android.app.animation.Interpolators.LINEAR;
+import static com.android.app.animation.Interpolators.TOUCH_RESPONSE;
+import static com.android.app.animation.Interpolators.clampToProgress;
import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
@@ -35,9 +38,6 @@
import static com.android.launcher3.QuickstepTransitionManager.SPLIT_DIVIDER_ANIM_DURATION;
import static com.android.launcher3.QuickstepTransitionManager.SPLIT_LAUNCH_DURATION;
import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
-import static com.android.launcher3.anim.Interpolators.clampToProgress;
import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
import static com.android.quickstep.views.DesktopTaskView.DESKTOP_MODE_SUPPORTED;
@@ -61,12 +61,12 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.app.animation.Interpolators;
import com.android.launcher3.BaseActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.statehandlers.DepthController;
@@ -237,12 +237,12 @@
for (RemoteTargetHandle targetHandle : remoteTargetHandles) {
TaskViewSimulator tvsLocal = targetHandle.getTaskViewSimulator();
out.setFloat(tvsLocal.fullScreenProgress,
- AnimatedFloat.VALUE, 1, TOUCH_RESPONSE_INTERPOLATOR);
+ AnimatedFloat.VALUE, 1, TOUCH_RESPONSE);
out.setFloat(tvsLocal.recentsViewScale,
AnimatedFloat.VALUE, tvsLocal.getFullScreenScale(),
- TOUCH_RESPONSE_INTERPOLATOR);
+ TOUCH_RESPONSE);
out.setFloat(tvsLocal.recentsViewScroll, AnimatedFloat.VALUE, 0,
- TOUCH_RESPONSE_INTERPOLATOR);
+ TOUCH_RESPONSE);
out.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
@@ -355,9 +355,9 @@
float fullScreenScale =
topMostSimulators[i].getTaskViewSimulator().getFullScreenScale();
out.addFloat(ttv, VIEW_TRANSLATE_Y, translationY,
- translationY / fullScreenScale, TOUCH_RESPONSE_INTERPOLATOR);
+ translationY / fullScreenScale, TOUCH_RESPONSE);
out.addFloat(ttv, VIEW_TRANSLATE_X, translationX,
- translationX / fullScreenScale, TOUCH_RESPONSE_INTERPOLATOR);
+ translationX / fullScreenScale, TOUCH_RESPONSE);
}
Matrix[] k0i = new Matrix[matrixSize];
@@ -405,7 +405,7 @@
if (depthController != null) {
out.setFloat(depthController.stateDepth, MULTI_PROPERTY_VALUE,
- BACKGROUND_APP.getDepth(baseActivity), TOUCH_RESPONSE_INTERPOLATOR);
+ BACKGROUND_APP.getDepth(baseActivity), TOUCH_RESPONSE);
}
}
@@ -639,7 +639,7 @@
raController.setWillFinishToHome(false);
}
launcherAnim = recentsView.createAdjacentPageAnimForTaskLaunch(taskView);
- launcherAnim.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR);
+ launcherAnim.setInterpolator(Interpolators.TOUCH_RESPONSE);
launcherAnim.setDuration(RECENTS_LAUNCH_DURATION);
windowAnimEndListener = new AnimatorListenerAdapter() {
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index d6a468d..b5c0f7d 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -24,6 +24,8 @@
import static android.view.MotionEvent.ACTION_UP;
import static com.android.launcher3.Launcher.INTENT_ACTION_ALL_APPS_TOGGLE;
+import static com.android.launcher3.MotionEventsUtils.isTrackpadMotionEvent;
+import static com.android.launcher3.MotionEventsUtils.isTrackpadMultiFingerSwipe;
import static com.android.launcher3.config.FeatureFlags.ENABLE_TRACKPAD_GESTURE;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.quickstep.GestureState.DEFAULT_STATE;
@@ -37,7 +39,6 @@
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNFOLD_ANIMATION_FORWARDER;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TRACING_ENABLED;
@@ -82,11 +83,9 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.provider.RestoreDbTask;
-import com.android.launcher3.statehandlers.DesktopVisibilityController;
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.launcher3.taskbar.TaskbarManager;
@@ -98,11 +97,14 @@
import com.android.launcher3.uioverrides.flags.FlagsFactory;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.LockedUserState;
import com.android.launcher3.util.OnboardingPrefs;
+import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.util.TraceHelper;
import com.android.quickstep.inputconsumers.AccessibilityInputConsumer;
import com.android.quickstep.inputconsumers.AssistantInputConsumer;
import com.android.quickstep.inputconsumers.DeviceLockedInputConsumer;
+import com.android.quickstep.inputconsumers.NavHandleLongPressInputConsumer;
import com.android.quickstep.inputconsumers.OneHandedModeInputConsumer;
import com.android.quickstep.inputconsumers.OtherActivityInputConsumer;
import com.android.quickstep.inputconsumers.OverviewInputConsumer;
@@ -110,9 +112,9 @@
import com.android.quickstep.inputconsumers.ProgressDelegateInputConsumer;
import com.android.quickstep.inputconsumers.ResetGestureInputConsumer;
import com.android.quickstep.inputconsumers.ScreenPinnedInputConsumer;
-import com.android.quickstep.inputconsumers.StatusBarInputConsumer;
import com.android.quickstep.inputconsumers.SysUiOverlayInputConsumer;
import com.android.quickstep.inputconsumers.TaskbarUnstashInputConsumer;
+import com.android.quickstep.inputconsumers.TrackpadStatusBarInputConsumer;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.ActiveGestureLog.CompoundString;
import com.android.quickstep.util.ProtoTracer;
@@ -140,8 +142,6 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
-import java.util.Arrays;
-import java.util.LinkedList;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -488,8 +488,8 @@
BootAwarePreloader.start(this);
// Call runOnUserUnlocked() before any other callbacks to ensure everything is initialized.
- mDeviceState.runOnUserUnlocked(this::onUserUnlocked);
- mDeviceState.runOnUserUnlocked(mTaskbarManager::onUserUnlocked);
+ LockedUserState.get(this).runOnUserUnlocked(this::onUserUnlocked);
+ LockedUserState.get(this).runOnUserUnlocked(mTaskbarManager::onUserUnlocked);
mDeviceState.addNavigationModeChangedCallback(this::onNavigationModeChanged);
ProtoTracer.INSTANCE.get(this).add(this);
@@ -511,7 +511,7 @@
private void initInputMonitor(String reason) {
disposeEventHandlers("Initializing input monitor due to: " + reason);
- if (mDeviceState.isButtonNavMode()) {
+ if (mDeviceState.isButtonNavMode() && !ENABLE_TRACKPAD_GESTURE.get()) {
return;
}
@@ -559,7 +559,7 @@
}
private void resetHomeBounceSeenOnQuickstepEnabledFirstTime() {
- if (!mDeviceState.isUserUnlocked() || mDeviceState.isButtonNavMode()) {
+ if (!LockedUserState.get(this).isUserUnlocked() || mDeviceState.isButtonNavMode()) {
// Skip if not yet unlocked (can't read user shared prefs) or if the current navigation
// mode doesn't have gestures
return;
@@ -602,24 +602,12 @@
@UiThread
private void onSystemUiFlagsChanged(int lastSysUIFlags) {
- if (mDeviceState.isUserUnlocked()) {
+ if (LockedUserState.get(this).isUserUnlocked()) {
int systemUiStateFlags = mDeviceState.getSystemUiStateFlags();
SystemUiProxy.INSTANCE.get(this).setLastSystemUiStateFlags(systemUiStateFlags);
mOverviewComponentObserver.onSystemUiStateChanged();
mTaskbarManager.onSystemUiFlagsChanged(systemUiStateFlags);
- boolean wasFreeformActive =
- (lastSysUIFlags & SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE) != 0;
- boolean isFreeformActive =
- (systemUiStateFlags & SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE) != 0;
- if (wasFreeformActive != isFreeformActive) {
- DesktopVisibilityController controller =
- LauncherActivityInterface.INSTANCE.getDesktopVisibilityController();
- if (controller != null) {
- controller.setFreeformTasksVisible(isFreeformActive);
- }
- }
-
int isShadeExpandedFlag =
SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED | SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
boolean wasExpanded = (lastSysUIFlags & isShadeExpandedFlag) != 0;
@@ -647,7 +635,7 @@
@UiThread
private void onAssistantVisibilityChanged() {
- if (mDeviceState.isUserUnlocked()) {
+ if (LockedUserState.get(this).isUserUnlocked()) {
mOverviewComponentObserver.getActivityInterface().onAssistantVisibilityChanged(
mDeviceState.getAssistantVisibility());
}
@@ -657,7 +645,7 @@
public void onDestroy() {
Log.d(TAG, "Touch service destroyed: user=" + getUserId());
sIsInitialized = false;
- if (mDeviceState.isUserUnlocked()) {
+ if (LockedUserState.get(this).isUserUnlocked()) {
mInputConsumer.unregisterInputConsumer();
mOverviewComponentObserver.onDestroy();
}
@@ -691,12 +679,12 @@
TestLogging.recordMotionEvent(
TestProtocol.SEQUENCE_TIS, "TouchInteractionService.onInputEvent", event);
- if (!mDeviceState.isUserUnlocked()) {
+ if (!LockedUserState.get(this).isUserUnlocked() || (mDeviceState.isButtonNavMode()
+ && !isTrackpadMotionEvent(event))) {
return;
}
- Object traceToken = TraceHelper.INSTANCE.beginFlagsOverride(
- TraceHelper.FLAG_ALLOW_BINDER_TRACKING);
+ SafeCloseable traceToken = TraceHelper.INSTANCE.allowIpcs("TIS.onInputEvent");
final int action = event.getActionMasked();
// Note this will create a new consumer every mouse click, as after ACTION_UP from the click
@@ -707,8 +695,7 @@
mRotationTouchHelper.setOrientationTransformIfNeeded(event);
if ((!mDeviceState.isOneHandedModeActive()
- && mRotationTouchHelper.isInSwipeUpTouchRegion(event,
- mOverviewComponentObserver.getActivityInterface()))
+ && mRotationTouchHelper.isInSwipeUpTouchRegion(event))
|| isHoverActionWithoutConsumer) {
// Clone the previous gesture state since onConsumerAboutToBeSwitched might trigger
// onConsumerInactive and wipe the previous gesture state
@@ -720,7 +707,8 @@
mGestureState = newGestureState;
mConsumer = newConsumer(prevGestureState, mGestureState, event);
mUncheckedConsumer = mConsumer;
- } else if (mDeviceState.isUserUnlocked() && mDeviceState.isFullyGesturalNavMode()
+ } else if (LockedUserState.get(this).isUserUnlocked()
+ && (mDeviceState.isFullyGesturalNavMode() || isTrackpadMultiFingerSwipe(event))
&& mDeviceState.canTriggerAssistantAction(event)) {
mGestureState = createGestureState(mGestureState,
getTrackpadGestureType(event));
@@ -783,9 +771,6 @@
if (mGestureState.isTrackpadGesture() && (action == ACTION_POINTER_DOWN
|| action == ACTION_POINTER_UP)) {
// Skip ACTION_POINTER_DOWN and ACTION_POINTER_UP events from trackpad.
- if (action == ACTION_POINTER_DOWN) {
- mGestureState.setTrackpadGestureType(getTrackpadGestureType(event));
- }
} else if (event.isHoverEvent()) {
mUncheckedConsumer.onHoverEvent(event);
} else {
@@ -795,7 +780,7 @@
if (cleanUpConsumer) {
reset();
}
- TraceHelper.INSTANCE.endFlagsOverride(traceToken);
+ traceToken.close();
ProtoTracer.INSTANCE.get(this).scheduleFrameUpdate();
}
@@ -862,9 +847,11 @@
return consumer;
}
- boolean canStartSystemGesture = mDeviceState.canStartSystemGesture();
+ boolean canStartSystemGesture =
+ mGestureState.isTrackpadGesture() ? mDeviceState.canStartTrackpadGesture()
+ : mDeviceState.canStartSystemGesture();
- if (!mDeviceState.isUserUnlocked()) {
+ if (!LockedUserState.get(this).isUserUnlocked()) {
CompoundString reasonString = newCompoundString("device locked");
InputConsumer consumer;
if (canStartSystemGesture) {
@@ -897,11 +884,12 @@
.append(", trying to use default input consumer");
base = getDefaultInputConsumer(reasonString);
}
- if (mDeviceState.isGesturalNavMode()) {
+ if (mDeviceState.isGesturalNavMode() || newGestureState.isTrackpadGesture()) {
handleOrientationSetup(base);
}
- if (mDeviceState.isFullyGesturalNavMode()) {
- String reasonPrefix = "device is in gesture navigation mode";
+ if (mDeviceState.isFullyGesturalNavMode() || newGestureState.isTrackpadGesture()) {
+ String reasonPrefix = "device is in gesture navigation mode or 3-button mode with a"
+ + "trackpad gesture";
if (mDeviceState.canTriggerAssistantAction(event)) {
reasonString.append(NEWLINE_PREFIX)
.append(reasonPrefix)
@@ -926,6 +914,8 @@
+ "using TaskbarUnstashInputConsumer");
base = new TaskbarUnstashInputConsumer(this, base, mInputMonitorCompat, tac);
}
+ } else if (canStartSystemGesture && FeatureFlags.ENABLE_LONG_PRESS_NAV_HANDLE.get()) {
+ base = new NavHandleLongPressInputConsumer(this, base, mInputMonitorCompat);
}
if (mDeviceState.isBubblesExpanded()) {
@@ -945,11 +935,12 @@
}
if (ENABLE_TRACKPAD_GESTURE.get() && mGestureState.isTrackpadGesture()
- && !previousGestureState.isRecentsAnimationRunning()) {
+ && canStartSystemGesture && !previousGestureState.isRecentsAnimationRunning()) {
reasonString = newCompoundString(reasonPrefix)
.append(SUBSTRING_PREFIX)
- .append("Trackpad 3-finger gesture, using StatusBarInputConsumer");
- base = new StatusBarInputConsumer(getBaseContext(), base, mInputMonitorCompat);
+ .append("Trackpad 3-finger gesture, using TrackpadStatusBarInputConsumer");
+ base = new TrackpadStatusBarInputConsumer(getBaseContext(), base,
+ mInputMonitorCompat);
}
if (mDeviceState.isScreenPinningActive()) {
@@ -1112,17 +1103,21 @@
private InputConsumer createDeviceLockedInputConsumer(
GestureState gestureState, CompoundString reasonString) {
- if (mDeviceState.isFullyGesturalNavMode() && gestureState.getRunningTask() != null) {
+ if ((mDeviceState.isFullyGesturalNavMode() || gestureState.isTrackpadGesture())
+ && gestureState.getRunningTask() != null) {
reasonString.append(SUBSTRING_PREFIX)
- .append("device is in gesture nav mode and running task != null")
+ .append("device is in gesture nav mode or 3-button mode with a trackpad gesture"
+ + "and running task != null")
.append(", using DeviceLockedInputConsumer");
return new DeviceLockedInputConsumer(
this, mDeviceState, mTaskAnimationManager, gestureState, mInputMonitorCompat);
} else {
return getDefaultInputConsumer(reasonString
.append(SUBSTRING_PREFIX)
- .append(mDeviceState.isFullyGesturalNavMode()
- ? "running task == null" : "device is not in gesture nav mode")
+ .append((mDeviceState.isFullyGesturalNavMode()
+ || gestureState.isTrackpadGesture())
+ ? "running task == null"
+ : "device is not in gesture nav mode and it's not a trackpad gesture")
.append(", trying to use default input consumer"));
}
}
@@ -1215,7 +1210,7 @@
}
private void preloadOverview(boolean fromInit, boolean forSUWAllSet) {
- if (!mDeviceState.isUserUnlocked()) {
+ if (!LockedUserState.get(this).isUserUnlocked()) {
return;
}
@@ -1251,7 +1246,7 @@
@Override
public void onConfigurationChanged(Configuration newConfig) {
- if (!mDeviceState.isUserUnlocked()) {
+ if (!LockedUserState.get(this).isUserUnlocked()) {
return;
}
final BaseActivityInterface activityInterface =
@@ -1278,80 +1273,41 @@
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] rawArgs) {
- if (rawArgs.length > 0 && Utilities.IS_DEBUG_DEVICE) {
- LinkedList<String> args = new LinkedList(Arrays.asList(rawArgs));
- switch (args.pollFirst()) {
- case "cmd":
- if (args.peekFirst() == null) {
- printAvailableCommands(pw);
- } else {
- onCommand(pw, args);
- }
- break;
- }
- } else {
- // Dump everything
- FlagsFactory.dump(pw);
- if (mDeviceState.isUserUnlocked()) {
- PluginManagerWrapper.INSTANCE.get(getBaseContext()).dump(pw);
- }
- mDeviceState.dump(pw);
- if (mOverviewComponentObserver != null) {
- mOverviewComponentObserver.dump(pw);
- }
- if (mOverviewCommandHelper != null) {
- mOverviewCommandHelper.dump(pw);
- }
- if (mGestureState != null) {
- mGestureState.dump(pw);
- }
- pw.println("Input state:");
- pw.println(" mInputMonitorCompat=" + mInputMonitorCompat);
- pw.println(" mInputEventReceiver=" + mInputEventReceiver);
- DisplayController.INSTANCE.get(this).dump(pw);
- pw.println("TouchState:");
- BaseDraggingActivity createdOverviewActivity = mOverviewComponentObserver == null ? null
- : mOverviewComponentObserver.getActivityInterface().getCreatedActivity();
- boolean resumed = mOverviewComponentObserver != null
- && mOverviewComponentObserver.getActivityInterface().isResumed();
- pw.println(" createdOverviewActivity=" + createdOverviewActivity);
- pw.println(" resumed=" + resumed);
- pw.println(" mConsumer=" + mConsumer.getName());
- ActiveGestureLog.INSTANCE.dump("", pw);
- RecentsModel.INSTANCE.get(this).dump("", pw);
- pw.println("ProtoTrace:");
- pw.println(" file=" + ProtoTracer.INSTANCE.get(this).getTraceFile());
- if (createdOverviewActivity != null) {
- createdOverviewActivity.getDeviceProfile().dump(this, "", pw);
- }
- mTaskbarManager.dumpLogs("", pw);
+ // Dump everything
+ FlagsFactory.dump(pw);
+ if (LockedUserState.get(this).isUserUnlocked()) {
+ PluginManagerWrapper.INSTANCE.get(getBaseContext()).dump(pw);
}
- }
-
- private void printAvailableCommands(PrintWriter pw) {
- pw.println("Available commands:");
- pw.println(" clear-touch-log: Clears the touch interaction log");
- pw.println(" print-gesture-log: only prints the ActiveGestureLog dump");
- }
-
- private void onCommand(PrintWriter pw, LinkedList<String> args) {
- String cmd = args.pollFirst();
- if (cmd == null) {
- pw.println("Command missing");
- printAvailableCommands(pw);
- return;
+ mDeviceState.dump(pw);
+ if (mOverviewComponentObserver != null) {
+ mOverviewComponentObserver.dump(pw);
}
- switch (cmd) {
- case "clear-touch-log":
- ActiveGestureLog.INSTANCE.clear();
- break;
- case "print-gesture-log":
- ActiveGestureLog.INSTANCE.dump("", pw);
- break;
- default:
- pw.println("Command does not exist: " + cmd);
- printAvailableCommands(pw);
+ if (mOverviewCommandHelper != null) {
+ mOverviewCommandHelper.dump(pw);
}
+ if (mGestureState != null) {
+ mGestureState.dump(pw);
+ }
+ pw.println("Input state:");
+ pw.println(" mInputMonitorCompat=" + mInputMonitorCompat);
+ pw.println(" mInputEventReceiver=" + mInputEventReceiver);
+ DisplayController.INSTANCE.get(this).dump(pw);
+ pw.println("TouchState:");
+ BaseDraggingActivity createdOverviewActivity = mOverviewComponentObserver == null ? null
+ : mOverviewComponentObserver.getActivityInterface().getCreatedActivity();
+ boolean resumed = mOverviewComponentObserver != null
+ && mOverviewComponentObserver.getActivityInterface().isResumed();
+ pw.println(" createdOverviewActivity=" + createdOverviewActivity);
+ pw.println(" resumed=" + resumed);
+ pw.println(" mConsumer=" + mConsumer.getName());
+ ActiveGestureLog.INSTANCE.dump("", pw);
+ RecentsModel.INSTANCE.get(this).dump("", pw);
+ pw.println("ProtoTrace:");
+ pw.println(" file=" + ProtoTracer.INSTANCE.get(this).getTraceFile());
+ if (createdOverviewActivity != null) {
+ createdOverviewActivity.getDeviceProfile().dump(this, "", pw);
+ }
+ mTaskbarManager.dumpLogs("", pw);
}
private AbsSwipeUpHandler createLauncherSwipeHandler(
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackNavBarTouchController.java b/quickstep/src/com/android/quickstep/fallback/FallbackNavBarTouchController.java
index 8a87f63..66f5c00 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackNavBarTouchController.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackNavBarTouchController.java
@@ -42,7 +42,7 @@
mActivity = activity;
NavigationMode sysUINavigationMode = DisplayController.getNavigationMode(mActivity);
if (sysUINavigationMode == NavigationMode.NO_BUTTON) {
- NavBarPosition navBarPosition = new NavBarPosition(sysUINavigationMode,
+ NavBarPosition navBarPosition = new NavBarPosition(mActivity, sysUINavigationMode,
DisplayController.INSTANCE.get(mActivity).getInfo());
mTriggerSwipeUpTracker = new TriggerSwipeUpTouchTracker(mActivity,
true /* disableHorizontalSwipe */, navBarPosition,
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
index 11b1ab8..8a9e04e 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
@@ -15,9 +15,9 @@
*/
package com.android.quickstep.fallback;
-import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
-import static com.android.launcher3.anim.Interpolators.INSTANT;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.app.animation.Interpolators.FINAL_FRAME;
+import static com.android.app.animation.Interpolators.INSTANT;
+import static com.android.app.animation.Interpolators.LINEAR;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_MODAL;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X;
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index 074aedd..a9a57c6 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -35,6 +35,7 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.statemanager.StateManager.StateListener;
import com.android.launcher3.util.SplitConfigurationOptions;
@@ -79,11 +80,16 @@
}
@Override
- public void startHome(boolean animated) {
+ protected void handleStartHome(boolean animated) {
mActivity.startHome();
AbstractFloatingView.closeAllOpenViews(mActivity, mActivity.isStarted());
}
+ @Override
+ public boolean isCommandQueueEmpty() {
+ return mActivity.isCommandQueueEmpty();
+ }
+
/**
* When starting gesture interaction from home, we add a temporary invisible tile corresponding
* to the home task. This allows us to handle quick-switch similarly to a quick-switching
@@ -246,7 +252,11 @@
setOverviewSelectEnabled(false);
}
if (finalState != OVERVIEW_SPLIT_SELECT) {
- resetFromSplitSelectionState();
+ if (FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
+ mSplitSelectStateController.resetState();
+ } else {
+ resetFromSplitSelectionState();
+ }
}
if (isOverlayEnabled) {
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java
index 6a36d9f..2abc7ba 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java
@@ -103,8 +103,7 @@
if (mState == STATE_INACTIVE) {
int pointerIndex = ev.getActionIndex();
if (mDeviceState.getRotationTouchHelper().isInSwipeUpTouchRegion(ev,
- pointerIndex, mGestureState.getActivityInterface())
- && mDelegate.allowInterceptByParent()) {
+ pointerIndex) && mDelegate.allowInterceptByParent()) {
setActive(ev);
mActivePointerId = ev.getPointerId(pointerIndex);
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/AssistantInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/AssistantInputConsumer.java
index 162ace4..a209b3b 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/AssistantInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/AssistantInputConsumer.java
@@ -42,9 +42,9 @@
import android.view.MotionEvent;
import android.view.ViewConfiguration;
+import com.android.app.animation.Interpolators;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.R;
-import com.android.launcher3.anim.Interpolators;
import com.android.quickstep.BaseActivityInterface;
import com.android.quickstep.GestureState;
import com.android.quickstep.InputConsumer;
@@ -209,7 +209,7 @@
SystemUiProxy.INSTANCE.get(mContext).onAssistantProgress(0f);
}
});
- animator.setInterpolator(Interpolators.DEACCEL_2);
+ animator.setInterpolator(Interpolators.DECELERATE_2);
animator.start();
}
mPassedSlop = false;
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
index 59a9582..2a35584 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
@@ -39,9 +39,9 @@
import android.view.RemoteAnimationTarget;
import android.view.VelocityTracker;
+import com.android.app.animation.Interpolators;
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimatedFloat;
-import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.util.DisplayController;
@@ -153,8 +153,7 @@
if (!mThresholdCrossed) {
// Cancel interaction in case of multi-touch interaction
int ptrIdx = ev.getActionIndex();
- if (!mDeviceState.getRotationTouchHelper().isInSwipeUpTouchRegion(ev, ptrIdx,
- mGestureState.getActivityInterface())) {
+ if (!mDeviceState.getRotationTouchHelper().isInSwipeUpTouchRegion(ev, ptrIdx)) {
int action = ev.getAction();
ev.setAction(ACTION_CANCEL);
finishTouchTracking(ev);
@@ -204,7 +203,7 @@
// Animate back to fullscreen before finishing
ObjectAnimator animator = mProgress.animateToValue(mProgress.value, 0);
animator.setDuration(100);
- animator.setInterpolator(Interpolators.ACCEL);
+ animator.setInterpolator(Interpolators.ACCELERATE);
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressHandler.java b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressHandler.java
new file mode 100644
index 0000000..5c5b9ca
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressHandler.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.inputconsumers;
+
+import android.content.Context;
+
+import com.android.launcher3.R;
+import com.android.launcher3.util.ResourceBasedOverride;
+
+/**
+ * Class for extending nav handle long press behavior
+ */
+public class NavHandleLongPressHandler implements ResourceBasedOverride {
+
+ /** Creates NavHandleLongPressHandler as specified by overrides */
+ public static NavHandleLongPressHandler newInstance(Context context) {
+ return Overrides.getObject(NavHandleLongPressHandler.class, context,
+ R.string.nav_handle_long_press_handler_class);
+ }
+
+ /**
+ * Called when nav handle is long pressed.
+ *
+ * @return if the long press was consumed, meaning other input consumers should receive a
+ * cancel event
+ */
+ public boolean onLongPress() {
+ return false;
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java
new file mode 100644
index 0000000..542dea1
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.inputconsumers;
+
+import android.content.Context;
+import android.view.GestureDetector;
+import android.view.GestureDetector.SimpleOnGestureListener;
+import android.view.MotionEvent;
+
+import com.android.launcher3.R;
+import com.android.launcher3.util.DisplayController;
+import com.android.quickstep.InputConsumer;
+import com.android.systemui.shared.system.InputMonitorCompat;
+
+/**
+ * Listens for a long press
+ */
+public class NavHandleLongPressInputConsumer extends DelegateInputConsumer {
+
+ private final GestureDetector mLongPressDetector;
+ private final NavHandleLongPressHandler mNavHandleLongPressHandler;
+ private final float mNavHandleWidth;
+ private final float mScreenWidth;
+
+ public NavHandleLongPressInputConsumer(Context context, InputConsumer delegate,
+ InputMonitorCompat inputMonitor) {
+ super(delegate, inputMonitor);
+ mNavHandleWidth = context.getResources()
+ .getDimensionPixelSize(R.dimen.navigation_home_handle_width);
+ mScreenWidth = DisplayController.INSTANCE.get(context).getInfo().currentSize.x;
+
+ mNavHandleLongPressHandler = NavHandleLongPressHandler.newInstance(context);
+
+ mLongPressDetector = new GestureDetector(context, new SimpleOnGestureListener() {
+ @Override
+ public void onLongPress(MotionEvent motionEvent) {
+ if (isInArea(motionEvent.getRawX())) {
+ if (mNavHandleLongPressHandler.onLongPress()) {
+ setActive(motionEvent);
+ }
+ }
+ }
+ });
+ }
+
+ @Override
+ public int getType() {
+ return TYPE_NAV_HANDLE_LONG_PRESS | mDelegate.getType();
+ }
+
+ @Override
+ public void onMotionEvent(MotionEvent ev) {
+ mLongPressDetector.onTouchEvent(ev);
+ if (mState != STATE_ACTIVE) {
+ mDelegate.onMotionEvent(ev);
+ }
+ }
+
+ protected boolean isInArea(float x) {
+ float areaFromMiddle = mNavHandleWidth / 2.0f;
+ float distFromMiddle = Math.abs(mScreenWidth / 2.0f - x);
+
+ return distFromMiddle < areaFromMiddle;
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index f9cd4ee..10c6316 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -28,7 +28,6 @@
import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
import static com.android.launcher3.Utilities.squaredHypot;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.launcher3.util.TraceHelper.FLAG_CHECK_FOR_RACE_CONDITIONS;
import static com.android.launcher3.util.VelocityUtils.PX_PER_MS;
import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID;
@@ -229,8 +228,7 @@
// Until we detect the gesture, handle events as we receive them
mInputEventReceiver.setBatchingEnabled(false);
- Object traceToken = TraceHelper.INSTANCE.beginSection(DOWN_EVT,
- FLAG_CHECK_FOR_RACE_CONDITIONS);
+ TraceHelper.INSTANCE.beginSection(DOWN_EVT);
mActivePointerId = ev.getPointerId(0);
mDownPos.set(ev.getX(), ev.getY());
mLastPos.set(mDownPos);
@@ -241,15 +239,14 @@
startTouchTrackingForWindowAnimation(ev.getEventTime());
}
- TraceHelper.INSTANCE.endSection(traceToken);
+ TraceHelper.INSTANCE.endSection();
break;
}
case ACTION_POINTER_DOWN: {
if (!mPassedPilferInputSlop) {
// Cancel interaction in case of multi-touch interaction
int ptrIdx = ev.getActionIndex();
- if (!mRotationTouchHelper.isInSwipeUpTouchRegion(ev, ptrIdx,
- mActivityInterface)) {
+ if (!mRotationTouchHelper.isInSwipeUpTouchRegion(ev, ptrIdx)) {
forceCancelGesture(ev);
}
}
@@ -352,7 +349,8 @@
mInteractionHandler.updateDisplacement(displacement - mStartDisplacement);
}
- if (mDeviceState.isFullyGesturalNavMode()) {
+ if (mDeviceState.isFullyGesturalNavMode()
+ || mGestureState.isTrackpadGesture()) {
boolean minSwipeMet = upDist >= Math.max(mMotionPauseMinDisplacement,
mInteractionHandler.getThresholdToAllowMotionPause());
mInteractionHandler.setCanSlowSwipeGoHome(minSwipeMet);
@@ -417,8 +415,7 @@
* the animation can still be running.
*/
private void finishTouchTracking(MotionEvent ev) {
- Object traceToken = TraceHelper.INSTANCE.beginSection(UP_EVT,
- FLAG_CHECK_FOR_RACE_CONDITIONS);
+ TraceHelper.INSTANCE.beginSection(UP_EVT);
if (mPassedWindowMoveSlop && mInteractionHandler != null) {
if (ev.getActionMasked() == ACTION_CANCEL) {
@@ -455,7 +452,7 @@
onInteractionGestureFinished();
}
cleanupAfterGesture();
- TraceHelper.INSTANCE.endSection(traceToken);
+ TraceHelper.INSTANCE.endSection();
}
private void cleanupAfterGesture() {
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java
index ab70272..5202529 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java
@@ -15,7 +15,7 @@
*/
package com.android.quickstep.inputconsumers;
-import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
+import static com.android.app.animation.Interpolators.scrollInterpolatorForVelocity;
import static com.android.launcher3.touch.BaseSwipeDetector.calculateDuration;
import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_POSITIVE;
import static com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL;
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java
index fbe7fde..e9a0761 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java
@@ -37,6 +37,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.launcher3.taskbar.TaskbarTranslationController.TransitionCallback;
+import com.android.launcher3.taskbar.bubbles.BubbleControllers;
import com.android.launcher3.touch.OverScroll;
import com.android.launcher3.util.DisplayController;
import com.android.quickstep.InputConsumer;
@@ -62,6 +63,7 @@
private final int mTaskbarNavThresholdY;
private final boolean mIsTaskbarAllAppsOpen;
private boolean mHasPassedTaskbarNavThreshold;
+ private boolean mIsInBubbleBarArea;
private final PointF mDownPos = new PointF();
private final PointF mLastPos = new PointF();
@@ -136,7 +138,7 @@
mHasPassedTaskbarNavThreshold = false;
mTaskbarActivityContext.setAutohideSuspendFlag(
FLAG_AUTOHIDE_SUSPEND_TOUCHING, true);
- if (isInArea(x)) {
+ if (isInTaskbarArea(x)) {
if (!mIsTransientTaskbar) {
mLongPressDownX = x;
mLongPressDownY = y;
@@ -145,10 +147,12 @@
mCanceledUnstashHint = false;
}
}
-
if (mTransitionCallback != null && !mIsTaskbarAllAppsOpen) {
mTransitionCallback.onActionDown();
}
+ if (mIsTransientTaskbar && isInBubbleBarArea(x)) {
+ mIsInBubbleBarArea = true;
+ }
break;
case MotionEvent.ACTION_POINTER_UP:
int ptrIdx = ev.getActionIndex();
@@ -185,7 +189,11 @@
if (!mHasPassedTaskbarNavThreshold && passedTaskbarNavThreshold) {
mHasPassedTaskbarNavThreshold = true;
- mTaskbarActivityContext.onSwipeToUnstashTaskbar();
+ if (mIsInBubbleBarArea) {
+ mTaskbarActivityContext.onSwipeToOpenBubblebar();
+ } else {
+ mTaskbarActivityContext.onSwipeToUnstashTaskbar();
+ }
}
if (dY < 0) {
@@ -208,21 +216,32 @@
mTransitionCallback.onActionEnd();
}
mHasPassedTaskbarNavThreshold = false;
+ mIsInBubbleBarArea = false;
break;
}
}
}
}
- private boolean isInArea(float x) {
+ private boolean isInTaskbarArea(float x) {
float areaFromMiddle = mUnstashArea / 2.0f;
float distFromMiddle = Math.abs(mScreenWidth / 2.0f - x);
return distFromMiddle < areaFromMiddle;
}
+ private boolean isInBubbleBarArea(float x) {
+ if (mTaskbarActivityContext != null && mIsTransientTaskbar) {
+ BubbleControllers controllers = mTaskbarActivityContext.getBubbleControllers();
+ if (controllers == null) return false;
+ Rect bubbleBarBounds = controllers.bubbleBarViewController.getBubbleBarBounds();
+ return x >= bubbleBarBounds.left && x <= bubbleBarBounds.right;
+ }
+ return false;
+ }
+
private void onLongPressDetected(MotionEvent motionEvent) {
if (mTaskbarActivityContext != null
- && isInArea(motionEvent.getRawX())
+ && isInTaskbarArea(motionEvent.getRawX())
&& !mIsTransientTaskbar) {
boolean taskBarPressed = mTaskbarActivityContext.onLongPressToUnstashTaskbar();
if (taskBarPressed) {
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/StatusBarInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/TrackpadStatusBarInputConsumer.java
similarity index 91%
rename from quickstep/src/com/android/quickstep/inputconsumers/StatusBarInputConsumer.java
rename to quickstep/src/com/android/quickstep/inputconsumers/TrackpadStatusBarInputConsumer.java
index 898aa86..7ff9982 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/StatusBarInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/TrackpadStatusBarInputConsumer.java
@@ -27,15 +27,15 @@
import com.android.quickstep.SystemUiProxy;
import com.android.systemui.shared.system.InputMonitorCompat;
-/** Allows the status bar to be pull down for notification shade */
-public class StatusBarInputConsumer extends DelegateInputConsumer {
+/** Allows the status bar to be pull down for notification shade using the trackpad. */
+public class TrackpadStatusBarInputConsumer extends DelegateInputConsumer {
private final SystemUiProxy mSystemUiProxy;
private final float mTouchSlop;
private final PointF mDown = new PointF();
private boolean mHasPassedTouchSlop;
- public StatusBarInputConsumer(Context context, InputConsumer delegate,
+ public TrackpadStatusBarInputConsumer(Context context, InputConsumer delegate,
InputMonitorCompat inputMonitor) {
super(delegate, inputMonitor);
@@ -79,7 +79,7 @@
private void dispatchTouchEvent(MotionEvent ev) {
if (mSystemUiProxy.isActive()) {
- mSystemUiProxy.onStatusBarMotionEvent(ev);
+ mSystemUiProxy.onStatusBarTrackpadEvent(ev);
}
}
}
diff --git a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
index 2eaff46..42b0edf 100644
--- a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
+++ b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
@@ -15,10 +15,10 @@
*/
package com.android.quickstep.interaction;
+import static com.android.app.animation.Interpolators.FAST_OUT_SLOW_IN;
+import static com.android.app.animation.Interpolators.LINEAR;
import static com.android.launcher3.Utilities.mapBoundToRange;
import static com.android.launcher3.Utilities.mapRange;
-import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.quickstep.OverviewComponentObserver.startHomeIntentSafely;
import android.animation.Animator;
@@ -96,8 +96,6 @@
private static final float ANIMATION_PAUSE_ALPHA_THRESHOLD = 0.1f;
private TISBindHelper mTISBindHelper;
- private TISBinder mBinder;
- @Nullable private TaskbarManager mTaskbarManager = null;
private final AnimatedFloat mSwipeProgress = new AnimatedFloat(this::onSwipeProgressUpdate);
private BgDrawable mBackground;
@@ -233,27 +231,27 @@
}
private void setSetupUIVisible(boolean visible) {
- if (mBinder == null || mTaskbarManager == null) return;
- mTaskbarManager.setSetupUIVisible(visible);
+ TaskbarManager taskbarManager = mTISBindHelper.getTaskbarManager();
+ if (taskbarManager == null) return;
+ taskbarManager.setSetupUIVisible(visible);
}
@Override
protected void onResume() {
super.onResume();
maybeResumeOrPauseBackgroundAnimation();
- if (mBinder != null) {
+ TISBinder binder = mTISBindHelper.getBinder();
+ if (binder != null) {
setSetupUIVisible(true);
- mBinder.setSwipeUpProxy(this::createSwipeUpProxy);
+ binder.setSwipeUpProxy(this::createSwipeUpProxy);
}
}
private void onTISConnected(TISBinder binder) {
- mBinder = binder;
- mTaskbarManager = mBinder.getTaskbarManager();
setSetupUIVisible(isResumed());
- mBinder.setSwipeUpProxy(isResumed() ? this::createSwipeUpProxy : null);
- mBinder.setOverviewTargetChangeListener(mBinder::preloadOverviewForSUWAllSet);
- mBinder.preloadOverviewForSUWAllSet();
+ binder.setSwipeUpProxy(isResumed() ? this::createSwipeUpProxy : null);
+ binder.setOverviewTargetChangeListener(binder::preloadOverviewForSUWAllSet);
+ binder.preloadOverviewForSUWAllSet();
}
@Override
@@ -268,10 +266,11 @@
}
private void clearBinderOverride() {
- if (mBinder != null) {
+ TISBinder binder = mTISBindHelper.getBinder();
+ if (binder != null) {
setSetupUIVisible(false);
- mBinder.setSwipeUpProxy(null);
- mBinder.setOverviewTargetChangeListener(null);
+ binder.setSwipeUpProxy(null);
+ binder.setOverviewTargetChangeListener(null);
}
}
@@ -328,8 +327,9 @@
mRootView.setAlpha(alpha);
mRootView.setTranslationY((alpha - 1) * mSwipeUpShift);
- if (mLauncherStartAnim == null && mTaskbarManager != null) {
- mLauncherStartAnim = mTaskbarManager.createLauncherStartFromSuwAnim(MAX_SWIPE_DURATION);
+ TaskbarManager taskbarManager = mTISBindHelper.getTaskbarManager();
+ if (mLauncherStartAnim == null && taskbarManager != null) {
+ mLauncherStartAnim = taskbarManager.createLauncherStartFromSuwAnim(MAX_SWIPE_DURATION);
}
if (mLauncherStartAnim != null) {
mLauncherStartAnim.setPlayFraction(Utilities.mapBoundToRange(
diff --git a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
index 5d25279..135cb72 100644
--- a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
@@ -23,9 +23,9 @@
import android.graphics.PointF;
import android.view.View;
+import com.android.app.animation.Interpolators;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
-import com.android.launcher3.anim.Interpolators;
import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureResult;
import com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult;
import com.android.quickstep.util.LottieAnimationColorUtils;
@@ -183,7 +183,7 @@
/* upperBound = */ 1f,
/* toMin = */ 1f,
/* toMax = */ EXITING_APP_MIN_SIZE_PERCENTAGE,
- Interpolators.DEACCEL);
+ Interpolators.DECELERATE);
// shrink the exiting app as we progress through the back gesture
mExitingAppView.setPivotX(isLeftGesture ? mScreenWidth : 0);
@@ -197,7 +197,7 @@
/* upperBound = */ 1f,
/* toMin = */ 0,
/* toMax = */ mExitingAppMargin,
- Interpolators.DEACCEL)
+ Interpolators.DECELERATE)
* (isLeftGesture ? -1 : 1));
// round the corners of the exiting app as we progress through the back gesture
diff --git a/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java b/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java
index 8eb4059..a9dcad8 100644
--- a/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java
+++ b/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java
@@ -40,8 +40,8 @@
import androidx.dynamicanimation.animation.SpringAnimation;
import androidx.dynamicanimation.animation.SpringForce;
+import com.android.app.animation.Interpolators;
import com.android.launcher3.R;
-import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.testing.shared.ResourceUtils;
import com.android.launcher3.util.VibratorWrapper;
diff --git a/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java b/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
index 62726a0..2189a24 100644
--- a/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
+++ b/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
@@ -64,7 +64,6 @@
private View mRotationPrompt;
private TISBindHelper mTISBindHelper;
- private TISBinder mBinder;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -321,7 +320,6 @@
}
private void onTISConnected(TISBinder binder) {
- mBinder = binder;
updateServiceState(isResumed());
}
@@ -332,8 +330,9 @@
}
private void updateServiceState(boolean isEnabled) {
- if (mBinder != null) {
- mBinder.setGestureBlockedTaskId(isEnabled ? getTaskId() : -1);
+ TISBinder binder = mTISBindHelper.getBinder();
+ if (binder != null) {
+ binder.setGestureBlockedTaskId(isEnabled ? getTaskId() : -1);
}
}
diff --git a/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java b/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
index 6cee690..63e41d5 100644
--- a/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
+++ b/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
@@ -65,7 +65,7 @@
mDisplaySize.set(currentSize.x, currentSize.y);
mSwipeUpTouchTracker =
new TriggerSwipeUpTouchTracker(context, true /*disableHorizontalSwipe*/,
- new NavBarPosition(NavigationMode.NO_BUTTON, displayRotation),
+ new NavBarPosition(mContext, NavigationMode.NO_BUTTON, displayRotation),
null /*onInterceptTouch*/, this);
mMotionPauseDetector = new MotionPauseDetector(context);
diff --git a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
index 454dd17..75b80b3 100644
--- a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
@@ -15,7 +15,7 @@
*/
package com.android.quickstep.interaction;
-import static com.android.launcher3.anim.Interpolators.ACCEL;
+import static com.android.app.animation.Interpolators.ACCELERATE;
import static com.android.launcher3.config.FeatureFlags.ENABLE_NEW_GESTURE_NAV_TUTORIAL;
import android.animation.Animator;
@@ -254,7 +254,7 @@
public void animateTaskViewToOverview(boolean animateDelayedSuccessFeedback) {
PendingAnimation anim = new PendingAnimation(TASK_VIEW_END_ANIMATION_DURATION_MILLIS);
anim.setFloat(mTaskViewSwipeUpAnimation
- .getCurrentShift(), AnimatedFloat.VALUE, 1, ACCEL);
+ .getCurrentShift(), AnimatedFloat.VALUE, 1, ACCELERATE);
if (animateDelayedSuccessFeedback) {
anim.addListener(new AnimatorListenerAdapter() {
diff --git a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
index 558d5dc..d0d7534 100644
--- a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
@@ -18,7 +18,7 @@
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
-import static com.android.launcher3.anim.Interpolators.ACCEL;
+import static com.android.app.animation.Interpolators.ACCELERATE;
import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
import static com.android.quickstep.AbsSwipeUpHandler.MAX_SWIPE_DURATION;
@@ -171,14 +171,14 @@
PendingAnimation anim = new PendingAnimation(300);
if (toOverviewFirst) {
anim.setFloat(mTaskViewSwipeUpAnimation
- .getCurrentShift(), AnimatedFloat.VALUE, 1, ACCEL);
+ .getCurrentShift(), AnimatedFloat.VALUE, 1, ACCELERATE);
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation, boolean isReverse) {
PendingAnimation fadeAnim =
new PendingAnimation(TASK_VIEW_END_ANIMATION_DURATION_MILLIS);
fadeAnim.setFloat(mTaskViewSwipeUpAnimation
- .getCurrentShift(), AnimatedFloat.VALUE, 0, ACCEL);
+ .getCurrentShift(), AnimatedFloat.VALUE, 0, ACCELERATE);
if (resetViews) {
fadeAnim.addListener(mResetTaskView);
}
@@ -213,7 +213,7 @@
});
} else {
anim.setFloat(mTaskViewSwipeUpAnimation
- .getCurrentShift(), AnimatedFloat.VALUE, 0, ACCEL);
+ .getCurrentShift(), AnimatedFloat.VALUE, 0, ACCELERATE);
if (resetViews) {
anim.addListener(mResetTaskView);
}
@@ -239,8 +239,8 @@
mFakeTaskView.setVisibility(View.VISIBLE);
PendingAnimation anim = new PendingAnimation(300);
anim.setFloat(mTaskViewSwipeUpAnimation
- .getCurrentShift(), AnimatedFloat.VALUE, 0, ACCEL);
- anim.setViewAlpha(mFakeTaskView, 1, ACCEL);
+ .getCurrentShift(), AnimatedFloat.VALUE, 0, ACCELERATE);
+ anim.setViewAlpha(mFakeTaskView, 1, ACCELERATE);
anim.addListener(mResetTaskView);
AnimatorSet animset = anim.buildAnim();
if (animateTaskbar) {
@@ -260,7 +260,7 @@
mTaskViewSwipeUpAnimation.handleSwipeUpToHome(finalVelocity);
// After home animation finishes, fade out and run onEndRunnable.
PendingAnimation fadeAnim = new PendingAnimation(300);
- fadeAnim.setViewAlpha(mFakeIconView, 0, ACCEL);
+ fadeAnim.setViewAlpha(mFakeIconView, 0, ACCELERATE);
final View hotseatIconView = mHotseatIconView;
if (hotseatIconView != null) {
hotseatIconView.setVisibility(INVISIBLE);
diff --git a/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java b/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java
index 409bf9c..cca4d52 100644
--- a/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java
+++ b/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java
@@ -18,12 +18,9 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.launcher3.config.FeatureFlags;
-
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Locale;
@@ -150,10 +147,6 @@
lastEventEntries.add(eventEntry);
}
- public void clear() {
- Arrays.fill(logs, null);
- }
-
public void dump(String prefix, PrintWriter writer) {
writer.println(prefix + "ActiveGestureErrorDetector:");
for (int i = 0; i < logs.length; i++) {
diff --git a/quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java b/quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java
index a92ab2a..cb35ec8 100644
--- a/quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java
+++ b/quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java
@@ -15,9 +15,9 @@
*/
package com.android.quickstep.util;
-import static com.android.launcher3.anim.Interpolators.DEACCEL;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.quickstep.AbsSwipeUpHandler.ALL_APPS_SHIFT_THRESHOLD;
+import static com.android.app.animation.Interpolators.DECELERATE;
+import static com.android.app.animation.Interpolators.LINEAR;
+import static com.android.launcher3.LauncherPrefs.ALL_APPS_OVERVIEW_THRESHOLD;
import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION;
@@ -34,6 +34,7 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.LauncherState;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorPlaybackController;
@@ -55,7 +56,7 @@
private enum RecentsResistanceParams {
FROM_APP(0.75f, 0.5f, 1f, false),
- FROM_APP_TO_ALL_APPS(0.75f, 0.5f, 0.8f, false),
+ FROM_APP_TO_ALL_APPS(1f, 0.6f, 0.8f, false),
FROM_APP_TABLET(1f, 0.7f, 1f, true),
FROM_APP_TO_ALL_APPS_TABLET(1f, 0.5f, 0.5f, false),
FROM_OVERVIEW(1f, 0.75f, 0.5f, false);
@@ -91,7 +92,7 @@
public final boolean stopScalingAtTop;
}
- private static final TimeInterpolator RECENTS_SCALE_RESIST_INTERPOLATOR = DEACCEL;
+ private static final TimeInterpolator RECENTS_SCALE_RESIST_INTERPOLATOR = DECELERATE;
private static final TimeInterpolator RECENTS_TRANSLATE_RESIST_INTERPOLATOR = LINEAR;
private static final Rect TEMP_RECT = new Rect();
@@ -188,7 +189,8 @@
recentsOrientedState.getOrientationHandler());
float dragLengthFactor = (float) dp.heightPx / transitionDragLength;
// -1s are because 0-1 is reserved for the normal transition.
- return (ALL_APPS_SHIFT_THRESHOLD - 1) / (dragLengthFactor - 1);
+ float threshold = LauncherPrefs.get(context).get(ALL_APPS_OVERVIEW_THRESHOLD) / 100f;
+ return (threshold - 1) / (dragLengthFactor - 1);
}
/**
diff --git a/quickstep/src/com/android/quickstep/util/BinderTracker.java b/quickstep/src/com/android/quickstep/util/BinderTracker.java
deleted file mode 100644
index cb04e5b..0000000
--- a/quickstep/src/com/android/quickstep/util/BinderTracker.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.quickstep.util;
-
-import android.os.Binder;
-import android.os.IBinder;
-import android.os.Looper;
-import android.util.Log;
-
-import com.android.launcher3.config.FeatureFlags;
-
-/**
- * Utility class to test and check binder calls during development.
- */
-public class BinderTracker {
-
- private static final String TAG = "BinderTracker";
-
- public static void start() {
- if (!FeatureFlags.IS_STUDIO_BUILD) {
- Log.wtf(TAG, "Accessing tracker in released code.", new Exception());
- return;
- }
-
- Binder.setProxyTransactListener(new Tracker());
- }
-
- public static void stop() {
- if (!FeatureFlags.IS_STUDIO_BUILD) {
- Log.wtf(TAG, "Accessing tracker in released code.", new Exception());
- return;
- }
- Binder.setProxyTransactListener(null);
- }
-
- private static class Tracker implements Binder.ProxyTransactListener {
-
- @Override
- public Object onTransactStarted(IBinder iBinder, int code) {
- if (Looper.myLooper() == Looper.getMainLooper()) {
- Log.e(TAG, "Binder call on ui thread", new Exception());
- }
- return null;
- }
-
- @Override
- public void onTransactEnded(Object session) { }
- }
-}
diff --git a/quickstep/src/com/android/quickstep/util/BorderAnimator.java b/quickstep/src/com/android/quickstep/util/BorderAnimator.java
index 011d45c..7563187 100644
--- a/quickstep/src/com/android/quickstep/util/BorderAnimator.java
+++ b/quickstep/src/com/android/quickstep/util/BorderAnimator.java
@@ -29,9 +29,9 @@
import androidx.annotation.NonNull;
import androidx.annotation.Px;
+import com.android.app.animation.Interpolators;
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.AnimatorListeners;
-import com.android.launcher3.anim.Interpolators;
/**
* Utility class for drawing a rounded-rect border around a view.
diff --git a/quickstep/src/com/android/quickstep/util/GroupTask.java b/quickstep/src/com/android/quickstep/util/GroupTask.java
index 2be4f0a..9c49647 100644
--- a/quickstep/src/com/android/quickstep/util/GroupTask.java
+++ b/quickstep/src/com/android/quickstep/util/GroupTask.java
@@ -37,6 +37,10 @@
@TaskView.Type
public final int taskViewType;
+ public GroupTask(@NonNull Task task) {
+ this(task, null, null);
+ }
+
public GroupTask(@NonNull Task t1, @Nullable Task t2, @Nullable SplitBounds splitBounds) {
this(t1, t2, splitBounds, t2 != null ? TaskView.Type.GROUPED : TaskView.Type.SINGLE);
}
diff --git a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java
index 6d15e8b..e0b5272 100644
--- a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java
+++ b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java
@@ -60,6 +60,8 @@
private final NaturalRotationUnfoldProgressProvider mNaturalOrientationProgressProvider;
private final UnfoldMoveFromCenterHotseatAnimator mUnfoldMoveFromCenterHotseatAnimator;
private final UnfoldMoveFromCenterWorkspaceAnimator mUnfoldMoveFromCenterWorkspaceAnimator;
+ private final TransitionStatusProvider mExternalTransitionStatusProvider =
+ new TransitionStatusProvider();
private PreemptiveUnfoldTransitionProgressProvider mPreemptiveProgressProvider = null;
private Boolean mIsTablet = null;
@@ -88,6 +90,8 @@
unfoldTransitionProgressProvider);
}
+ unfoldTransitionProgressProvider.addCallback(mExternalTransitionStatusProvider);
+
mUnfoldMoveFromCenterHotseatAnimator = new UnfoldMoveFromCenterHotseatAnimator(launcher,
windowManager, rotationChangeProvider);
mUnfoldMoveFromCenterWorkspaceAnimator = new UnfoldMoveFromCenterWorkspaceAnimator(launcher,
@@ -166,11 +170,26 @@
}
if (mIsTablet != null && dp.isTablet != mIsTablet) {
- if (dp.isTablet && SystemUiProxy.INSTANCE.get(mLauncher).isActive()) {
+ // We should preemptively start the animation only if:
+ // - We changed to the unfolded screen
+ // - SystemUI IPC connection is alive, so we won't end up in a situation that we won't
+ // receive transition progress events from SystemUI later because there was no
+ // IPC connection established (e.g. because of SystemUI crash)
+ // - SystemUI has not already sent unfold animation progress events. This might happen
+ // if Launcher was not open during unfold, in this case we receive the configuration
+ // change only after we went back to home screen and we don't want to start the
+ // animation in this case.
+ if (dp.isTablet
+ && SystemUiProxy.INSTANCE.get(mLauncher).isActive()
+ && !mExternalTransitionStatusProvider.hasRun()) {
// Preemptively start the unfold animation to make sure that we have drawn
// the first frame of the animation before the screen gets unblocked
preemptivelyStartAnimationOnNextFrame();
}
+
+ if (!dp.isTablet) {
+ mExternalTransitionStatusProvider.onFolded();
+ }
}
mIsTablet = dp.isTablet;
@@ -222,4 +241,48 @@
HOTSEAT_SCALE_PROPERTY.setValue(mLauncher.getHotseat(), value);
}
}
+
+ /**
+ * Class to track the current status of the external transition provider (the events are coming
+ * from the SystemUI side through IPC), it allows to check if the transition has already
+ * finished or currently running on the SystemUI side since last unfold.
+ */
+ private static class TransitionStatusProvider implements TransitionProgressListener {
+
+ private boolean mHasRun = false;
+
+ @Override
+ public void onTransitionStarted() {
+ markAsRun();
+ }
+
+ @Override
+ public void onTransitionProgress(float progress) {
+ markAsRun();
+ }
+
+ @Override
+ public void onTransitionFinished() {
+ markAsRun();
+ }
+
+ /**
+ * Called when the device is folded, so we can reset the status of the animation
+ */
+ public void onFolded() {
+ mHasRun = false;
+ }
+
+ /**
+ * Returns true if there was an animation already (or it is currently running) after
+ * unfolding the device
+ */
+ public boolean hasRun() {
+ return mHasRun;
+ }
+
+ private void markAsRun() {
+ mHasRun = true;
+ }
+ }
}
diff --git a/quickstep/src/com/android/quickstep/util/NavBarPosition.java b/quickstep/src/com/android/quickstep/util/NavBarPosition.java
index 59c8263..a89e7e3 100644
--- a/quickstep/src/com/android/quickstep/util/NavBarPosition.java
+++ b/quickstep/src/com/android/quickstep/util/NavBarPosition.java
@@ -16,7 +16,9 @@
package com.android.quickstep.util;
import static com.android.launcher3.util.NavigationMode.NO_BUTTON;
+import static com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen;
+import android.content.Context;
import android.view.Surface;
import com.android.launcher3.util.DisplayController.Info;
@@ -27,25 +29,26 @@
*/
public class NavBarPosition {
+ private final boolean mIsLargeScreen;
private final NavigationMode mMode;
private final int mDisplayRotation;
- public NavBarPosition(NavigationMode mode, Info info) {
- mMode = mode;
- mDisplayRotation = info.rotation;
+ public NavBarPosition(Context context, NavigationMode mode, Info info) {
+ this(context, mode, info.rotation);
}
- public NavBarPosition(NavigationMode mode, int displayRotation) {
+ public NavBarPosition(Context context, NavigationMode mode, int displayRotation) {
+ mIsLargeScreen = isLargeScreen(context);
mMode = mode;
mDisplayRotation = displayRotation;
}
public boolean isRightEdge() {
- return mMode != NO_BUTTON && mDisplayRotation == Surface.ROTATION_90;
+ return mMode != NO_BUTTON && mDisplayRotation == Surface.ROTATION_90 && !mIsLargeScreen;
}
public boolean isLeftEdge() {
- return mMode != NO_BUTTON && mDisplayRotation == Surface.ROTATION_270;
+ return mMode != NO_BUTTON && mDisplayRotation == Surface.ROTATION_270 && !mIsLargeScreen;
}
public float getRotation() {
diff --git a/quickstep/src/com/android/quickstep/util/OverviewToSplitTimings.java b/quickstep/src/com/android/quickstep/util/OverviewToSplitTimings.java
index e189a66..3027f79 100644
--- a/quickstep/src/com/android/quickstep/util/OverviewToSplitTimings.java
+++ b/quickstep/src/com/android/quickstep/util/OverviewToSplitTimings.java
@@ -16,8 +16,8 @@
package com.android.quickstep.util;
-import static com.android.launcher3.anim.Interpolators.EMPHASIZED;
-import static com.android.launcher3.anim.Interpolators.INSTANT;
+import static com.android.app.animation.Interpolators.EMPHASIZED;
+import static com.android.app.animation.Interpolators.INSTANT;
import android.view.animation.Interpolator;
diff --git a/quickstep/src/com/android/quickstep/util/PhoneOverviewToSplitTimings.java b/quickstep/src/com/android/quickstep/util/PhoneOverviewToSplitTimings.java
index f1dde53..a38f437 100644
--- a/quickstep/src/com/android/quickstep/util/PhoneOverviewToSplitTimings.java
+++ b/quickstep/src/com/android/quickstep/util/PhoneOverviewToSplitTimings.java
@@ -16,7 +16,7 @@
package com.android.quickstep.util;
-import static com.android.launcher3.anim.Interpolators.EMPHASIZED;
+import static com.android.app.animation.Interpolators.EMPHASIZED;
import android.view.animation.Interpolator;
diff --git a/quickstep/src/com/android/quickstep/util/SplitAnimationTimings.java b/quickstep/src/com/android/quickstep/util/SplitAnimationTimings.java
index 7dc1b32..93f2255 100644
--- a/quickstep/src/com/android/quickstep/util/SplitAnimationTimings.java
+++ b/quickstep/src/com/android/quickstep/util/SplitAnimationTimings.java
@@ -16,7 +16,7 @@
package com.android.quickstep.util;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.app.animation.Interpolators.LINEAR;
import android.view.animation.Interpolator;
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index f25619d..e063b44 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -73,16 +73,18 @@
import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
import com.android.quickstep.RecentsModel;
+import com.android.quickstep.SplitSelectionListener;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TaskAnimationManager;
import com.android.quickstep.TaskViewUtils;
import com.android.quickstep.views.FloatingTaskView;
import com.android.quickstep.views.GroupedTaskView;
-import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
import java.util.function.Consumer;
/**
@@ -138,6 +140,8 @@
private FloatingTaskView mFirstFloatingTaskView;
+ private final List<SplitSelectionListener> mSplitSelectionListeners = new ArrayList<>();
+
public SplitSelectStateController(Context context, Handler handler, StateManager stateManager,
DepthController depthController, StatsLogManager statsLogManager,
SystemUiProxy systemUiProxy, RecentsModel recentsModel) {
@@ -248,6 +252,27 @@
}
/**
+ * Listener will only get callbacks going forward from the point of registration. No
+ * methods will be fired upon registering.
+ */
+ public void registerSplitListener(@NonNull SplitSelectionListener listener) {
+ if (mSplitSelectionListeners.contains(listener)) {
+ return;
+ }
+ mSplitSelectionListeners.add(listener);
+ }
+
+ public void unregisterSplitListener(@NonNull SplitSelectionListener listener) {
+ mSplitSelectionListeners.remove(listener);
+ }
+
+ private void dispatchOnSplitSelectionExit() {
+ for (SplitSelectionListener listener : mSplitSelectionListeners) {
+ listener.onSplitSelectionExit(false);
+ }
+ }
+
+ /**
* To be called when the actual tasks ({@link #mInitialTaskId}, {@link #mSecondTaskId}) are
* to be launched. Call after launcher side animations are complete.
*/
@@ -790,12 +815,16 @@
}
/**
- * To be called if split select was cancelled
+ * To be called whenever we exit split selection state. If
+ * {@link FeatureFlags#ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE} is set, this should be the
+ * central way split is getting reset, which should then go through the callbacks to reset
+ * other state.
*/
public void resetState() {
if (FeatureFlags.ENABLE_SPLIT_LAUNCH_DATA_REFACTOR.get()) {
mSplitSelectDataHolder.resetState();
}
+ dispatchOnSplitSelectionExit();
mInitialTaskId = INVALID_TASK_ID;
mInitialTaskIntent = null;
mSecondTaskId = INVALID_TASK_ID;
@@ -810,6 +839,7 @@
mAnimateCurrentTaskDismissal = false;
mDismissingFromSplitPair = false;
mSecondPendingIntent = null;
+ mFirstFloatingTaskView = null;
}
/**
diff --git a/quickstep/src/com/android/quickstep/util/SplitToConfirmTimings.java b/quickstep/src/com/android/quickstep/util/SplitToConfirmTimings.java
index f5b00cf..d1ec2b6 100644
--- a/quickstep/src/com/android/quickstep/util/SplitToConfirmTimings.java
+++ b/quickstep/src/com/android/quickstep/util/SplitToConfirmTimings.java
@@ -16,7 +16,7 @@
package com.android.quickstep.util;
-import static com.android.launcher3.anim.Interpolators.EMPHASIZED;
+import static com.android.app.animation.Interpolators.EMPHASIZED;
import android.view.animation.Interpolator;
diff --git a/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java b/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
index cd5edab..6b3199f 100644
--- a/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
+++ b/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
@@ -15,11 +15,11 @@
*/
package com.android.quickstep.util;
+import static com.android.app.animation.Interpolators.LINEAR;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
import static com.android.launcher3.states.StateAnimationConfig.SKIP_DEPTH_CONTROLLER;
import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
@@ -177,8 +177,8 @@
addDepthAnimationForState(launcher, NORMAL, duration);
- mAnimators.play(launcher.getRootView().getSysUiScrim().createSysuiMultiplierAnim(0f, 1f)
- .setDuration(duration));
+ mAnimators.play(launcher.getRootView().getSysUiScrim().getSysUIMultiplier()
+ .animateToValue(0f, 1f).setDuration(duration));
}
private void addAnimationForPage(CellLayout page, int totalRows, long duration) {
diff --git a/quickstep/src/com/android/quickstep/util/SystemWindowManagerProxy.java b/quickstep/src/com/android/quickstep/util/SystemWindowManagerProxy.java
index 9808b28..5f7d694 100644
--- a/quickstep/src/com/android/quickstep/util/SystemWindowManagerProxy.java
+++ b/quickstep/src/com/android/quickstep/util/SystemWindowManagerProxy.java
@@ -29,6 +29,7 @@
import com.android.launcher3.util.window.CachedDisplayInfo;
import com.android.launcher3.util.window.WindowManagerProxy;
+import java.util.List;
import java.util.Set;
/**
@@ -54,16 +55,16 @@
}
@Override
- public ArrayMap<CachedDisplayInfo, WindowBounds[]> estimateInternalDisplayBounds(
+ public ArrayMap<CachedDisplayInfo, List<WindowBounds>> estimateInternalDisplayBounds(
Context displayInfoContext) {
- ArrayMap<CachedDisplayInfo, WindowBounds[]> result = new ArrayMap<>();
+ ArrayMap<CachedDisplayInfo, List<WindowBounds>> result = new ArrayMap<>();
WindowManager windowManager = displayInfoContext.getSystemService(WindowManager.class);
Set<WindowMetrics> possibleMaximumWindowMetrics =
windowManager.getPossibleMaximumWindowMetrics(DEFAULT_DISPLAY);
FileLog.d("b/283944974", "possibleMaximumWindowMetrics: " + possibleMaximumWindowMetrics);
for (WindowMetrics windowMetrics : possibleMaximumWindowMetrics) {
CachedDisplayInfo info = getDisplayInfo(windowMetrics, Surface.ROTATION_0);
- WindowBounds[] bounds = estimateWindowBounds(displayInfoContext, info);
+ List<WindowBounds> bounds = estimateWindowBounds(displayInfoContext, info);
result.put(info, bounds);
}
return result;
diff --git a/quickstep/src/com/android/quickstep/util/TISBindHelper.java b/quickstep/src/com/android/quickstep/util/TISBindHelper.java
index 7b122c6..ddc796f 100644
--- a/quickstep/src/com/android/quickstep/util/TISBindHelper.java
+++ b/quickstep/src/com/android/quickstep/util/TISBindHelper.java
@@ -23,6 +23,10 @@
import android.os.IBinder;
import android.util.Log;
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.taskbar.TaskbarManager;
+import com.android.quickstep.OverviewCommandHelper;
import com.android.quickstep.TouchInteractionService;
import com.android.quickstep.TouchInteractionService.TISBinder;
@@ -50,6 +54,7 @@
private short mConnectionAttempts;
private boolean mTisServiceBound;
private boolean mIsConnected;
+ @Nullable private TISBinder mBinder;
public TISBindHelper(Context context, Consumer<TISBinder> connectionCallback) {
mContext = context;
@@ -70,7 +75,8 @@
Log.d(TAG, "TIS service connected");
mIsConnected = true;
- mConnectionCallback.accept((TISBinder) iBinder);
+ mBinder = (TISBinder) iBinder;
+ mConnectionCallback.accept(mBinder);
// Flush the pending callbacks
for (Runnable r : mPendingConnectedCallbacks) {
r.run();
@@ -80,7 +86,11 @@
}
@Override
- public void onServiceDisconnected(ComponentName componentName) { }
+ public void onServiceDisconnected(ComponentName componentName) {
+ Log.d(TAG, "TIS service disconnected");
+ mBinder = null;
+ mIsConnected = false;
+ }
@Override
public void onBindingDied(ComponentName name) {
@@ -88,6 +98,21 @@
internalBindToTIS();
}
+ @Nullable
+ public TISBinder getBinder() {
+ return mBinder;
+ }
+
+ @Nullable
+ public TaskbarManager getTaskbarManager() {
+ return mBinder == null ? null : mBinder.getTaskbarManager();
+ }
+
+ @Nullable
+ public OverviewCommandHelper getOverviewCommandHelper() {
+ return mBinder == null ? null : mBinder.getOverviewCommandHelper();
+ }
+
/**
* Runs the given {@param r} runnable when the service is connected.
*/
@@ -139,6 +164,7 @@
public void onDestroy() {
internalUnbindToTIS();
resetServiceBindRetryState();
+ mBinder = null;
mIsConnected = false;
mPendingConnectedCallbacks.clear();
}
diff --git a/quickstep/src/com/android/quickstep/util/TabletHomeToSplitTimings.java b/quickstep/src/com/android/quickstep/util/TabletHomeToSplitTimings.java
index bf8612a..8804049 100644
--- a/quickstep/src/com/android/quickstep/util/TabletHomeToSplitTimings.java
+++ b/quickstep/src/com/android/quickstep/util/TabletHomeToSplitTimings.java
@@ -16,7 +16,7 @@
package com.android.quickstep.util;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.app.animation.Interpolators.LINEAR;
import android.view.animation.Interpolator;
diff --git a/quickstep/src/com/android/quickstep/util/TabletOverviewToSplitTimings.java b/quickstep/src/com/android/quickstep/util/TabletOverviewToSplitTimings.java
index cbf46bf..5463d84 100644
--- a/quickstep/src/com/android/quickstep/util/TabletOverviewToSplitTimings.java
+++ b/quickstep/src/com/android/quickstep/util/TabletOverviewToSplitTimings.java
@@ -16,7 +16,7 @@
package com.android.quickstep.util;
-import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
+import static com.android.app.animation.Interpolators.DECELERATE_2;
import android.view.animation.Interpolator;
@@ -36,8 +36,8 @@
public int getGridSlideDuration() { return 500; }
public int getDuration() { return TABLET_ENTER_DURATION; }
- public Interpolator getStagedRectXInterpolator() { return DEACCEL_2; }
- public Interpolator getStagedRectYInterpolator() { return DEACCEL_2; }
- public Interpolator getStagedRectScaleXInterpolator() { return DEACCEL_2; }
- public Interpolator getStagedRectScaleYInterpolator() { return DEACCEL_2; }
+ public Interpolator getStagedRectXInterpolator() { return DECELERATE_2; }
+ public Interpolator getStagedRectYInterpolator() { return DECELERATE_2; }
+ public Interpolator getStagedRectScaleXInterpolator() { return DECELERATE_2; }
+ public Interpolator getStagedRectScaleYInterpolator() { return DECELERATE_2; }
}
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index 303a528..48dadd1 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -112,8 +112,7 @@
mContext = context;
mSizeStrategy = sizeStrategy;
- // TODO(b/187074722): Don't create this per-TaskViewSimulator
- mOrientationState = TraceHelper.allowIpcs("",
+ mOrientationState = TraceHelper.allowIpcs("TaskViewSimulator.init",
() -> new RecentsOrientedState(context, sizeStrategy, i -> { }));
mOrientationState.setGestureActive(true);
mCurrentFullscreenParams = new FullscreenDrawParams(context);
diff --git a/quickstep/src/com/android/quickstep/util/TransformParams.java b/quickstep/src/com/android/quickstep/util/TransformParams.java
index 0f20e43..1cbded6 100644
--- a/quickstep/src/com/android/quickstep/util/TransformParams.java
+++ b/quickstep/src/com/android/quickstep/util/TransformParams.java
@@ -21,8 +21,8 @@
import android.util.FloatProperty;
import android.view.RemoteAnimationTarget;
+import com.android.app.animation.Interpolators;
import com.android.launcher3.Utilities;
-import com.android.launcher3.anim.Interpolators;
import com.android.quickstep.RemoteAnimationTargets;
import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties;
@@ -153,7 +153,8 @@
// Fade out Assistant overlay.
if (activityType == ACTIVITY_TYPE_ASSISTANT && app.isNotInRecents) {
float progress = Utilities.boundToRange(getProgress(), 0, 1);
- builder.setAlpha(1 - Interpolators.DEACCEL_2_5.getInterpolation(progress));
+ builder.setAlpha(1 - Interpolators.DECELERATE_QUINT
+ .getInterpolation(progress));
} else {
builder.setAlpha(getTargetAlpha());
}
diff --git a/quickstep/src/com/android/quickstep/util/WorkspaceRevealAnim.java b/quickstep/src/com/android/quickstep/util/WorkspaceRevealAnim.java
index 34fa7f1..0a97793 100644
--- a/quickstep/src/com/android/quickstep/util/WorkspaceRevealAnim.java
+++ b/quickstep/src/com/android/quickstep/util/WorkspaceRevealAnim.java
@@ -32,11 +32,11 @@
import android.util.FloatProperty;
import android.view.View;
+import com.android.app.animation.Interpolators;
import com.android.launcher3.Hotseat;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.Workspace;
-import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.states.StateAnimationConfig;
@@ -92,7 +92,8 @@
}
// Add sysui scrim animation.
- mAnimators.play(launcher.getRootView().getSysUiScrim().createSysuiMultiplierAnim(0f, 1f));
+ mAnimators.play(launcher.getRootView().getSysUiScrim()
+ .getSysUIMultiplier().animateToValue(0f, 1f));
mAnimators.setDuration(DURATION_MS);
mAnimators.setInterpolator(Interpolators.DECELERATED_EASE);
diff --git a/quickstep/src/com/android/quickstep/views/AllAppsEduView.java b/quickstep/src/com/android/quickstep/views/AllAppsEduView.java
index 716d389..fdc8f1f 100644
--- a/quickstep/src/com/android/quickstep/views/AllAppsEduView.java
+++ b/quickstep/src/com/android/quickstep/views/AllAppsEduView.java
@@ -15,12 +15,12 @@
*/
package com.android.quickstep.views;
+import static com.android.app.animation.Interpolators.FAST_OUT_SLOW_IN;
+import static com.android.app.animation.Interpolators.LINEAR;
+import static com.android.app.animation.Interpolators.OVERSHOOT_1_7;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
-import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_7;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALL_APPS_EDU_SHOWN;
import android.animation.Animator;
diff --git a/quickstep/src/com/android/quickstep/views/ClearAllButton.java b/quickstep/src/com/android/quickstep/views/ClearAllButton.java
index 6813857..19ac1f8 100644
--- a/quickstep/src/com/android/quickstep/views/ClearAllButton.java
+++ b/quickstep/src/com/android/quickstep/views/ClearAllButton.java
@@ -16,6 +16,8 @@
package com.android.quickstep.views;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_GRID_ONLY_OVERVIEW;
+
import android.content.Context;
import android.util.AttributeSet;
import android.util.FloatProperty;
@@ -248,8 +250,15 @@
*/
private float getOriginalTranslationY() {
DeviceProfile deviceProfile = mActivity.getDeviceProfile();
- return deviceProfile.isTablet
- ? deviceProfile.overviewRowSpacing
- : deviceProfile.overviewTaskThumbnailTopMarginPx / 2.0f;
+ if (deviceProfile.isTablet) {
+ if (ENABLE_GRID_ONLY_OVERVIEW.get()) {
+ return (getRecentsView().getLastComputedTaskSize().height()
+ + deviceProfile.overviewTaskThumbnailTopMarginPx) / 2.0f
+ + deviceProfile.overviewRowSpacing;
+ } else {
+ return deviceProfile.overviewRowSpacing;
+ }
+ }
+ return deviceProfile.overviewTaskThumbnailTopMarginPx / 2.0f;
}
}
diff --git a/quickstep/src/com/android/quickstep/views/DesktopAppSelectView.java b/quickstep/src/com/android/quickstep/views/DesktopAppSelectView.java
new file mode 100644
index 0000000..a5be142
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/DesktopAppSelectView.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.views;
+
+import static com.android.app.animation.Interpolators.EMPHASIZED_DECELERATE;
+import static com.android.app.animation.Interpolators.LINEAR;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.LinearLayout;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+
+/**
+ * Floating view show on launcher home screen that notifies the user that an app will be launched to
+ * the desktop.
+ */
+public class DesktopAppSelectView extends LinearLayout {
+
+ private static final int SHOW_INITIAL_HEIGHT_DP = 7;
+ private static final int SHOW_CONTAINER_SCALE_DURATION = 333;
+ private static final int SHOW_CONTAINER_ALPHA_DURATION = 83;
+ private static final int SHOW_CONTENT_ALPHA_DELAY = 67;
+ private static final int SHOW_CONTENT_ALPHA_DURATION = 83;
+ private static final int HIDE_DURATION = 83;
+
+ private final Launcher mLauncher;
+
+ private View mText;
+ private View mCloseButton;
+ @Nullable
+ private Runnable mOnCloseCallback;
+ private AnimatorSet mShowAnimation;
+ private Animator mHideAnimation;
+
+ public DesktopAppSelectView(Context context) {
+ this(context, null);
+ }
+
+ public DesktopAppSelectView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public DesktopAppSelectView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public DesktopAppSelectView(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ mLauncher = Launcher.getLauncher(context);
+ }
+
+ /**
+ * Show the popup on launcher home screen
+ *
+ * @param onCloseCallback optional callback that is called when user clicks the close button
+ * @return the created view
+ */
+ public static DesktopAppSelectView show(Launcher launcher, @Nullable Runnable onCloseCallback) {
+ DesktopAppSelectView view = (DesktopAppSelectView) launcher.getLayoutInflater().inflate(
+ R.layout.floating_desktop_app_select, launcher.getDragLayer(), false);
+ view.setOnCloseClickCallback(onCloseCallback);
+ view.show();
+ return view;
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mText = findViewById(R.id.desktop_app_select_text);
+ mCloseButton = findViewById(R.id.close_button);
+ mCloseButton.setOnClickListener(v -> {
+ if (mHideAnimation == null) {
+ hide();
+ if (mOnCloseCallback != null) {
+ mOnCloseCallback.run();
+ }
+ }
+ });
+ }
+
+ private void show() {
+ mLauncher.getDragLayer().addView(this);
+
+ // Set up initial values
+ getBackground().setAlpha(0);
+ mText.setAlpha(0);
+ mCloseButton.setAlpha(0);
+ int initialHeightPx = Utilities.dpToPx(SHOW_INITIAL_HEIGHT_DP);
+ int finalHeight = getResources().getDimensionPixelSize(
+ R.dimen.desktop_mode_floating_app_select_height);
+ float initialScale = initialHeightPx / (float) finalHeight;
+ setScaleY(initialScale);
+ setPivotY(0);
+
+ // Animate the container
+ ValueAnimator containerBackground = ValueAnimator.ofInt(0, 255);
+ containerBackground.addUpdateListener(
+ animation -> getBackground().setAlpha((Integer) animation.getAnimatedValue()));
+ containerBackground.setDuration(SHOW_CONTAINER_ALPHA_DURATION);
+ containerBackground.setInterpolator(LINEAR);
+
+ ObjectAnimator containerSize = ObjectAnimator.ofFloat(this, SCALE_Y, 1f);
+ containerSize.setDuration(SHOW_CONTAINER_SCALE_DURATION);
+ containerSize.setInterpolator(EMPHASIZED_DECELERATE);
+
+ // Animate the contents
+ ObjectAnimator textAlpha = ObjectAnimator.ofFloat(mText, ALPHA, 1);
+ ObjectAnimator buttonAlpha = ObjectAnimator.ofFloat(mCloseButton, ALPHA, 1);
+ AnimatorSet contentAlpha = new AnimatorSet();
+ contentAlpha.playTogether(textAlpha, buttonAlpha);
+ contentAlpha.setStartDelay(SHOW_CONTENT_ALPHA_DELAY);
+ contentAlpha.setDuration(SHOW_CONTENT_ALPHA_DURATION);
+ contentAlpha.setInterpolator(LINEAR);
+
+ // Start the animation
+ mShowAnimation = new AnimatorSet();
+ mShowAnimation.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ mShowAnimation = null;
+ }
+ });
+ mShowAnimation.playTogether(containerBackground, containerSize, contentAlpha);
+ mShowAnimation.start();
+ }
+
+ /**
+ * Hide the floating view
+ */
+ public void hide() {
+ if (mShowAnimation != null) {
+ mShowAnimation.cancel();
+ }
+ mHideAnimation = ObjectAnimator.ofFloat(this, ALPHA, 0);
+ mHideAnimation.setDuration(HIDE_DURATION).setInterpolator(LINEAR);
+ mHideAnimation.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ mLauncher.getDragLayer().removeView(DesktopAppSelectView.this);
+ mHideAnimation = null;
+ }
+ });
+ mHideAnimation.start();
+ }
+
+ /**
+ * Add a callback that is called when close button is clicked
+ */
+ public void setOnCloseClickCallback(@Nullable Runnable callback) {
+ mOnCloseCallback = callback;
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
index 75a8ea2..a5652dc 100644
--- a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
@@ -1,8 +1,8 @@
package com.android.quickstep.views;
+import static com.android.app.animation.Interpolators.LINEAR;
+import static com.android.app.animation.Interpolators.clampToProgress;
import static com.android.launcher3.AbstractFloatingView.TYPE_TASK_MENU;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.anim.Interpolators.clampToProgress;
import android.animation.ValueAnimator;
import android.content.Context;
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index 4dbf4e3..9e9b22f 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -37,6 +37,7 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.LauncherState;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statehandlers.DesktopVisibilityController;
@@ -81,7 +82,7 @@
}
@Override
- public void startHome(boolean animated) {
+ protected void handleStartHome(boolean animated) {
StateManager stateManager = mActivity.getStateManager();
animated &= stateManager.shouldAnimateStateChange();
stateManager.goToState(NORMAL, animated);
@@ -89,6 +90,11 @@
}
@Override
+ public boolean isCommandQueueEmpty() {
+ return mActivity.isCommandQueueEmpty();
+ }
+
+ @Override
protected void onTaskLaunchAnimationEnd(boolean success) {
if (success) {
mActivity.getStateManager().moveToRestState();
@@ -218,7 +224,11 @@
@Override
protected boolean canLaunchFullscreenTask() {
- return !mActivity.isInState(OVERVIEW_SPLIT_SELECT);
+ if (FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
+ return !mSplitSelectStateController.isSplitSelectActive();
+ } else {
+ return !mActivity.isInState(OVERVIEW_SPLIT_SELECT);
+ }
}
@Override
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 55b9f95..f0daf8d 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -21,6 +21,16 @@
import static android.view.View.MeasureSpec.EXACTLY;
import static android.view.View.MeasureSpec.makeMeasureSpec;
+import static com.android.app.animation.Interpolators.ACCELERATE;
+import static com.android.app.animation.Interpolators.ACCELERATE_0_75;
+import static com.android.app.animation.Interpolators.ACCELERATE_DECELERATE;
+import static com.android.app.animation.Interpolators.DECELERATE_2;
+import static com.android.app.animation.Interpolators.EMPHASIZED_DECELERATE;
+import static com.android.app.animation.Interpolators.FAST_OUT_SLOW_IN;
+import static com.android.app.animation.Interpolators.FINAL_FRAME;
+import static com.android.app.animation.Interpolators.LINEAR;
+import static com.android.app.animation.Interpolators.OVERSHOOT_0_75;
+import static com.android.app.animation.Interpolators.clampToProgress;
import static com.android.launcher3.AbstractFloatingView.TYPE_TASK_MENU;
import static com.android.launcher3.AbstractFloatingView.getTopOpenViewWithType;
import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS;
@@ -32,16 +42,6 @@
import static com.android.launcher3.Utilities.mapToRange;
import static com.android.launcher3.Utilities.squaredHypot;
import static com.android.launcher3.Utilities.squaredTouchSlop;
-import static com.android.launcher3.anim.Interpolators.ACCEL;
-import static com.android.launcher3.anim.Interpolators.ACCEL_0_75;
-import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
-import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
-import static com.android.launcher3.anim.Interpolators.EMPHASIZED_DECELERATE;
-import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
-import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.anim.Interpolators.OVERSHOOT_0_75;
-import static com.android.launcher3.anim.Interpolators.clampToProgress;
import static com.android.launcher3.config.FeatureFlags.ENABLE_GRID_ONLY_OVERVIEW;
import static com.android.launcher3.config.FeatureFlags.ENABLE_LAUNCH_FROM_STAGED_APP;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_OVERVIEW_ACTIONS_SPLIT;
@@ -173,6 +173,7 @@
import com.android.quickstep.RemoteTargetGluer;
import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
import com.android.quickstep.RotationTouchHelper;
+import com.android.quickstep.SplitSelectionListener;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TaskOverlayFactory;
import com.android.quickstep.TaskThumbnailCache;
@@ -211,6 +212,7 @@
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.stream.Collectors;
@@ -661,7 +663,8 @@
/**
* Placeholder view indicating where the first split screen selected app will be placed
*/
- private SplitSelectStateController mSplitSelectStateController;
+ protected SplitSelectStateController mSplitSelectStateController;
+
/**
* The first task that split screen selection was initiated with. When split select state is
* initialized, we create a
@@ -684,6 +687,19 @@
@Nullable
private SplitSelectSource mSplitSelectSource;
+ private final SplitSelectionListener mSplitSelectionListener = new SplitSelectionListener() {
+ @Override
+ public void onSplitSelectionConfirmed() { }
+
+ @Override
+ public void onSplitSelectionActive() { }
+
+ @Override
+ public void onSplitSelectionExit(boolean launchedSplit) {
+ resetFromSplitSelectionState();
+ }
+ };
+
/**
* Keeps track of the index of the TaskView that split screen was initialized with so we know
* where to insert it back into list of taskViews in case user backs out of entering split
@@ -694,8 +710,6 @@
*/
private int mSplitHiddenTaskViewIndex = -1;
@Nullable
- private FloatingTaskView mFirstFloatingTaskView;
- @Nullable
private FloatingTaskView mSecondFloatingTaskView;
/**
@@ -992,16 +1006,34 @@
}
/**
- * Update the thumbnail of the task.
+ * Update the thumbnail(s) of the relevant TaskView.
* @param refreshNow Refresh immediately if it's true.
*/
@Nullable
- public TaskView updateThumbnail(int taskId, ThumbnailData thumbnailData, boolean refreshNow) {
- TaskView taskView = getTaskViewByTaskId(taskId);
- if (taskView != null) {
- taskView.getThumbnail().setThumbnail(taskView.getTask(), thumbnailData, refreshNow);
+ public TaskView updateThumbnail(
+ HashMap<Integer, ThumbnailData> thumbnailData, boolean refreshNow) {
+ TaskView updatedTaskView = null;
+ for (Map.Entry<Integer, ThumbnailData> entry : thumbnailData.entrySet()) {
+ Integer id = entry.getKey();
+ ThumbnailData thumbnail = entry.getValue();
+ TaskView taskView = getTaskViewByTaskId(id);
+ if (taskView == null) {
+ continue;
+ }
+ // taskView could be a GroupedTaskView, so select the relevant task by ID
+ TaskIdAttributeContainer taskAttributes = taskView.getTaskAttributesById(id);
+ if (taskAttributes == null) {
+ continue;
+ }
+ Task task = taskAttributes.getTask();
+ TaskThumbnailView taskThumbnailView = taskAttributes.getThumbnailView();
+ taskThumbnailView.setThumbnail(task, thumbnail, refreshNow);
+ // thumbnailData can contain 1-2 ids, but they should correspond to the same
+ // TaskView, so overwriting is ok
+ updatedTaskView = taskView;
}
- return taskView;
+
+ return updatedTaskView;
}
@Override
@@ -1048,6 +1080,9 @@
mIPipAnimationListener);
mOrientationState.initListeners();
mTaskOverlayFactory.initListeners();
+ if (FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
+ mSplitSelectStateController.registerSplitListener(mSplitSelectionListener);
+ }
}
@Override
@@ -1066,6 +1101,9 @@
mIPipAnimationListener.setActivityAndRecentsView(null, null);
mOrientationState.destroyListeners();
mTaskOverlayFactory.removeListeners();
+ if (FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
+ mSplitSelectStateController.unregisterSplitListener(mSplitSelectionListener);
+ }
}
@Override
@@ -1180,7 +1218,7 @@
new SurfaceTransactionApplier(mActivity.getDragLayer());
ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
appAnimator.setDuration(RECENTS_LAUNCH_DURATION);
- appAnimator.setInterpolator(ACCEL_DEACCEL);
+ appAnimator.setInterpolator(ACCELERATE_DECELERATE);
appAnimator.addUpdateListener(valueAnimator -> {
float percent = valueAnimator.getAnimatedFraction();
SurfaceTransaction transaction = new SurfaceTransaction();
@@ -1631,6 +1669,10 @@
int[] runningTaskId = getTaskIdsForTaskViewId(mRunningTaskViewId);
int[] focusedTaskId = getTaskIdsForTaskViewId(mFocusedTaskViewId);
+ // Reset the focused task to avoiding initializing TaskViews layout as focused task during
+ // binding. The focused task view will be updated after all the TaskViews are bound.
+ mFocusedTaskViewId = INVALID_TASK_ID;
+
// Removing views sets the currentPage to 0, so we save this and restore it after
// the new set of views are added
int previousCurrentPage = mCurrentPage;
@@ -1639,8 +1681,8 @@
// If we are entering Overview as a result of initiating a split from somewhere else
// (e.g. split from Home), we need to make sure the staged app is not drawn as a thumbnail.
int stagedTaskIdToBeRemovedFromGrid;
- if (mSplitSelectSource != null) {
- stagedTaskIdToBeRemovedFromGrid = mSplitSelectSource.alreadyRunningTaskId;
+ if (isSplitSelectionActive()) {
+ stagedTaskIdToBeRemovedFromGrid = mSplitSelectStateController.getInitialTaskId();
updateCurrentTaskActionsVisibility();
} else {
stagedTaskIdToBeRemovedFromGrid = INVALID_TASK_ID;
@@ -1737,7 +1779,9 @@
mFocusedTaskViewId = newFocusedTaskView != null && !ENABLE_GRID_ONLY_OVERVIEW.get()
? newFocusedTaskView.getTaskViewId() : INVALID_TASK_ID;
updateTaskSize();
- updateChildTaskOrientations();
+ if (newFocusedTaskView != null) {
+ newFocusedTaskView.setOrientationState(mOrientationState);
+ }
TaskView newRunningTaskView = null;
if (hasAnyValidTaskIds(runningTaskId)) {
@@ -2322,7 +2366,15 @@
startHome(mActivity.isStarted());
}
- public abstract void startHome(boolean animated);
+ public void startHome(boolean animated) {
+ if (!isCommandQueueEmpty()) return;
+ handleStartHome(animated);
+ }
+
+ protected abstract void handleStartHome(boolean animated);
+
+ /** Returns whether the overview command helper queue is empty. */
+ public abstract boolean isCommandQueueEmpty();
public void reset() {
setCurrentTask(-1);
@@ -2344,7 +2396,9 @@
remoteTargetHandle.getTransformParams().setTargetSet(null);
remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(false);
});
- resetFromSplitSelectionState();
+ if (!FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
+ resetFromSplitSelectionState();
+ }
// These are relatively expensive and don't need to be done this frame (RecentsView isn't
// visible anyway), so defer by a frame to get off the critical path, e.g. app to home.
@@ -3126,7 +3180,7 @@
// Use setFloat instead of setViewAlpha as we want to keep the view visible even when it's
// alpha is set to 0 so that it can be recycled in the view pool properly
anim.setFloat(taskView, VIEW_ALPHA, 0,
- clampToProgress(isOnGridBottomRow(taskView) ? ACCEL : FINAL_FRAME, 0, 0.5f));
+ clampToProgress(isOnGridBottomRow(taskView) ? ACCELERATE : FINAL_FRAME, 0, 0.5f));
FloatProperty<TaskView> secondaryViewTranslate =
taskView.getSecondaryDismissTranslationProperty();
int secondaryTaskDimension = mOrientationHandler.getSecondaryDimension(taskView);
@@ -3165,7 +3219,7 @@
AnimUtils.getDeviceOverviewToSplitTimings(mActivity.getDeviceProfile().isTablet);
RectF startingTaskRect = new RectF();
- safeRemoveDragLayerView(mFirstFloatingTaskView);
+ safeRemoveDragLayerView(mSplitSelectStateController.getFirstFloatingTaskView());
SplitAnimInitProps splitAnimInitProps =
mSplitSelectStateController.getSplitAnimationController().getFirstAnimInitViews(
() -> mSplitHiddenTaskView, () -> mSplitSelectSource);
@@ -3178,17 +3232,18 @@
timings.getIconFadeEndOffset()));
}
- mFirstFloatingTaskView = FloatingTaskView.getFloatingTaskView(mActivity,
+ FloatingTaskView firstFloatingTaskView = FloatingTaskView.getFloatingTaskView(mActivity,
splitAnimInitProps.getOriginalView(),
splitAnimInitProps.getOriginalBitmap(),
splitAnimInitProps.getIconDrawable(), startingTaskRect);
- mFirstFloatingTaskView.setAlpha(1);
- mFirstFloatingTaskView.addStagingAnimation(anim, startingTaskRect, mTempRect,
+ firstFloatingTaskView.setAlpha(1);
+ firstFloatingTaskView.addStagingAnimation(anim, startingTaskRect, mTempRect,
splitAnimInitProps.getFadeWithThumbnail(), splitAnimInitProps.isStagedTask());
+ mSplitSelectStateController.setFirstFloatingTaskView(firstFloatingTaskView);
// Allow user to click staged app to launch into fullscreen
if (ENABLE_LAUNCH_FROM_STAGED_APP.get()) {
- mFirstFloatingTaskView.setOnClickListener(this::animateToFullscreen);
+ firstFloatingTaskView.setOnClickListener(this::animateToFullscreen);
}
// SplitInstructionsView: animate in
@@ -3227,7 +3282,11 @@
InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER);
} else {
// If transition to split select was interrupted, clean up to prevent glitches
- resetFromSplitSelectionState();
+ if (FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
+ mSplitSelectStateController.resetState();
+ } else {
+ resetFromSplitSelectionState();
+ }
InteractionJankMonitorWrapper.cancel(
InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER);
}
@@ -3260,8 +3319,13 @@
true /* isStagedTask */);
pendingAnimation.addEndListener(animationSuccess ->
- mSplitSelectStateController.launchInitialAppFullscreen(launchSuccess ->
- resetFromSplitSelectionState()));
+ mSplitSelectStateController.launchInitialAppFullscreen(launchSuccess -> {
+ if (FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
+ mSplitSelectStateController.resetState();
+ } else {
+ resetFromSplitSelectionState();
+ }
+ }));
pendingAnimation.buildAnim().start();
}
@@ -3968,13 +4032,12 @@
private void removeTaskInternal(int dismissedTaskViewId) {
int[] taskIds = getTaskIdsForTaskViewId(dismissedTaskViewId);
- int primaryTaskId = taskIds[0];
- int secondaryTaskId = taskIds[1];
UI_HELPER_EXECUTOR.getHandler().post(
() -> {
- ActivityManagerWrapper.getInstance().removeTask(primaryTaskId);
- if (secondaryTaskId != -1) {
- ActivityManagerWrapper.getInstance().removeTask(secondaryTaskId);
+ for (int taskId : taskIds) {
+ if (taskId != -1) {
+ ActivityManagerWrapper.getInstance().removeTask(taskId);
+ }
}
});
}
@@ -4294,7 +4357,7 @@
private void updatePageOffsets() {
float offset = mAdjacentPageHorizontalOffset;
- float modalOffset = ACCEL_0_75.getInterpolation(mTaskModalness);
+ float modalOffset = ACCELERATE_0_75.getInterpolation(mTaskModalness);
int count = getChildCount();
boolean showAsGrid = showAsGrid();
@@ -4693,8 +4756,10 @@
mSplitSelectStateController.getActiveSplitStagePosition(), firstTaskEndingBounds,
secondTaskEndingBounds);
- mFirstFloatingTaskView.getBoundsOnScreen(firstTaskStartingBounds);
- mFirstFloatingTaskView.addConfirmAnimation(pendingAnimation,
+ FloatingTaskView firstFloatingTaskView =
+ mSplitSelectStateController.getFirstFloatingTaskView();
+ firstFloatingTaskView.getBoundsOnScreen(firstTaskStartingBounds);
+ firstFloatingTaskView.addConfirmAnimation(pendingAnimation,
new RectF(firstTaskStartingBounds), firstTaskEndingBounds,
false /* fadeWithThumbnail */, true /* isStagedTask */);
@@ -4712,7 +4777,13 @@
pendingAnimation.addEndListener(aBoolean -> {
mSplitSelectStateController.launchSplitTasks(
- aBoolean1 -> RecentsView.this.resetFromSplitSelectionState());
+ aBoolean1 -> {
+ if (FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
+ mSplitSelectStateController.resetState();
+ } else {
+ resetFromSplitSelectionState();
+ }
+ });
InteractionJankMonitorWrapper.end(InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER);
});
@@ -4727,18 +4798,18 @@
// Fade out all other views underneath placeholders
ObjectAnimator tvFade = ObjectAnimator.ofFloat(this, RecentsView.CONTENT_ALPHA,1, 0);
- pendingAnimation.add(tvFade, DEACCEL_2, SpringProperty.DEFAULT);
+ pendingAnimation.add(tvFade, DECELERATE_2, SpringProperty.DEFAULT);
pendingAnimation.buildAnim().start();
return true;
}
@SuppressLint("WrongCall")
protected void resetFromSplitSelectionState() {
- if (mSplitSelectSource != null || mSplitHiddenTaskViewIndex != -1) {
- safeRemoveDragLayerView(mFirstFloatingTaskView);
+ if (mSplitSelectSource != null || mSplitHiddenTaskViewIndex != -1 ||
+ FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
+ safeRemoveDragLayerView(mSplitSelectStateController.getFirstFloatingTaskView());
safeRemoveDragLayerView(mSecondFloatingTaskView);
safeRemoveDragLayerView(mSplitInstructionsView);
- mFirstFloatingTaskView = null;
mSecondFloatingTaskView = null;
mSplitInstructionsView = null;
mSplitSelectSource = null;
@@ -4754,7 +4825,11 @@
setTaskViewsPrimarySplitTranslation(0);
setTaskViewsSecondarySplitTranslation(0);
- mSplitSelectStateController.resetState();
+ if (!FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
+ // When flag is on, this method gets called from resetState() call below, let's avoid
+ // infinite recursion today
+ mSplitSelectStateController.resetState();
+ }
if (mSplitHiddenTaskViewIndex == -1) {
return;
}
@@ -4821,8 +4896,10 @@
mSplitPlaceholderInset, mActivity.getDeviceProfile(),
mSplitSelectStateController.getActiveSplitStagePosition(), mTempRect);
mTempRectF.set(mTempRect);
- mFirstFloatingTaskView.updateOrientationHandler(mOrientationHandler);
- mFirstFloatingTaskView.update(mTempRectF, /*progress=*/1f);
+ FloatingTaskView firstFloatingTaskView =
+ mSplitSelectStateController.getFirstFloatingTaskView();
+ firstFloatingTaskView.updateOrientationHandler(mOrientationHandler);
+ firstFloatingTaskView.update(mTempRectF, /*progress=*/1f);
PagedOrientationHandler orientationHandler = getPagedOrientationHandler();
Pair<FloatProperty, FloatProperty> taskViewsFloat =
@@ -5944,7 +6021,7 @@
@Nullable
public FloatingTaskView getFirstFloatingTaskView() {
- return mFirstFloatingTaskView;
+ return mSplitSelectStateController.getFirstFloatingTaskView();
}
@Nullable
diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuView.java b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
index 2c9afb4..b4d24e5 100644
--- a/quickstep/src/com/android/quickstep/views/TaskMenuView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
@@ -36,12 +36,12 @@
import androidx.annotation.Nullable;
+import com.android.app.animation.Interpolators;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimationSuccessListener;
-import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.touch.PagedOrientationHandler;
@@ -256,7 +256,7 @@
final Animator revealAnimator = createOpenCloseOutlineProvider()
.createRevealAnimator(this, closing);
- revealAnimator.setInterpolator(Interpolators.DEACCEL);
+ revealAnimator.setInterpolator(Interpolators.DECELERATE);
mOpenCloseAnimator.playTogether(revealAnimator,
ObjectAnimator.ofFloat(
mTaskContainer.getThumbnailView(), DIM_ALPHA,
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 17d83ec..40e3dca 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -20,11 +20,11 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.widget.Toast.LENGTH_SHORT;
+import static com.android.app.animation.Interpolators.ACCELERATE_DECELERATE;
+import static com.android.app.animation.Interpolators.FAST_OUT_SLOW_IN;
+import static com.android.app.animation.Interpolators.LINEAR;
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor;
-import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
-import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_ICON_TAP_OR_LONGPRESS;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_TAP;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
@@ -32,6 +32,7 @@
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
import static com.android.launcher3.util.SplitConfigurationOptions.getLogEventForPosition;
+import static com.android.quickstep.TaskOverlayFactory.getEnabledShortcuts;
import static com.android.quickstep.util.BorderAnimator.DEFAULT_BORDER_COLOR;
import static java.lang.annotation.RetentionPolicy.SOURCE;
@@ -71,11 +72,11 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.app.animation.Interpolators;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
-import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.popup.SystemShortcut;
@@ -88,6 +89,7 @@
import com.android.launcher3.util.RunnableList;
import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
+import com.android.launcher3.util.TraceHelper;
import com.android.launcher3.util.TransformingTouchDelegate;
import com.android.launcher3.util.ViewPool.Reusable;
import com.android.quickstep.RecentsModel;
@@ -163,7 +165,7 @@
public static final long SCALE_ICON_DURATION = 120;
private static final long DIM_ANIM_DURATION = 700;
- private static final Interpolator GRID_INTERPOLATOR = ACCEL_DEACCEL;
+ private static final Interpolator GRID_INTERPOLATOR = ACCELERATE_DECELERATE;
/**
* This technically can be a vanilla {@link TouchDelegate} class, however that class requires
@@ -580,7 +582,6 @@
mIconView, STAGE_POSITION_UNDEFINED);
mSnapshotView.bind(task);
setOrientationState(orientedState);
- mDigitalWellBeingToast.initialize(mTask);
}
/**
@@ -1560,8 +1561,8 @@
if (taskContainer == null) {
continue;
}
- for (SystemShortcut s : TaskOverlayFactory.getEnabledShortcuts(this,
- taskContainer)) {
+ for (SystemShortcut s : TraceHelper.allowIpcs(
+ "TV.a11yInfo", () -> getEnabledShortcuts(this, taskContainer))) {
info.addAction(s.createAccessibilityAction(context));
}
}
@@ -1598,7 +1599,7 @@
if (taskContainer == null) {
continue;
}
- for (SystemShortcut s : TaskOverlayFactory.getEnabledShortcuts(this,
+ for (SystemShortcut s : getEnabledShortcuts(this,
taskContainer)) {
if (s.hasHandlerForAction(action)) {
s.onClick(this);
diff --git a/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java b/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
index 83341cb..b12d98b 100644
--- a/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
+++ b/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
@@ -20,6 +20,7 @@
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+import static com.android.launcher3.util.TestUtil.runOnExecutorSync;
import static com.android.launcher3.util.WidgetUtils.createAppWidgetProviderInfo;
import static com.google.common.truth.Truth.assertThat;
@@ -40,11 +41,9 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.icons.ComponentWithLabel;
-import com.android.launcher3.icons.IconCache;
import com.android.launcher3.model.BgDataModel.FixedContainerItems;
import com.android.launcher3.model.QuickstepModelDelegate.PredictorState;
+import com.android.launcher3.util.LauncherLayoutBuilder;
import com.android.launcher3.util.LauncherModelHelper;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.android.launcher3.widget.PendingAddWidgetInfo;
@@ -53,8 +52,6 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
import java.util.Arrays;
import java.util.List;
@@ -76,17 +73,9 @@
private LauncherModelHelper mModelHelper;
private UserHandle mUserHandle;
- @Mock
- private IconCache mIconCache;
-
@Before
public void setup() throws Exception {
mModelHelper = new LauncherModelHelper();
- MockitoAnnotations.initMocks(this);
- doAnswer(invocation -> {
- ComponentWithLabel componentWithLabel = invocation.getArgument(0);
- return componentWithLabel.getComponent().getShortClassName();
- }).when(mIconCache).getTitleNoCache(any());
mUserHandle = myUserHandle();
mApp1Provider1 = createAppWidgetProviderInfo(
@@ -114,16 +103,12 @@
.collect(Collectors.toList());
}).when(manager).getInstalledProvidersForPackage(any(), eq(myUserHandle()));
- // 2 widgets, app4/provider1 & app5/provider1, have already been added to the workspace.
- mModelHelper.initializeData("widgets_predication_update_task_data");
-
+ LauncherLayoutBuilder builder = new LauncherLayoutBuilder()
+ .atWorkspace(0, 1, 2).putWidget("app4", "provider1", 1, 1)
+ .atWorkspace(0, 1, 3).putWidget("app5", "provider1", 1, 1);
+ mModelHelper.setupDefaultLayoutProvider(builder);
MAIN_EXECUTOR.submit(() -> mModelHelper.getModel().addCallbacks(mCallback)).get();
- MODEL_EXECUTOR.post(() -> mModelHelper.getBgDataModel().widgetsModel.update(
- LauncherAppState.getInstance(mModelHelper.sandboxContext),
- /* packageUser= */ null));
-
- MODEL_EXECUTOR.submit(() -> { }).get();
- MAIN_EXECUTOR.submit(() -> { }).get();
+ mModelHelper.loadModelSync();
}
@After
@@ -132,65 +117,72 @@
}
@Test
- public void widgetsRecommendationRan_shouldOnlyReturnNotAddedWidgetsInAppPredictionOrder()
- throws Exception {
- // WHEN newPredicationTask is executed with app predication of 5 apps.
- AppTarget app1 = new AppTarget(new AppTargetId("app1"), "app1", "provider1",
- mUserHandle);
- AppTarget app2 = new AppTarget(new AppTargetId("app2"), "app2", "provider1",
- mUserHandle);
- AppTarget app3 = new AppTarget(new AppTargetId("app3"), "app3", "className",
- mUserHandle);
- AppTarget app4 = new AppTarget(new AppTargetId("app4"), "app4", "provider1",
- mUserHandle);
- AppTarget app5 = new AppTarget(new AppTargetId("app5"), "app5", "provider1",
- mUserHandle);
- mModelHelper.executeTaskForTest(
- newWidgetsPredicationTask(List.of(app5, app3, app2, app4, app1)))
- .forEach(Runnable::run);
+ public void widgetsRecommendationRan_shouldOnlyReturnNotAddedWidgetsInAppPredictionOrder() {
+ // Run on model executor so that no other task runs in the middle.
+ runOnExecutorSync(MODEL_EXECUTOR, () -> {
+ // WHEN newPredicationTask is executed with app predication of 5 apps.
+ AppTarget app1 = new AppTarget(new AppTargetId("app1"), "app1", "provider1",
+ mUserHandle);
+ AppTarget app2 = new AppTarget(new AppTargetId("app2"), "app2", "provider1",
+ mUserHandle);
+ AppTarget app3 = new AppTarget(new AppTargetId("app3"), "app3", "className",
+ mUserHandle);
+ AppTarget app4 = new AppTarget(new AppTargetId("app4"), "app4", "provider1",
+ mUserHandle);
+ AppTarget app5 = new AppTarget(new AppTargetId("app5"), "app5", "provider1",
+ mUserHandle);
+ mCallback.mRecommendedWidgets = null;
+ mModelHelper.getModel().enqueueModelUpdateTask(
+ newWidgetsPredicationTask(List.of(app5, app3, app2, app4, app1)));
+ runOnExecutorSync(MAIN_EXECUTOR, () -> { });
- // THEN only 2 widgets are returned because
- // 1. app5/provider1 & app4/provider1 have already been added to workspace. They are
- // excluded from the result.
- // 2. app3 doesn't have a widget.
- // 3. only 1 widget is picked from app1 because we only want to promote one widget per app.
- List<PendingAddWidgetInfo> recommendedWidgets = mCallback.mRecommendedWidgets.items
- .stream()
- .map(itemInfo -> (PendingAddWidgetInfo) itemInfo)
- .collect(Collectors.toList());
- assertThat(recommendedWidgets).hasSize(2);
- assertWidgetInfo(recommendedWidgets.get(0).info, mApp2Provider1);
- assertWidgetInfo(recommendedWidgets.get(1).info, mApp1Provider1);
+ // THEN only 2 widgets are returned because
+ // 1. app5/provider1 & app4/provider1 have already been added to workspace. They are
+ // excluded from the result.
+ // 2. app3 doesn't have a widget.
+ // 3. only 1 widget is picked from app1 because we only want to promote one widget per app.
+ List<PendingAddWidgetInfo> recommendedWidgets = mCallback.mRecommendedWidgets.items
+ .stream()
+ .map(itemInfo -> (PendingAddWidgetInfo) itemInfo)
+ .collect(Collectors.toList());
+ assertThat(recommendedWidgets).hasSize(2);
+ assertWidgetInfo(recommendedWidgets.get(0).info, mApp2Provider1);
+ assertWidgetInfo(recommendedWidgets.get(1).info, mApp1Provider1);
+ });
}
@Test
- public void widgetsRecommendationRan_shouldReturnPackageWidgetsWhenEmpty()
- throws Exception {
+ public void widgetsRecommendationRan_shouldReturnPackageWidgetsWhenEmpty() {
+ runOnExecutorSync(MODEL_EXECUTOR, () -> {
- // Not installed widget
- AppTarget widget1 = new AppTarget(new AppTargetId("app1"), "app1", "provider3",
- mUserHandle);
- // Not installed app
- AppTarget widget3 = new AppTarget(new AppTargetId("app2"), "app3", "provider1",
- mUserHandle);
- // Workspace added widgets
- AppTarget widget4 = new AppTarget(new AppTargetId("app4"), "app4", "provider1",
- mUserHandle);
- AppTarget widget5 = new AppTarget(new AppTargetId("app5"), "app5", "provider1",
- mUserHandle);
- mModelHelper.executeTaskForTest(
- newWidgetsPredicationTask(List.of(widget5, widget3, widget4, widget1)))
- .forEach(Runnable::run);
+ // Not installed widget
+ AppTarget widget1 = new AppTarget(new AppTargetId("app1"), "app1", "provider3",
+ mUserHandle);
+ // Not installed app
+ AppTarget widget3 = new AppTarget(new AppTargetId("app2"), "app3", "provider1",
+ mUserHandle);
+ // Workspace added widgets
+ AppTarget widget4 = new AppTarget(new AppTargetId("app4"), "app4", "provider1",
+ mUserHandle);
+ AppTarget widget5 = new AppTarget(new AppTargetId("app5"), "app5", "provider1",
+ mUserHandle);
- // THEN only 2 widgets are returned because the launcher only filters out non-exist widgets.
- List<PendingAddWidgetInfo> recommendedWidgets = mCallback.mRecommendedWidgets.items
- .stream()
- .map(itemInfo -> (PendingAddWidgetInfo) itemInfo)
- .collect(Collectors.toList());
- assertThat(recommendedWidgets).hasSize(2);
- // Another widget from the same package
- assertWidgetInfo(recommendedWidgets.get(0).info, mApp4Provider2);
- assertWidgetInfo(recommendedWidgets.get(1).info, mApp1Provider1);
+ mCallback.mRecommendedWidgets = null;
+ mModelHelper.getModel().enqueueModelUpdateTask(
+ newWidgetsPredicationTask(List.of(widget5, widget3, widget4, widget1)));
+ runOnExecutorSync(MAIN_EXECUTOR, () -> { });
+
+ // THEN only 2 widgets are returned because the launcher only filters out
+ // non-exist widgets.
+ List<PendingAddWidgetInfo> recommendedWidgets = mCallback.mRecommendedWidgets.items
+ .stream()
+ .map(itemInfo -> (PendingAddWidgetInfo) itemInfo)
+ .collect(Collectors.toList());
+ assertThat(recommendedWidgets).hasSize(2);
+ // Another widget from the same package
+ assertWidgetInfo(recommendedWidgets.get(0).info, mApp4Provider2);
+ assertWidgetInfo(recommendedWidgets.get(1).info, mApp1Provider1);
+ });
}
private void assertWidgetInfo(
diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
index 97e34c5..7492ab8 100644
--- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
+++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
@@ -62,6 +62,7 @@
import com.android.launcher3.util.rule.SamplerRule;
import com.android.launcher3.util.rule.ScreenRecordRule;
import com.android.launcher3.util.rule.TestStabilityRule;
+import com.android.launcher3.util.rule.ViewCaptureAnalysisRule;
import com.android.launcher3.util.rule.ViewCaptureRule;
import com.android.quickstep.views.RecentsView;
@@ -116,12 +117,14 @@
Utilities.enableRunningInTestHarnessForTests();
}
- final ViewCaptureRule viewCaptureRule = new ViewCaptureRule();
+ final ViewCaptureRule viewCaptureRule = new ViewCaptureRule(
+ RecentsActivity.ACTIVITY_TRACKER::getCreatedActivity);
mOrderSensitiveRules = RuleChain
.outerRule(new SamplerRule())
.around(new NavigationModeSwitchRule(mLauncher))
+ .around(new FailureWatcher(mLauncher, viewCaptureRule::getViewCaptureData))
.around(viewCaptureRule)
- .around(new FailureWatcher(mDevice, mLauncher, viewCaptureRule.getViewCapture()));
+ .around(new ViewCaptureAnalysisRule(viewCaptureRule.getViewCapture()));
mOtherLauncherActivity = context.getPackageManager().queryIntentActivities(
getHomeIntentInPackage(context),
diff --git a/quickstep/tests/src/com/android/quickstep/OrientationTouchTransformerTest.java b/quickstep/tests/src/com/android/quickstep/OrientationTouchTransformerTest.java
index 9c240f0..298dd6c 100644
--- a/quickstep/tests/src/com/android/quickstep/OrientationTouchTransformerTest.java
+++ b/quickstep/tests/src/com/android/quickstep/OrientationTouchTransformerTest.java
@@ -53,6 +53,8 @@
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
+import java.util.List;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
public class OrientationTouchTransformerTest {
@@ -296,7 +298,7 @@
WindowManagerProxy wmProxy = mock(WindowManagerProxy.class);
doReturn(cachedDisplayInfo).when(wmProxy).getDisplayInfo(any());
doReturn(windowBounds).when(wmProxy).getRealBounds(any(), any());
- ArrayMap<CachedDisplayInfo, WindowBounds[]> internalDisplayBounds = new ArrayMap<>();
+ ArrayMap<CachedDisplayInfo, List<WindowBounds>> internalDisplayBounds = new ArrayMap<>();
doReturn(internalDisplayBounds).when(wmProxy).estimateInternalDisplayBounds(any());
return new DisplayController.Info(
getApplicationContext(), wmProxy, new ArrayMap<>());
diff --git a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
index df5303f..5127190 100644
--- a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
+++ b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
@@ -20,7 +20,6 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.launcher3.ui.TaplTestsLauncher3;
-import com.android.launcher3.util.RaceConditionReproducer;
import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
import org.junit.Before;
@@ -45,18 +44,6 @@
startTestActivity(2);
}
- private void runTest(String... eventSequence) {
- final RaceConditionReproducer eventProcessor = new RaceConditionReproducer(eventSequence);
-
- // Destroy Launcher activity.
- closeLauncherActivity();
-
- // The test action.
- eventProcessor.startIteration();
- mLauncher.goHome();
- eventProcessor.finishIteration();
- }
-
@Ignore
@Test
@NavigationModeSwitch
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index 32eadce..7350214 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -42,6 +42,7 @@
import com.android.launcher3.tapl.Overview;
import com.android.launcher3.tapl.OverviewActions;
import com.android.launcher3.tapl.OverviewTask;
+import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
import com.android.launcher3.ui.TaplTestsLauncher3;
import com.android.launcher3.util.Wait;
import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java b/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java
index e8cadab..3317ce1 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java
@@ -15,6 +15,7 @@
*/
package com.android.quickstep;
+
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
@@ -22,6 +23,7 @@
import android.content.Intent;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
import com.android.launcher3.ui.TaplTestsLauncher3;
import com.android.quickstep.TaskbarModeSwitchRule.TaskbarModeSwitch;
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java b/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java
index 40be480..4ff2f9c 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java
@@ -20,6 +20,7 @@
import androidx.test.filters.LargeTest;
+import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
import org.junit.Test;
diff --git a/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java b/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java
index 7e408a8..8cc8487 100644
--- a/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java
+++ b/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java
@@ -17,8 +17,8 @@
import static androidx.test.InstrumentationRegistry.getContext;
import static androidx.test.InstrumentationRegistry.getInstrumentation;
-import static androidx.test.InstrumentationRegistry.getTargetContext;
+import static com.android.launcher3.WorkspaceLayoutManager.FIRST_SCREEN_ID;
import static com.android.launcher3.testcomponent.TestCommandReceiver.EXTRA_VALUE;
import static com.android.launcher3.testcomponent.TestCommandReceiver.SET_LIST_VIEW_SERVICE_BINDER;
import static com.android.launcher3.util.WidgetUtils.createWidgetInfo;
@@ -32,7 +32,6 @@
import static org.mockito.Mockito.spy;
import android.appwidget.AppWidgetManager;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
@@ -42,14 +41,16 @@
import android.view.ViewConfiguration;
import android.widget.RemoteViews;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
import androidx.test.filters.Suppress;
-import androidx.test.runner.AndroidJUnit4;
import androidx.test.uiautomator.By;
import androidx.test.uiautomator.UiDevice;
import androidx.test.uiautomator.Until;
-import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherModel;
+import com.android.launcher3.celllayout.FavoriteItemsTransaction;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.tapl.LaunchedAppState;
import com.android.launcher3.testcomponent.ListViewService;
@@ -57,6 +58,7 @@
import com.android.launcher3.testcomponent.TestCommandReceiver;
import com.android.launcher3.ui.TaplTestsLauncher3;
import com.android.launcher3.ui.TestViewHelpers;
+import com.android.launcher3.util.Executors;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
@@ -67,6 +69,7 @@
import org.mockito.stubbing.Answer;
import java.lang.reflect.Field;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.IntConsumer;
/**
@@ -84,9 +87,9 @@
@RunWith(AndroidJUnit4.class)
public class ViewInflationDuringSwipeUp extends AbstractQuickStepTest {
- private ContentResolver mResolver;
private SparseArray<ViewConfiguration> mConfigMap;
private InitTracker mInitTracker;
+ private LauncherModel mModel;
@Before
public void setUp() throws Exception {
@@ -101,8 +104,8 @@
TaplTestsLauncher3.initialize(this);
- mResolver = mTargetContext.getContentResolver();
- LauncherSettings.Settings.call(mResolver, LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
+ mModel = LauncherAppState.getInstance(mTargetContext).getModel();
+ Executors.MODEL_EXECUTOR.submit(mModel.getModelDbController()::createEmptyDB).get();
// Get static configuration map
Field field = ViewConfiguration.class.getDeclaredField("sConfigurations");
@@ -182,26 +185,30 @@
private void executeSwipeUpTestWithWidget(IntConsumer widgetIdCreationCallback,
IntConsumer updateBeforeSwipeUp, String finalWidgetText) {
try {
- // Clear all existing data
- LauncherSettings.Settings.call(mResolver,
- LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
- LauncherSettings.Settings.call(mResolver,
- LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG);
- LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false);
+ LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(false);
+
// Make sure the widget is big enough to show a list of items
info.minSpanX = 2;
info.minSpanY = 2;
info.spanX = 2;
info.spanY = 2;
- LauncherAppWidgetInfo item = createWidgetInfo(info, getTargetContext(), true);
+ AtomicInteger widgetId = new AtomicInteger();
+ new FavoriteItemsTransaction(mTargetContext)
+ .addItem(() -> {
+ LauncherAppWidgetInfo item = createWidgetInfo(info, mTargetContext, true);
+ item.screenId = FIRST_SCREEN_ID;
+ widgetId.set(item.appWidgetId);
+ return item;
+ })
+ .commitAndLoadHome(mLauncher);
- addItemToScreen(item);
+
+
assertTrue("Widget is not present",
mLauncher.goHome().tryGetWidget(info.label, DEFAULT_UI_TIMEOUT) != null);
- int widgetId = item.appWidgetId;
// Verify widget id
- widgetIdCreationCallback.accept(widgetId);
+ widgetIdCreationCallback.accept(widgetId.get());
// Go to overview once so that all views are initialized and cached
startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
@@ -214,7 +221,7 @@
LaunchedAppState launchedAppState = mLauncher.getLaunchedAppState();
// Update widget
- updateBeforeSwipeUp.accept(widgetId);
+ updateBeforeSwipeUp.accept(widgetId.get());
launchedAppState.switchToOverview();
assertEquals("Views inflated during swipe up", 0, mInitTracker.viewInitCount);
diff --git a/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java b/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
index 83602be..a54dc2d 100644
--- a/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
+++ b/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
@@ -50,6 +50,9 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
+import java.util.List;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
public class TaskViewSimulatorTest {
@@ -150,7 +153,7 @@
WindowBounds wm = new WindowBounds(
new Rect(0, 0, mDisplaySize.x, mDisplaySize.y),
mDisplayInsets);
- WindowBounds[] allBounds = new WindowBounds[4];
+ List<WindowBounds> allBounds = new ArrayList<>(4);
for (int i = 0; i < 4; i++) {
Rect boundsR = new Rect(wm.bounds);
Rect insetsR = new Rect(wm.insets);
@@ -158,7 +161,7 @@
RotationUtils.rotateRect(insetsR, RotationUtils.deltaRotation(rotation, i));
RotationUtils.rotateRect(boundsR, RotationUtils.deltaRotation(rotation, i));
boundsR.set(0, 0, Math.abs(boundsR.width()), Math.abs(boundsR.height()));
- allBounds[i] = new WindowBounds(boundsR, insetsR);
+ allBounds.add(new WindowBounds(boundsR, insetsR));
}
WindowManagerProxy wmProxy = mock(WindowManagerProxy.class);
@@ -166,7 +169,7 @@
doReturn(wm).when(wmProxy).getRealBounds(any(), any());
doReturn(NavigationMode.NO_BUTTON).when(wmProxy).getNavigationMode(any());
- ArrayMap<CachedDisplayInfo, WindowBounds[]> perDisplayBoundsCache =
+ ArrayMap<CachedDisplayInfo, List<WindowBounds>> perDisplayBoundsCache =
new ArrayMap<>();
perDisplayBoundsCache.put(cdi.normalize(), allBounds);
diff --git a/res/drawable-hdpi/workspace_bg.9.png b/res/drawable-hdpi/workspace_bg.9.png
deleted file mode 100755
index 1d82fd4..0000000
--- a/res/drawable-hdpi/workspace_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/workspace_bg.9.png b/res/drawable-mdpi/workspace_bg.9.png
deleted file mode 100755
index 116ce44..0000000
--- a/res/drawable-mdpi/workspace_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/workspace_bg.9.png b/res/drawable-xhdpi/workspace_bg.9.png
deleted file mode 100755
index b1b3b85..0000000
--- a/res/drawable-xhdpi/workspace_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/workspace_bg.9.png b/res/drawable-xxhdpi/workspace_bg.9.png
deleted file mode 100755
index d47f6b2..0000000
--- a/res/drawable-xxhdpi/workspace_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/workspace_bg.9.png b/res/drawable-xxxhdpi/workspace_bg.9.png
deleted file mode 100755
index 3281548..0000000
--- a/res/drawable-xxxhdpi/workspace_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable/bubble_ic_overflow_button.xml b/res/drawable/bubble_ic_overflow_button.xml
new file mode 100644
index 0000000..475639e
--- /dev/null
+++ b/res/drawable/bubble_ic_overflow_button.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2023 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:width="24dp"
+ android:height="24dp">
+ <path
+ android:fillColor="#1A73E8"
+ android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
+</vector>
diff --git a/res/drawable/ic_wallpaper.xml b/res/drawable/ic_wallpaper.xml
deleted file mode 100644
index 9543f88..0000000
--- a/res/drawable/ic_wallpaper.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<!--
- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="@dimen/options_menu_icon_size"
- android:height="@dimen/options_menu_icon_size"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0"
- android:tint="?android:attr/textColorPrimary">
- <path
- android:fillColor="@android:color/white"
- android:pathData="M9,12.71l2.14,2.58l3-3.87L18,16.57H6L9,12.71z M5,5h6V3H5C3.9,3,3,3.9,3,5v6h2V5z M19,19h-6v2h6c1.1,0,2-0.9,2-2v-6h-2V19z
- M5,19v-6H3v6c0,1.1,0.9,2,2,2h6v-2H5z M19,5v6h2V5c0-1.1-0.9-2-2-2h-6v2H19z M16,9c0.55,0,1-0.45,1-1s-0.45-1-1-1
- c-0.55,0-1,0.45-1,1S15.45,9,16,9z"/>
-</vector>
diff --git a/res/layout/widgets_table_container.xml b/res/layout/widgets_table_container.xml
index 4a32672..c41d0bb 100644
--- a/res/layout/widgets_table_container.xml
+++ b/res/layout/widgets_table_container.xml
@@ -16,6 +16,7 @@
<com.android.launcher3.widget.picker.WidgetsListTableView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/widgets_table"
+ android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
android:background="@drawable/bg_widgets_content"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index ceae567..b5e4012 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"Vouer hernoem na <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Vouer: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> items"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Vouer: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> of meer items"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Muurpapiere"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Muurpapier en styl"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Wysig tuisskerm"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Tuis-instellings"</string>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index 57fafc8..39871f6 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"አቃፊ <xliff:g id="NAME">%1$s</xliff:g> ተብሎ ዳግም ተሰይሟል"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"አቃፊ፦ <xliff:g id="NAME">%1$s</xliff:g>፣ <xliff:g id="SIZE">%2$d</xliff:g> ንጥሎች"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"አቃፊ፦ <xliff:g id="NAME">%1$s</xliff:g>፣ <xliff:g id="SIZE">%2$d</xliff:g> ወይም ተጨማሪ ንጥሎች"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"የግድግዳ ወረቀቶች"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"ልጣፍ እና ቅጥ"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"መነሻ ማያ ገጽን አርትዕ"</string>
<string name="settings_button_text" msgid="8873672322605444408">"የመነሻ ቅንብሮች"</string>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 90a1e87..69e96cb 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"تمت إعادة تسمية المجلد إلى <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"المجلد: <xliff:g id="NAME">%1$s</xliff:g>، <xliff:g id="SIZE">%2$d</xliff:g> عنصر"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"المجلد: <xliff:g id="NAME">%1$s</xliff:g>، <xliff:g id="SIZE">%2$d</xliff:g> عنصر أو أكثر"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"الخلفيات"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"الخلفية والأسلوب"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"تعديل الشاشة الرئيسية"</string>
<string name="settings_button_text" msgid="8873672322605444408">"إعدادات الشاشة الرئيسية"</string>
diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml
index 587b377..8c18e8d 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"ফ\'ল্ডাৰৰ নাম সলনি কৰি <xliff:g id="NAME">%1$s</xliff:g> কৰা হৈছে"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"ফ’ল্ডাৰ: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> টা বস্তু"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"ফ’ল্ডাৰ: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> টা অথবা তাতকৈ অধিক বস্তু"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"ৱালপেপাৰসমূহ"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"ৱালপেপাৰ আৰু শৈলী"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"গৃহ স্ক্ৰীন সম্পাদনা কৰক"</string>
<string name="settings_button_text" msgid="8873672322605444408">"গৃহ ছেটিং"</string>
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index 1e1b634..bf12761 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"Qovluq adı <xliff:g id="NAME">%1$s</xliff:g> ilə dəyişdirildi"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Qovluq: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> element"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Qovluq: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> və ya daha çox element"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Divar kağızları"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Divar kağızı və üslub"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Əsas ekranı redaktə edin"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Home ayarları"</string>
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index 2b3ed53..cfb6fbd 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"Folder je preimenovan u <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> stavke"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ili više stavki"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Pozadine"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Pozadina i stil"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Izmeni početni ekran"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Podešavanja početnog ekrana"</string>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index 1eee242..5950ec5 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"Папка перайменавана ў <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Папка: <xliff:g id="NAME">%1$s</xliff:g>, элементы: <xliff:g id="SIZE">%2$d</xliff:g>"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Папка: <xliff:g id="NAME">%1$s</xliff:g>, элементы: <xliff:g id="SIZE">%2$d</xliff:g> ці больш"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Шпалеры"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Шпалеры і стыль"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Змяніць Галоўны экран"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Налады галоўнага экрана"</string>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index f59dcfb..b0f1c4c 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"Папката е преименувана на „<xliff:g id="NAME">%1$s</xliff:g>“"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Папка: „<xliff:g id="NAME">%1$s</xliff:g>“ – <xliff:g id="SIZE">%2$d</xliff:g> елемента"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Папка: „<xliff:g id="NAME">%1$s</xliff:g>“ – <xliff:g id="SIZE">%2$d</xliff:g> или повече елементи"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Тапети"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Тапет и стил"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Редактиране на началния екран"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Настройки за началния екран"</string>
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
index 326fc7b..e28638b 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"ফোল্ডারের নাম পাল্টে <xliff:g id="NAME">%1$s</xliff:g> করা হয়েছে"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"ফোল্ডার: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g>টি আইটেম"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"ফোল্ডার: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g>টি বা তার বেশি আইটেম"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"ওয়ালপেপারগুলি"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"ওয়ালপেপার এবং স্টাইল"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"হোম স্ক্রিন এডিট করুন"</string>
<string name="settings_button_text" msgid="8873672322605444408">"হোম সেটিংস"</string>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index 8c3dd08..8387f14 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"Ime foldera je promijenjeno u <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, br. stavki: <xliff:g id="SIZE">%2$d</xliff:g>"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ili više stavki"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Pozadinske slike"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Pozadinska slika i stil"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Uredi Početni ekran"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Postavke početnog ekrana"</string>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 9fe81dd..dd2d03f 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"S\'ha canviat el nom de la carpeta a <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Carpeta: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> elements"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Carpeta: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> o més elements"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Fons de pantalla"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Estil i fons de pantalla"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Edita la pantalla d\'inici"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Config. pantalla d\'inici"</string>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 40e1397..f9c7c77 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"Složka přejmenována na <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Složka: <xliff:g id="NAME">%1$s</xliff:g>, počet položek: <xliff:g id="SIZE">%2$d</xliff:g>"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Složka: <xliff:g id="NAME">%1$s</xliff:g>, počet položek: <xliff:g id="SIZE">%2$d</xliff:g> nebo více"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Tapety"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Tapeta a styl"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Upravit plochu"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Nastavení plochy"</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 2ccdded..5113da8 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"Mappen er omdøbt til <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Mappe: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> elementer"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Mappe: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> eller flere elementer"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Baggrunde"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Baggrund og stil"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Rediger startskærm"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Indst. for startskærm"</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 93ea28e..2fa7f91 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"Ordner umbenannt in <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Ordner: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> Elemente"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Ordner: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> oder mehr Elemente"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Hintergründe"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Hintergrund und Stil"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Startbildschirm bearbeiten"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Einstellungen"</string>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 6b6f249..f6a94cd 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"Ο φάκελος μετονομάστηκε σε <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Φάκελος: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> στοιχεία"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Φάκελος: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ή περισσότερα στοιχεία"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Ταπετσαρίες"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Ταπετσαρία και στιλ"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Επεξεργασία αρχικής οθόνης"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Ρυθμίσεις Αρχ. Οθ."</string>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index aa926df..47a67a8 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"Folder renamed to <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> items"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> or more items"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Wallpapers"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Wallpaper and style"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Edit home screen"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Home settings"</string>
diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml
index 40534ca..cc86e87 100644
--- a/res/values-en-rCA/strings.xml
+++ b/res/values-en-rCA/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"Folder renamed to <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> items"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> or more items"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Wallpapers"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Wallpaper and style"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Edit Home Screen"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Home settings"</string>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index aa926df..47a67a8 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"Folder renamed to <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> items"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> or more items"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Wallpapers"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Wallpaper and style"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Edit home screen"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Home settings"</string>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index aa926df..47a67a8 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"Folder renamed to <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> items"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> or more items"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Wallpapers"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Wallpaper and style"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Edit home screen"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Home settings"</string>
diff --git a/res/values-en-rXC/strings.xml b/res/values-en-rXC/strings.xml
index d7a5d95..cca6551 100644
--- a/res/values-en-rXC/strings.xml
+++ b/res/values-en-rXC/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"Folder renamed to <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> items"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> or more items"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Wallpapers"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Wallpaper & style"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Edit Home Screen"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Home settings"</string>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index bffa3f8..c49fa0e 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"El nombre de la carpeta se cambió a <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Carpeta: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> elementos"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Carpeta: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> o más elementos"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Fondos de pantalla"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Fondo de pantalla y estilo"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Editar pantalla principal"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Configuración de pantalla principal"</string>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 2ebefc0..b89a717 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"Se ha cambiado el nombre de la carpeta a <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Carpeta: <xliff:g id="NAME">%1$s</xliff:g> (<xliff:g id="SIZE">%2$d</xliff:g> elementos)"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Carpeta: <xliff:g id="NAME">%1$s</xliff:g> (<xliff:g id="SIZE">%2$d</xliff:g> o más elementos)"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Fondos de pantalla"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Fondo de pantalla y estilo"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Editar pantalla de inicio"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Ajustes de la pantalla de inicio"</string>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index 4b00f7b..ee7805f 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"Kausta uus nimi: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Kaust: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> üksust"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Kaust: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> või rohkem üksust"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Taustapildid"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Taustapilt ja stiil"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Muuda avaekraani"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Avakuva seaded"</string>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index 4d617c5..cbf7e1c 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"Karpetari <xliff:g id="NAME">%1$s</xliff:g> izena eman zaio"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"<xliff:g id="NAME">%1$s</xliff:g> karpeta (<xliff:g id="SIZE">%2$d</xliff:g> elementu)"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"<xliff:g id="NAME">%1$s</xliff:g> karpeta (<xliff:g id="SIZE">%2$d</xliff:g> elementu edo gehiago)"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Horma-paperak"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Horma-papera eta estiloa"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Editatu hasierako pantaila"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Hasierako pantailaren ezarpenak"</string>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 091211a..4ec760e 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"نام پوشه به <xliff:g id="NAME">%1$s</xliff:g> تغییر کرد"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"پوشه: <xliff:g id="NAME">%1$s</xliff:g>، <xliff:g id="SIZE">%2$d</xliff:g> مورد"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"پوشه: <xliff:g id="NAME">%1$s</xliff:g>، <xliff:g id="SIZE">%2$d</xliff:g> مورد یا بیشتر"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"کاغذدیواریها"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"کاغذدیواری و سبک"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"ویرایش «صفحه اصلی»"</string>
<string name="settings_button_text" msgid="8873672322605444408">"تنظیمات صفحه اصلی"</string>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index a1aa48c..16dabb7 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"Kansion nimeksi vaihdettiin <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Kansio: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> kohdetta"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Kansio: <xliff:g id="NAME">%1$s</xliff:g>, ainakin <xliff:g id="SIZE">%2$d</xliff:g> kohdetta"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Taustakuvat"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Taustakuva ja tyyli"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Muokkaa aloitusnäyttöä"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Aloitusnäyttö"</string>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index 7478968..1267205 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"Nouveau nom du dossier : <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Dossier : <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> élément(s)"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Dossier : <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> éléments ou plus"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Fonds d\'écran"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Fond d\'écran et style"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Modifier l\'écran d\'accueil"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Paramètres d\'accueil"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 63869f6..1addca7 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"Nouveau nom du dossier : <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Dossier : <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> éléments"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Dossier : <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> éléments ou plus"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Fonds d\'écran"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Fond d\'écran et style"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Modifier l\'écran d\'accueil"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Paramètres de l\'accueil"</string>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index ab68e22..cf6ac1c 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"O cartafol cambiou o nome a <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Cartafol: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> elementos"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Cartafol: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> elementos ou máis"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Fondos de pantalla"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Estilo e fondo de pantalla"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Editar pantalla de inicio"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Axustes de Inicio"</string>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index c3cf295..ee9280b 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"ફોલ્ડરનું નામ બદલીને <xliff:g id="NAME">%1$s</xliff:g> કર્યું"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"ફોલ્ડર: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> આઇટમ"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"ફોલ્ડર: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> કે વધુ આઇટમ"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"વૉલપેપર"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"વૉલપેપર અને સ્ટાઇલ"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"હોમ સ્ક્રીનમાં ફેરફાર કરો"</string>
<string name="settings_button_text" msgid="8873672322605444408">"હોમ સેટિંગ"</string>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 533c9ee..6866a8e 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"फ़ोल्डर का नाम बदलकर <xliff:g id="NAME">%1$s</xliff:g> किया गया"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"फ़ोल्डर: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> आइटम"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"फ़ोल्डर: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> या इससे ज़्यादा आइटम"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"वॉलपेपर"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"वॉलपेपर और स्टाइल"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"होम स्क्रीन में बदलाव करें"</string>
<string name="settings_button_text" msgid="8873672322605444408">"होम स्क्रीन की सेटिंग"</string>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index 3e52b18..59630f4 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"Mapa je preimenovana u <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Mapa: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> stavke"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Mapa: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ili više stavki"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Pozadine"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Pozadina i stil"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Uredi početni zaslon"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Postavke početnog zaslona"</string>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index 979539d..3bcf350 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"A mappa új neve: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Mappa: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> elem"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Mappa: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> vagy több elem"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Háttérképek"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Háttérkép és stílus"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Kezdőképernyő szerkesztése"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Kezdőképernyő beállításai"</string>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index 442f4f6..ccc028f 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"Պանակը վերանվանվեց <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Պանակ՝ <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> տարր"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Պանակ՝ <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> կամ ավելի տարրեր"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Պաստառներ"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Պաստառ և ոճ"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Փոփոխել հիմնական էկրանը"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Գլխավոր էկրանի կարգավորումներ"</string>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index f16f69f..c1d62d4 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"Folder diganti namanya menjadi <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> item"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> item atau lebih"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Wallpaper"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Wallpaper & gaya"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Edit Layar Utama"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Setelan layar utama"</string>
diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml
index fc574cb..e336a4b 100644
--- a/res/values-is/strings.xml
+++ b/res/values-is/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"Heiti möppu breytt í <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Mappa: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> atriði"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Mappa: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> eða fleiri atriði"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Veggfóður"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Veggfóður og stíll"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Breyta heimaskjá"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Heimastillingar"</string>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 59c0cb7..4342ce6 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"Nome della cartella sostituito con <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Cartella: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> elementi"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Cartella: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> o più elementi"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Sfondi"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Sfondo e stile"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Modifica la schermata Home"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Impostazioni schermata Home"</string>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 92f5d1d..808d324 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"שם התיקייה שונה ל-<xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"תיקייה: <xliff:g id="NAME">%1$s</xliff:g>, מספר הפריטים: <xliff:g id="SIZE">%2$d</xliff:g>"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"תיקייה: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> פריטים או יותר"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"טפטים"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"טפט וסגנון"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"עריכה של מסך הבית"</string>
<string name="settings_button_text" msgid="8873672322605444408">"הגדרות של מסך הבית"</string>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 43cc1e3..a6b1a21 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"フォルダの名前を「<xliff:g id="NAME">%1$s</xliff:g>」に変更しました"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"フォルダ: <xliff:g id="NAME">%1$s</xliff:g>、<xliff:g id="SIZE">%2$d</xliff:g> 件のアイテム"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"フォルダ: <xliff:g id="NAME">%1$s</xliff:g>、<xliff:g id="SIZE">%2$d</xliff:g> 件以上のアイテム"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"壁紙"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"壁紙とスタイル"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"ホーム画面を編集"</string>
<string name="settings_button_text" msgid="8873672322605444408">"ホームの設定"</string>
diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml
index 7553f6a..7e79faa 100644
--- a/res/values-ka/strings.xml
+++ b/res/values-ka/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"საქაღალდეს შეეცვალა სახელი „<xliff:g id="NAME">%1$s</xliff:g>“-ად"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"საქაღალდე: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ერთეული"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"საქაღალდე: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ან მეტი ერთეული"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"ფონები"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"ფონი და სტილი"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"მთავარი ეკრანის რედაქტირება"</string>
<string name="settings_button_text" msgid="8873672322605444408">"მთავარი გვერდის პარამეტრები"</string>
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
index 00babfd..cb514d8 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"Қалта атауы <xliff:g id="NAME">%1$s</xliff:g> болып өзгертілді"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Қалта: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> элемент бар"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Қалта: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> не одан көп элемент бар"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Тұсқағаздар"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Тұсқағаз және стиль"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Негізгі экранды өзгерту"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Негізгі экран параметрлері"</string>
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
index 42e0d4b..bdb9f0d 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"បានប្ដូរឈ្មោះថតជា <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"ថត៖ <xliff:g id="NAME">%1$s</xliff:g>, ធាតុ <xliff:g id="SIZE">%2$d</xliff:g>"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"ថត៖ <xliff:g id="NAME">%1$s</xliff:g>, ធាតុ <xliff:g id="SIZE">%2$d</xliff:g> ឬច្រើនជាងនេះ"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"ផ្ទាំងរូបភាព"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"ផ្ទាំងរូបភាព និងរចនាប័ទ្ម"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"កែអេក្រង់ដើម"</string>
<string name="settings_button_text" msgid="8873672322605444408">"ការកំណត់ទំព័រដើម"</string>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index 91dbf1e..957cc4f 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"ಫೋಲ್ಡರ್ ಅನ್ನು <xliff:g id="NAME">%1$s</xliff:g> ಗೆ ಮರುಹೆಸರಿಸಲಾಗಿದೆ"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"ಫೋಲ್ಡರ್: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ಐಟಂಗಳು"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"ಫೋಲ್ಡರ್: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ಅಥವಾ ಹೆಚ್ಚಿನ ಐಟಂಗಳು"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"ವಾಲ್ಪೇಪರ್ಗಳು"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"ವಾಲ್ಪೇಪರ್ ಮತ್ತು ಶೈಲಿ"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"ಹೋಮ್ ಸ್ಕ್ರೀನ್ ಅನ್ನು ಎಡಿಟ್ ಮಾಡಿ"</string>
<string name="settings_button_text" msgid="8873672322605444408">"ಮುಖಪುಟ ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index ab682a4..955af0b 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"폴더 이름 변경: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"폴더: <xliff:g id="NAME">%1$s</xliff:g>, 항목 <xliff:g id="SIZE">%2$d</xliff:g>개"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"폴더: <xliff:g id="NAME">%1$s</xliff:g>, 항목 <xliff:g id="SIZE">%2$d</xliff:g>개 이상"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"배경화면"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"배경화면 및 스타일"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"홈 화면 수정"</string>
<string name="settings_button_text" msgid="8873672322605444408">"홈 설정"</string>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index 2047691..7d77440 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"Фолдердин аты <xliff:g id="NAME">%1$s</xliff:g> деп өзгөртүлдү"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"<xliff:g id="NAME">%1$s</xliff:g> папкасындагы объекттер: <xliff:g id="SIZE">%2$d</xliff:g>"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"<xliff:g id="NAME">%1$s</xliff:g> папкасындагы объекттер: <xliff:g id="SIZE">%2$d</xliff:g> же андан көбүрөөк"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Тушкагаздар"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Тушкагаз жана стиль"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Башкы экранды түзөтүү"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Башкы бет параметрлери"</string>
diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml
index 166c370..9c26cfa 100644
--- a/res/values-lo/strings.xml
+++ b/res/values-lo/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"ປ່ຽນຊື່ໂຟນເດີເປັນ <xliff:g id="NAME">%1$s</xliff:g> ແລ້ວ"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"ໂຟນເດີ: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ລາຍການ"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"ໂຟນເດີ: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ຫຼື ລາຍການເພີ່ມເຕີມ"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"ພາບພື້ນຫຼັງ"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"ຮູບພື້ນຫຼັງ ແລະ ຮູບແບບ"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"ແກ້ໄຂໂຮມສະກຣີນ"</string>
<string name="settings_button_text" msgid="8873672322605444408">"ການຕັ້ງຄ່າໜ້າຫຼັກ"</string>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index 4c67498..7d69c57 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"Aplankas pervardytas kaip „<xliff:g id="NAME">%1$s</xliff:g>“"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Aplankas: „<xliff:g id="NAME">%1$s</xliff:g>“, elementų: <xliff:g id="SIZE">%2$d</xliff:g>"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Aplankas: „<xliff:g id="NAME">%1$s</xliff:g>“, elementų: <xliff:g id="SIZE">%2$d</xliff:g> ar daugiau"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Ekrano fonai"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Ekrano fonas ir stilius"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Redaguoti pagrindinį ekraną"</string>
<string name="settings_button_text" msgid="8873672322605444408">"„Home“ nustatymai"</string>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index ad7af54..6dc9564 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"Mape pārdēvēta par: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Mape <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> vienumi"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Mape <xliff:g id="NAME">%1$s</xliff:g>, vienumu skaits mapē: vismaz <xliff:g id="SIZE">%2$d</xliff:g>"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Fona tapetes"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Fona tapete un stils"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Rediģēt sākuma ekrānu"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Sākumlapas iestatījumi"</string>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index c2ee822..eff0e15 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"Папката е преименувана во <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Папка: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ставки"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Папка: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> или повеќе ставки"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Тапети"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Тапет и стил"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Изменете го почетниот екран"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Поставки за почетен екран"</string>
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
index 3600edd..d7feb3c 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"ഫോൾഡറിന്റെ പേര് <xliff:g id="NAME">%1$s</xliff:g> എന്നായി മാറ്റി"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"ഫോൾഡർ: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ഇനങ്ങൾ"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"ഫോൾഡർ: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> അല്ലെങ്കിൽ അതിലധികം ഇനങ്ങൾ"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"വാൾപേപ്പർ"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"വാൾപേപ്പറും സ്റ്റൈലും"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"ഹോം സ്ക്രീൻ എഡിറ്റ് ചെയ്യുക"</string>
<string name="settings_button_text" msgid="8873672322605444408">"ഹോം ക്രമീകരണം"</string>
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
index 430e80d..716bd85 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"Фолдерын нэр <xliff:g id="NAME">%1$s</xliff:g> болов"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Фолдер: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> зүйл"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Фолдер: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> эсвэл үүнээс олон зүйл"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Дэлгэцийн зураг"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Дэлгэцийн зураг, загвар"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Үндсэн нүүрийг засах"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Нүүр хуудасны тохиргоо"</string>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index b4515a0..54cc691 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"फोल्डरचे नाव बदलून <xliff:g id="NAME">%1$s</xliff:g> असे ठेवले"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"फोल्डर: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> आयटम"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"फोल्डर: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> किंवा त्याहून अधिक आयटम"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"वॉलपेपर"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"वॉलपेपर आणि शैली"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"होम स्क्रीन संपादित करा"</string>
<string name="settings_button_text" msgid="8873672322605444408">"होम सेटिंग्ज"</string>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index 5dcc913..e4a2e57 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"Folder dinamakan semula kepada <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> item"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> atau lebih banyak item"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Hiasan latar"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Hiasan latar & gaya"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Edit Skrin Utama"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Tetapan skrin utama"</string>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index 86d7a1a..c39e1d6 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"ပြောင်းလဲလိုက်သော အကန့်အမည် <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"ဖိုင်တွဲ - <xliff:g id="NAME">%1$s</xliff:g>၊ <xliff:g id="SIZE">%2$d</xliff:g> ဖိုင်များ"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"ဖိုင်တွဲ - <xliff:g id="NAME">%1$s</xliff:g>၊ <xliff:g id="SIZE">%2$d</xliff:g> သို့မဟုတ် နောက်ထပ်ဖိုင်များ"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"နောက်ခံများ"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"နောက်ခံနှင့် ပုံစံ"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"ပင်မစာမျက်နှာ တည်းဖြတ်ရန်"</string>
<string name="settings_button_text" msgid="8873672322605444408">"ပင်မဆက်တင်များ"</string>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index 485f845..7266a81 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"Mappen heter nå <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Mappe: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> elementer"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Mappe: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> eller flere elementer"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Bakgrunner"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Bakgrunn og stil"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Endre startsiden"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Startsideinnstillinger"</string>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index 58df374..6aa8fb5 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"फोल्डर <xliff:g id="NAME">%1$s</xliff:g> मा पुनःनामाकरण गरियो।"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"फोल्डर: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> वस्तुहरू"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"फोल्डर: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> वा सोभन्दा बढी वस्तुहरू"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"वालपेपरहरु"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"वालपेपर तथा शैली"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"होम स्क्रिन बदल्नुहोस्"</string>
<string name="settings_button_text" msgid="8873672322605444408">"होम पेजका सेटिङहरू"</string>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 416d82d..8d2aa21 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"De naam van de map is gewijzigd in <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Map: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> items"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Map: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> of meer items"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Achtergrond"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Achtergrond en stijl"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Startscherm bewerken"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Instellingen start"</string>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index c00b57e..2493af3 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"ଫୋଲ୍ଡରର ନାମ <xliff:g id="NAME">%1$s</xliff:g>କୁ ବଦଳାଗଲା"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"ଫୋଲ୍ଡର୍: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ଆଇଟମଗୁଡ଼ିକ"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"ଫୋଲ୍ଡର୍: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> କିମ୍ବା ଅଧିକ ଆଇଟମ୍"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"ୱାଲପେପର୍"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"ୱାଲପେପର ଏବଂ ଷ୍ଟାଇଲ"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"ହୋମ ସ୍କ୍ରିନକୁ ଏଡିଟ କରନ୍ତୁ"</string>
<string name="settings_button_text" msgid="8873672322605444408">"ହୋମ ସେଟିଂସ"</string>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index f9a66df..e6e3f07 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"ਫੋਲਡਰ ਨੂੰ <xliff:g id="NAME">%1$s</xliff:g> ਮੁੜ ਨਾਮ ਦਿੱਤਾ ਗਿਆ"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"ਫੋਲਡਰ: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ਆਈਟਮਾਂ"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"ਫੋਲਡਰ: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ਜਾਂ ਹੋਰ ਆਈਟਮਾਂ"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"ਵਾਲਪੇਪਰ"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"ਵਾਲਪੇਪਰ ਅਤੇ ਸਟਾਈਲ"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"ਹੋਮ ਸਕ੍ਰੀਨ ਦਾ ਸੰਪਾਦਨ ਕਰੋ"</string>
<string name="settings_button_text" msgid="8873672322605444408">"ਹੋਮ ਸੈਟਿੰਗਾਂ"</string>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 4f3abe3..739ef6c 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"Nazwa folderu zmieniona na <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> elementy"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, liczba elementów: <xliff:g id="SIZE">%2$d</xliff:g> lub więcej"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Tapety"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Tapeta i styl"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Edytuj ekran główny"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Ustawienia ekranu głównego"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 0acf63e..76c65a4 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"Nome de pasta alterado para <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Pasta: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> itens"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Pasta: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ou mais itens"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Imagens de fundo"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Imagem fundo/estilo"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Editar ecrã principal"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Definições de início"</string>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index e0c9b84..e23f459 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"Pasta renomeada para <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Pasta: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> itens"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Pasta: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ou mais itens"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Planos de fundo"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Plano de fundo e estilo"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Editar tela inicial"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Configurações da tela inicial"</string>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index d6c5e5e..fc9c5d1 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"Dosar redenumit <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Dosar: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> elemente"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Dosar: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> sau mai multe elemente"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Imagini de fundal"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Imagine de fundal și stil"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Editează ecranul de pornire"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Setări ecran de pornire"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 6315ee5..b3ff865 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"Папка переименована в \"<xliff:g id="NAME">%1$s</xliff:g>\""</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Папка \"<xliff:g id="NAME">%1$s</xliff:g>\" (объектов: <xliff:g id="SIZE">%2$d</xliff:g>)"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Папка \"<xliff:g id="NAME">%1$s</xliff:g>\" (объектов: <xliff:g id="SIZE">%2$d</xliff:g> или больше)"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Обои"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Обои и стиль"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Изменить главный экран"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Главный экран"</string>
diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml
index 15bc4a7..6f93989 100644
--- a/res/values-si/strings.xml
+++ b/res/values-si/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"<xliff:g id="NAME">%1$s</xliff:g> වෙත ෆෝල්ඩරය නැවත නම් කෙරිණි"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"ෆෝල්ඩරය: <xliff:g id="NAME">%1$s</xliff:g>, අයිතම <xliff:g id="SIZE">%2$d</xliff:g>"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"ෆෝල්ඩර: <xliff:g id="NAME">%1$s</xliff:g>, අයිතම <xliff:g id="SIZE">%2$d</xliff:g>ක් හෝ වැඩි ගණනක්"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"වෝල්පේපර"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"වෝල්පේපරය සහ මෝස්තරය"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"මුල් තිරය සංස්කරණය කරන්න"</string>
<string name="settings_button_text" msgid="8873672322605444408">"නිවසේ සැකසීම්"</string>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 2028c35..742d726 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"Priečinok bol premenovaný na <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Priečinok: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> položky"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Priečinok: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> alebo viac položiek"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Tapety"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Tapeta a štýl"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Upraviť plochu"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Nastavenia plochy"</string>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index e6197c7..57289d3 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"Mapa je preimenovana v <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Mapa: <xliff:g id="NAME">%1$s</xliff:g>, št. elementov: <xliff:g id="SIZE">%2$d</xliff:g>"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Mapa: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ali več elementov"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Ozadja"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Zaslonsko ozadje in slog"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Urejanje začetnega zaslona"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Domače nastavitve"</string>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index c6b8e7c..7dee8aa 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"Dosja u riemërtua në <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Dosja: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> artikuj"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Dosja: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ose më shumë artikuj"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Imazhet e sfondit"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Imazhi i sfondit dhe stili"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Modifiko ekranin bazë"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Cilësimet e ekranit bazë"</string>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index a4d7795..7121c53 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"Фолдер је преименован у <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Фолдер: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ставке"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Фолдер: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> или више ставки"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Позадине"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Позадина и стил"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Измени почетни екран"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Подешавања почетног екрана"</string>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index cd946e7..aeaf708 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"Mappen har bytt namn till <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Mapp: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> objekt"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Mapp: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> eller fler objekt"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Bakgrunder"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Bakgrund och utseende"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Redigera startskärm"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Startinställningar"</string>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 2686f8a..5be3bf3 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"Folda imebadilishwa jina kuwa <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Folda: <xliff:g id="NAME">%1$s</xliff:g>, vipengee <xliff:g id="SIZE">%2$d</xliff:g>"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Folda: <xliff:g id="NAME">%1$s</xliff:g>, vipengee <xliff:g id="SIZE">%2$d</xliff:g> au zaidi"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Mandhari"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Mandhari na mtindo"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Badilisha Skrini ya Kwanza"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Mipangilio ya mwanzo"</string>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index 8d9b2cc..ec287f5 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"ஃபோல்டர் <xliff:g id="NAME">%1$s</xliff:g> என மறுபெயரிடப்பட்டது"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"ஃபோல்டர்: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ஃபைல்கள்"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"ஃபோல்டர்: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> அல்லது அதற்கு அதிகமான ஃபைல்கள்"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"வால்பேப்பர்கள்"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"வால்பேப்பர் & ஸ்டைல்"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"முகப்புத் திரையில் மாற்று"</string>
<string name="settings_button_text" msgid="8873672322605444408">"முகப்பு அமைப்புகள்"</string>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index 08e633d..032a241 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"ఫోల్డర్ పేరు <xliff:g id="NAME">%1$s</xliff:g>గా మార్చబడింది"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"ఫోల్డర్: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ఐటెమ్లు"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"ఫోల్డర్: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> లేదా అంతకంటే ఎక్కువ ఐటెమ్లు"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"వాల్పేపర్లు"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"వాల్పేపర్ & స్టయిల్"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"మొదటి స్క్రీన్ను ఎడిట్ చేయండి"</string>
<string name="settings_button_text" msgid="8873672322605444408">"మొదటి స్క్రీన్ సెట్టింగ్లు"</string>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 4b64993..fe79b12 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"เปลี่ยนชื่อโฟลเดอร์เป็น <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"โฟลเดอร์: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> รายการ"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"โฟลเดอร์: <xliff:g id="NAME">%1$s</xliff:g>, อย่างน้อย <xliff:g id="SIZE">%2$d</xliff:g> รายการ"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"วอลเปเปอร์"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"วอลเปเปอร์และรูปแบบ"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"แก้ไขหน้าจอหลัก"</string>
<string name="settings_button_text" msgid="8873672322605444408">"การตั้งค่าหน้าจอหลัก"</string>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index 61f9227..fe4aac1 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"Pinalitan ang pangalan ng folder ng <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> (na) item"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> o higit pang item"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Mga Wallpaper"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Wallpaper & istilo"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"I-edit ang Home Screen"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Mga setting ng Home"</string>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index ef28f18..8a836e6 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"Klasörün adı <xliff:g id="NAME">%1$s</xliff:g> olarak değiştirildi"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Klasör: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> öğe"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Klasör: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> veya daha fazla öğe"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Duvar Kağıtları"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Duvar kağıdı ve stil"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Ana ekranı düzenleyin"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Ana ekran ayarları"</string>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index 822936b..5dc5d8e 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"Папку перейменовано на <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Папка \"<xliff:g id="NAME">%1$s</xliff:g>\", елементів: <xliff:g id="SIZE">%2$d</xliff:g>"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Папка \"<xliff:g id="NAME">%1$s</xliff:g>\", елементів: <xliff:g id="SIZE">%2$d</xliff:g> або більше"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Фонові малюнки"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Оформлення і стиль"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Редагувати головний екран"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Налаштування головного екрана"</string>
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
index 1fffcfd..3994810 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"فولڈر کا نام تبدیل کر کے <xliff:g id="NAME">%1$s</xliff:g> کر دیا گیا"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"فولڈر: <xliff:g id="NAME">%1$s</xliff:g>، <xliff:g id="SIZE">%2$d</xliff:g> آئٹمز"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"فولڈر: <xliff:g id="NAME">%1$s</xliff:g>، <xliff:g id="SIZE">%2$d</xliff:g> یا مزید آئٹمز"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"وال پیپرز"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"وال پیپر اور طرز"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"ہوم اسکرین میں ترمیم کریں"</string>
<string name="settings_button_text" msgid="8873672322605444408">"ہوم ترتیبات"</string>
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
index db381ca..405c4ef 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"Jild nomi <xliff:g id="NAME">%1$s</xliff:g>ga o‘zgartirildi"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Jild: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> fayllar"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Jild: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> va undan ortiq fayllar"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Fon rasmlari"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Fon rasmi va uslubi"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Bosh ekranni tahrirlash"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Bosh ekran sozlamalari"</string>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index b73bc61..075a7b0 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"Đã đổi tên thư mục thành <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Thư mục: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> mục"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Thư mục: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> mục trở lên"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Hình nền"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Hình nền và phong cách"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Chỉnh sửa Màn hình chính"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Cài đặt màn hình chính"</string>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 20c8ea9..e525c02 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"已将文件夹重命名为“<xliff:g id="NAME">%1$s</xliff:g>”"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"文件夹:<xliff:g id="NAME">%1$s</xliff:g>,<xliff:g id="SIZE">%2$d</xliff:g> 个项目"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"文件夹:<xliff:g id="NAME">%1$s</xliff:g>,<xliff:g id="SIZE">%2$d</xliff:g> 个或更多项目"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"壁纸"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"壁纸与个性化"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"修改主屏幕"</string>
<string name="settings_button_text" msgid="8873672322605444408">"主屏幕设置"</string>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index a01b263..3cc6bff 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"資料夾已重新命名為「<xliff:g id="NAME">%1$s</xliff:g>」"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"資料夾:<xliff:g id="NAME">%1$s</xliff:g>,<xliff:g id="SIZE">%2$d</xliff:g> 個項目"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"資料夾:<xliff:g id="NAME">%1$s</xliff:g>,<xliff:g id="SIZE">%2$d</xliff:g> 個或以上的項目"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"桌布"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"桌布和樣式"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"編輯主畫面"</string>
<string name="settings_button_text" msgid="8873672322605444408">"主畫面設定"</string>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 69d1d90..cf40155 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"已將資料夾重新命名為「<xliff:g id="NAME">%1$s</xliff:g>」"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"資料夾:<xliff:g id="NAME">%1$s</xliff:g>,<xliff:g id="SIZE">%2$d</xliff:g> 個項目"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"資料夾:<xliff:g id="NAME">%1$s</xliff:g>,<xliff:g id="SIZE">%2$d</xliff:g> 個以上的項目"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"桌布"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"桌布和樣式"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"編輯主畫面"</string>
<string name="settings_button_text" msgid="8873672322605444408">"主畫面設定"</string>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index d9ed3f0..6ccc4be 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -99,7 +99,6 @@
<string name="folder_renamed" msgid="1794088362165669656">"Ifolda iqanjwe kabusha ngo-<xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format_exact" msgid="8626242716117004803">"Ifolda: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> izinto"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Ifolda: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> noma izinto eziningi"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Izithombe zangemuva"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Isithombe sangemuva nesitayela"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Hlela Isikrini Sasekhaya"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Amasethingi asekhaya"</string>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index e4650b2..76a1239 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -36,7 +36,6 @@
<attr name="workspaceShadowColor" format="color" />
<attr name="workspaceAmbientShadowColor" format="color" />
<attr name="workspaceKeyShadowColor" format="color" />
- <attr name="workspaceStatusBarScrim" format="reference" />
<attr name="widgetsTheme" format="reference" />
<attr name="iconOnlyShortcutColor" format="color" />
<attr name="eduHalfSheetBGColor" format="color" />
@@ -205,6 +204,14 @@
<!-- File that contains the specs for the workspace.
Needs FeatureFlags.ENABLE_RESPONSIVE_WORKSPACE enabled -->
<attr name="workspaceSpecsId" format="reference" />
+ <!-- File that contains the specs for all apps.
+ Needs FeatureFlags.ENABLE_RESPONSIVE_WORKSPACE enabled -->
+ <attr name="allAppsSpecsId" format="reference" />
+
+ <!-- File that contains the specs for the workspace.
+ Needs FeatureFlags.ENABLE_RESPONSIVE_WORKSPACE enabled -->
+ <attr name="folderSpecsId" format="reference" />
+
<!-- By default all categories are enabled -->
<attr name="deviceCategory" format="integer">
<!-- Enable on phone only -->
@@ -252,10 +259,22 @@
<attr name="maxAvailableSize" format="dimension" />
</declare-styleable>
- <declare-styleable name="SpecSize">
+ <declare-styleable name="SizeSpec">
<attr name="fixedSize" format="dimension" />
<attr name="ofAvailableSpace" format="float" />
<attr name="ofRemainderSpace" format="float" />
+ <attr name="matchWorkspace" format="boolean" />
+ <attr name="maxSize" format="dimension" />
+ </declare-styleable>
+
+ <declare-styleable name="FolderSpec">
+ <attr name="specType" />
+ <attr name="maxAvailableSize" />
+ </declare-styleable>
+
+ <declare-styleable name="AllAppsSpec">
+ <attr name="specType" />
+ <attr name="maxAvailableSize" />
</declare-styleable>
<declare-styleable name="ProfileDisplayOption">
@@ -564,4 +583,8 @@
<!-- The icon drawable of a widget category. -->
<attr name="sectionDrawable" format="reference" />
</declare-styleable>
+
+ <declare-styleable name="ArrowTipView">
+ <attr name="arrowTipBackground" format="color" />
+ </declare-styleable>
</resources>
diff --git a/res/values/config.xml b/res/values/config.xml
index 5a6698b..8f9731c 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -84,6 +84,8 @@
<string name="window_manager_proxy_class" translatable="false"></string>
<string name="secondary_display_predictions_class" translatable="false"></string>
<string name="widget_holder_factory_class" translatable="false"></string>
+ <string name="taskbar_search_session_controller_class" translatable="false"></string>
+ <string name="taskbar_model_callbacks_factory_class" translatable="false"></string>
<!-- View ID to use for QSB widget -->
<item type="id" name="qsb_widget" />
@@ -102,8 +104,6 @@
<!-- Default packages -->
<string name="wallpaper_picker_package" translatable="false"></string>
- <string name="custom_activity_picker" translatable="false">
- com.android.customization.picker.CustomizationPickerActivity</string>
<string name="local_colors_extraction_class" translatable="false"></string>
<string name="search_session_manager_class" translatable="false"></string>
@@ -217,4 +217,28 @@
<!-- Whether the floating rotation button should be on the left/right in the device's natural
orientation -->
<bool name="floating_rotation_button_position_left">true</bool>
+
+ <!-- Mapping of visual icon size to XML value http://b/235886078 -->
+ <dimen name="iconSize48dp">52dp</dimen>
+ <dimen name="iconSize50dp">55dp</dimen>
+ <dimen name="iconSize52dp">57dp</dimen>
+ <dimen name="iconSize54dp">59dp</dimen>
+ <dimen name="iconSize56dp">61dp</dimen>
+ <dimen name="iconSize58dp">63dp</dimen>
+ <dimen name="iconSize60dp">66dp</dimen>
+ <dimen name="iconSize66dp">72dp</dimen>
+ <dimen name="iconSize72dp">79dp</dimen>
+
+ <!-- Icon size steps in dp -->
+ <integer-array name="icon_size_steps">
+ <item>@dimen/iconSize48dp</item>
+ <item>@dimen/iconSize50dp</item>
+ <item>@dimen/iconSize52dp</item>
+ <item>@dimen/iconSize54dp</item>
+ <item>@dimen/iconSize56dp</item>
+ <item>@dimen/iconSize58dp</item>
+ <item>@dimen/iconSize60dp</item>
+ <item>@dimen/iconSize66dp</item>
+ <item>@dimen/iconSize72dp</item>
+ </integer-array>
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index c2eb373..1b46b4d 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -250,8 +250,6 @@
<!-- Strings for the customization mode -->
<!-- Text for wallpaper change button [CHAR LIMIT=30]-->
- <string name="wallpaper_button_text">Wallpapers</string>
- <!-- Text for wallpaper change button [CHAR LIMIT=30]-->
<string name="styles_wallpaper_button_text">Wallpaper & style</string>
<!-- Text for edit home screen button [CHAR LIMIT=30]-->
<string name="edit_home_screen">Edit Home Screen</string>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index c41f0e8..14454bd 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -50,7 +50,6 @@
<item name="workspaceShadowColor">#B0000000</item>
<item name="workspaceAmbientShadowColor">#40000000</item>
<item name="workspaceKeyShadowColor">#89000000</item>
- <item name="workspaceStatusBarScrim">@drawable/workspace_bg</item>
<item name="widgetsTheme">@style/WidgetContainerTheme</item>
<item name="folderPaginationColor">@color/folder_pagination_color_light</item>
<item name="folderPreviewColor">@color/folder_preview_light</item>
@@ -137,7 +136,6 @@
<item name="workspaceAmbientShadowColor">@android:color/transparent</item>
<item name="workspaceKeyShadowColor">@android:color/transparent</item>
<item name="isWorkspaceDarkText">true</item>
- <item name="workspaceStatusBarScrim">@null</item>
<item name="workspaceAccentColor">@color/workspace_accent_color_dark</item>
<item name="dropTargetHoverTextColor">@color/drop_target_hover_text_color_dark</item>
<item name="dropTargetHoverButtonColor">@color/drop_target_hover_button_color_dark</item>
@@ -188,7 +186,6 @@
<item name="workspaceAmbientShadowColor">@android:color/transparent</item>
<item name="workspaceKeyShadowColor">@android:color/transparent</item>
<item name="isWorkspaceDarkText">true</item>
- <item name="workspaceStatusBarScrim">@null</item>
<item name="workspaceAccentColor">@color/workspace_accent_color_dark</item>
<item name="dropTargetHoverTextColor">@color/drop_target_hover_text_color_dark</item>
<item name="dropTargetHoverButtonColor">@color/drop_target_hover_button_color_dark</item>
@@ -426,4 +423,7 @@
<item name="horizontalPadding">16dp</item>
</style>
+ <style name="ArrowTipStyle">
+ <item name="arrowTipBackground">@color/arrow_tip_view_bg</item>
+ </style>
</resources>
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index 7131452..9a1ccd0 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -360,37 +360,6 @@
lp.y = sTmpRect.top;
}
- // Handle invalid resize across CellLayouts in the two panel UI.
- if (mCellLayout.getParent() instanceof Workspace) {
- Workspace<?> workspace = (Workspace<?>) mCellLayout.getParent();
- CellLayout pairedCellLayout = workspace.getScreenPair(mCellLayout);
- if (pairedCellLayout != null) {
- Rect focusedCellLayoutBound = sTmpRect;
- mDragLayerRelativeCoordinateHelper.viewToRect(mCellLayout, focusedCellLayoutBound);
- Rect resizeFrameBound = sTmpRect2;
- findViewById(R.id.widget_resize_frame).getGlobalVisibleRect(resizeFrameBound);
- float progress = 1f;
- if (workspace.indexOfChild(pairedCellLayout) < workspace.indexOfChild(mCellLayout)
- && mDeltaX < 0
- && resizeFrameBound.left < focusedCellLayoutBound.left) {
- // Resize from right to left.
- progress = (mDragAcrossTwoPanelOpacityMargin + mDeltaX)
- / mDragAcrossTwoPanelOpacityMargin;
- } else if (workspace.indexOfChild(pairedCellLayout)
- > workspace.indexOfChild(mCellLayout)
- && mDeltaX > 0
- && resizeFrameBound.right > focusedCellLayoutBound.right) {
- // Resize from left to right.
- progress = (mDragAcrossTwoPanelOpacityMargin - mDeltaX)
- / mDragAcrossTwoPanelOpacityMargin;
- }
- float alpha = Math.max(MIN_OPACITY_FOR_CELL_LAYOUT_DURING_INVALID_RESIZE, progress);
- float springLoadedProgress = Math.min(1f, 1f - progress);
- updateInvalidResizeEffect(mCellLayout, pairedCellLayout, alpha,
- springLoadedProgress);
- }
- }
-
requestLayout();
}
@@ -547,13 +516,6 @@
}
final DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
- final CellLayout pairedCellLayout;
- if (mCellLayout.getParent() instanceof Workspace) {
- Workspace<?> workspace = (Workspace<?>) mCellLayout.getParent();
- pairedCellLayout = workspace.getScreenPair(mCellLayout);
- } else {
- pairedCellLayout = null;
- }
if (!animate) {
lp.width = newWidth;
lp.height = newHeight;
@@ -562,10 +524,6 @@
for (int i = 0; i < HANDLE_COUNT; i++) {
mDragHandles[i].setAlpha(1f);
}
- if (pairedCellLayout != null) {
- updateInvalidResizeEffect(mCellLayout, pairedCellLayout, /* alpha= */ 1f,
- /* springLoadedProgress= */ 0f);
- }
requestLayout();
} else {
ObjectAnimator oa = ObjectAnimator.ofPropertyValuesHolder(lp,
@@ -581,10 +539,6 @@
set.play(mFirstFrameAnimatorHelper.addTo(
ObjectAnimator.ofFloat(mDragHandles[i], ALPHA, 1f)));
}
- if (pairedCellLayout != null) {
- updateInvalidResizeEffect(mCellLayout, pairedCellLayout, /* alpha= */ 1f,
- /* springLoadedProgress= */ 0f, /* animatorSet= */ set);
- }
set.setDuration(SNAP_DURATION);
set.start();
}
diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java
index ede7e2f..c7cdfa8 100644
--- a/src/com/android/launcher3/AutoInstallsLayout.java
+++ b/src/com/android/launcher3/AutoInstallsLayout.java
@@ -17,6 +17,8 @@
package com.android.launcher3;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME;
+import static com.android.launcher3.provider.LauncherDbUtils.itemIdMatch;
import android.content.ComponentName;
import android.content.ContentValues;
@@ -30,7 +32,6 @@
import android.content.res.Resources.NotFoundException;
import android.content.res.XmlResourceParser;
import android.database.sqlite.SQLiteDatabase;
-import android.net.Uri;
import android.os.Bundle;
import android.os.Process;
import android.text.TextUtils;
@@ -44,7 +45,6 @@
import androidx.annotation.WorkerThread;
import androidx.annotation.XmlRes;
-import com.android.launcher3.LauncherProvider.SqlArguments;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
@@ -619,9 +619,7 @@
// failed to add, and less than 2 were actually added
if (folderItems.size() < 2) {
// Delete the folder
- Uri uri = Favorites.getContentUri(folderId);
- SqlArguments args = new SqlArguments(uri, null, null);
- mDb.delete(args.table, args.where, args.args);
+ mDb.delete(TABLE_NAME, itemIdMatch(folderId), null);
addedId = -1;
// If we have a single item, promote it to where the folder
@@ -634,7 +632,7 @@
copyInteger(myValues, childValues, Favorites.CELLY);
addedId = folderItems.get(0);
- mDb.update(Favorites.TABLE_NAME, childValues,
+ mDb.update(TABLE_NAME, childValues,
Favorites._ID + "=" + addedId, null);
}
}
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index f920d75..05d434e 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -210,7 +210,7 @@
setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx);
setCompoundDrawablePadding(grid.iconDrawablePaddingPx);
defaultIconSize = grid.iconSizePx;
- setCenterVertically(grid.isScalableGrid);
+ setCenterVertically(grid.iconCenterVertically);
} else if (mDisplay == DISPLAY_ALL_APPS) {
setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.allAppsIconTextSizePx);
setCompoundDrawablePadding(grid.allAppsIconDrawablePaddingPx);
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index 108e557..3d715e5 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -35,7 +35,7 @@
import androidx.annotation.VisibleForTesting;
-import com.android.launcher3.anim.Interpolators;
+import com.android.app.animation.Interpolators;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.DragOptions;
@@ -258,7 +258,7 @@
dragLayer.animateView(d.dragView, to, scale, 0.1f, 0.1f,
DRAG_VIEW_DROP_DURATION,
- Interpolators.DEACCEL_2, onAnimationEndRunnable,
+ Interpolators.DECELERATE_2, onAnimationEndRunnable,
DragLayer.ANIMATION_END_DISAPPEAR, null);
}
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 61b2748..64ac841 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -18,8 +18,8 @@
import static android.animation.ValueAnimator.areAnimatorsEnabled;
+import static com.android.app.animation.Interpolators.DECELERATE_1_5;
import static com.android.launcher3.LauncherState.EDIT_MODE;
-import static com.android.launcher3.anim.Interpolators.DEACCEL_1_5;
import static com.android.launcher3.dragndrop.DraggableView.DRAGGABLE_ICON;
import static com.android.launcher3.icons.IconNormalizer.ICON_VISIBLE_AREA_FACTOR;
import static com.android.launcher3.util.MultiTranslateDelegate.INDEX_REORDER_BOUNCE_OFFSET;
@@ -60,9 +60,9 @@
import androidx.core.graphics.ColorUtils;
import androidx.core.view.ViewCompat;
+import com.android.app.animation.Interpolators;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.accessibility.DragAndDropAccessibilityDelegate;
-import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.celllayout.CellPosMapper.CellPos;
import com.android.launcher3.celllayout.ReorderAlgorithm;
@@ -272,7 +272,7 @@
mReorderPreviewAnimationMagnitude = (REORDER_PREVIEW_MAGNITUDE * deviceProfile.iconSizePx);
// Initialize the data structures used for the drag visualization.
- mEaseOutInterpolator = Interpolators.DEACCEL_2_5; // Quint ease out
+ mEaseOutInterpolator = Interpolators.DECELERATE_QUINT; // Quint ease out
mDragCell[0] = mDragCell[1] = -1;
mDragCellSpan[0] = mDragCellSpan[1] = -1;
for (int i = 0; i < mDragOutlines.length; i++) {
@@ -382,8 +382,7 @@
private void resetCellSizeInternal(DeviceProfile deviceProfile) {
switch (mContainerType) {
case FOLDER:
- mBorderSpace = new Point(deviceProfile.folderCellLayoutBorderSpacePx,
- deviceProfile.folderCellLayoutBorderSpacePx);
+ mBorderSpace = new Point(deviceProfile.folderCellLayoutBorderSpacePx);
break;
case HOTSEAT:
mBorderSpace = new Point(deviceProfile.hotseatBorderSpace,
@@ -1606,7 +1605,7 @@
ValueAnimator va = ObjectAnimator.ofFloat(this, ANIMATION_PROGRESS,
animationProgress, 0);
a = va;
- a.setInterpolator(DEACCEL_1_5);
+ a.setInterpolator(DECELERATE_1_5);
a.setDuration(REORDER_ANIMATION_DURATION);
a.start();
}
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index fa1cbd6..bd47fca 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -16,13 +16,13 @@
package com.android.launcher3;
+import static com.android.app.animation.Interpolators.LINEAR;
import static com.android.launcher3.InvariantDeviceProfile.INDEX_DEFAULT;
import static com.android.launcher3.InvariantDeviceProfile.INDEX_LANDSCAPE;
import static com.android.launcher3.InvariantDeviceProfile.INDEX_TWO_PANEL_LANDSCAPE;
import static com.android.launcher3.InvariantDeviceProfile.INDEX_TWO_PANEL_PORTRAIT;
import static com.android.launcher3.Utilities.dpiFromPx;
import static com.android.launcher3.Utilities.pxFromSp;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.config.FeatureFlags.ENABLE_MULTI_DISPLAY_PARTIAL_DEPTH;
import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.ICON_OVERLAP_FACTOR;
import static com.android.launcher3.icons.GraphicsUtils.getShapePath;
@@ -53,9 +53,14 @@
import com.android.launcher3.icons.DotRenderer;
import com.android.launcher3.icons.IconNormalizer;
import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.responsive.AllAppsSpecs;
+import com.android.launcher3.responsive.CalculatedAllAppsSpec;
+import com.android.launcher3.responsive.CalculatedFolderSpec;
+import com.android.launcher3.responsive.FolderSpecs;
import com.android.launcher3.uioverrides.ApiWrapper;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.DisplayController.Info;
+import com.android.launcher3.util.IconSizeSteps;
import com.android.launcher3.util.ResourceHelper;
import com.android.launcher3.util.WindowBounds;
import com.android.launcher3.workspace.CalculatedWorkspaceSpec;
@@ -80,6 +85,7 @@
public final InvariantDeviceProfile inv;
private final Info mInfo;
private final DisplayMetrics mMetrics;
+ private final IconSizeSteps mIconSizeSteps;
// Device properties
public final boolean isTablet;
@@ -104,7 +110,7 @@
public final float aspectRatio;
- public final boolean isScalableGrid;
+ private final boolean mIsScalableGrid;
private final int mTypeIndex;
// Responsive grid
@@ -112,6 +118,12 @@
private WorkspaceSpecs mWorkspaceSpecs;
private CalculatedWorkspaceSpec mResponsiveWidthSpec;
private CalculatedWorkspaceSpec mResponsiveHeightSpec;
+ private AllAppsSpecs mAllAppsSpecs;
+ private CalculatedAllAppsSpec mAllAppsResponsiveWidthSpec;
+ private CalculatedAllAppsSpec mAllAppsResponsiveHeightSpec;
+ private FolderSpecs mFolderSpecs;
+ private CalculatedFolderSpec mResponsiveFolderWidthSpec;
+ private CalculatedFolderSpec mResponsiveFolderHeightSpec;
/**
* The maximum amount of left/right workspace padding as a percentage of the screen width.
@@ -153,13 +165,14 @@
public int iconTextSizePx;
public int iconDrawablePaddingPx;
public int iconDrawablePaddingOriginalPx;
+ public boolean iconCenterVertically;
public float cellScaleToFit;
public int cellWidthPx;
public int cellHeightPx;
public int workspaceCellPaddingXPx;
- public int cellYPaddingPx;
+ public int cellYPaddingPx = -1;
// Folder
public float folderLabelTextScale;
@@ -169,7 +182,7 @@
public int folderIconOffsetYPx;
// Folder content
- public int folderCellLayoutBorderSpacePx;
+ public Point folderCellLayoutBorderSpacePx;
public int folderContentPaddingLeftRight;
public int folderContentPaddingTop;
@@ -304,9 +317,11 @@
mInsets.set(windowBounds.insets);
// TODO(b/241386436): shouldn't change any launcher behaviour
- mIsResponsiveGrid = inv.workspaceSpecsId != INVALID_RESOURCE_HANDLE;
+ mIsResponsiveGrid = inv.workspaceSpecsId != INVALID_RESOURCE_HANDLE
+ && inv.allAppsSpecsId != INVALID_RESOURCE_HANDLE
+ && inv.folderSpecsId != INVALID_RESOURCE_HANDLE;
- isScalableGrid = inv.isScalable && !isVerticalBarLayout() && !isMultiWindowMode;
+ mIsScalableGrid = inv.isScalable && !isVerticalBarLayout() && !isMultiWindowMode;
// Determine device posture.
mInfo = info;
isTablet = info.isTablet(windowBounds);
@@ -322,6 +337,8 @@
final Resources res = context.getResources();
mMetrics = res.getDisplayMetrics();
+ mIconSizeSteps = mIsResponsiveGrid ? new IconSizeSteps(res) : null;
+
// Determine sizes.
widthPx = windowBounds.bounds.width();
heightPx = windowBounds.bounds.height();
@@ -343,14 +360,6 @@
}
}
- if (mIsResponsiveGrid) {
- mWorkspaceSpecs = new WorkspaceSpecs(new ResourceHelper(context, inv.workspaceSpecsId));
- mResponsiveWidthSpec = mWorkspaceSpecs.getCalculatedWidthSpec(inv.numColumns,
- availableWidthPx);
- mResponsiveHeightSpec = mWorkspaceSpecs.getCalculatedHeightSpec(inv.numRows,
- availableHeightPx);
- }
-
if (DisplayController.isTransientTaskbar(context)) {
float invTransientIconSizeDp = inv.transientTaskbarIconSize[mTypeIndex];
taskbarIconSize = pxFromDp(invTransientIconSizeDp, mMetrics);
@@ -373,8 +382,6 @@
edgeMarginPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin);
workspaceContentScale = res.getFloat(R.dimen.workspace_content_scale);
- desiredWorkspaceHorizontalMarginPx = getHorizontalMarginPx(inv, res);
- desiredWorkspaceHorizontalMarginOriginalPx = desiredWorkspaceHorizontalMarginPx;
gridVisualizationPaddingX = res.getDimensionPixelSize(
R.dimen.grid_visualization_horizontal_cell_spacing);
gridVisualizationPaddingY = res.getDimensionPixelSize(
@@ -407,7 +414,7 @@
folderLabelTextScale = res.getFloat(R.dimen.folder_label_text_scale);
- if (isScalableGrid && inv.folderStyle != INVALID_RESOURCE_HANDLE) {
+ if (mIsScalableGrid && inv.folderStyle != INVALID_RESOURCE_HANDLE) {
TypedArray folderStyle = context.obtainStyledAttributes(inv.folderStyle,
R.styleable.FolderStyle);
// These are re-set in #updateFolderCellSize if the grid is not scalable
@@ -418,19 +425,19 @@
folderContentPaddingTop = folderStyle.getDimensionPixelSize(
R.styleable.FolderStyle_folderTopPadding, 0);
- folderCellLayoutBorderSpacePx = folderStyle.getDimensionPixelSize(
+
+ int gutter = folderStyle.getDimensionPixelSize(
R.styleable.FolderStyle_folderBorderSpace, 0);
+ folderCellLayoutBorderSpacePx = new Point(gutter, gutter);
folderFooterHeightPx = folderStyle.getDimensionPixelSize(
R.styleable.FolderStyle_folderFooterHeight, 0);
folderStyle.recycle();
- } else {
- folderCellLayoutBorderSpacePx = 0;
+ } else if (!mIsResponsiveGrid) {
+ folderCellLayoutBorderSpacePx = new Point(0, 0);
folderFooterHeightPx = res.getDimensionPixelSize(R.dimen.folder_footer_height_default);
folderContentPaddingTop = res.getDimensionPixelSize(R.dimen.folder_top_padding_default);
}
- cellLayoutBorderSpacePx = getCellLayoutBorderSpace(inv);
- cellLayoutBorderSpaceOriginalPx = new Point(cellLayoutBorderSpacePx);
allAppsBorderSpacePx = new Point(
pxFromDp(inv.allAppsBorderSpaces[mTypeIndex].x, mMetrics),
pxFromDp(inv.allAppsBorderSpaces[mTypeIndex].y, mMetrics));
@@ -480,7 +487,7 @@
|| inv.inlineQsb[INDEX_TWO_PANEL_LANDSCAPE]
: inv.inlineQsb[INDEX_DEFAULT] || inv.inlineQsb[INDEX_LANDSCAPE])
&& hotseatQsbHeight > 0;
- isQsbInline = isScalableGrid && inv.inlineQsb[mTypeIndex] && canQsbInline;
+ isQsbInline = mIsScalableGrid && inv.inlineQsb[mTypeIndex] && canQsbInline;
areNavButtonsInline = isTaskbarPresent && !isGestureMode;
numShownHotseatIcons =
@@ -535,6 +542,38 @@
hotseatBarEndOffset = 0;
}
+ // Needs to be calculated after hotseatBarSizePx is correct,
+ // for the available height to be correct
+ if (mIsResponsiveGrid) {
+ mWorkspaceSpecs = new WorkspaceSpecs(new ResourceHelper(context, inv.workspaceSpecsId));
+ int availableResponsiveWidth =
+ availableWidthPx - (isVerticalBarLayout() ? hotseatBarSizePx : 0);
+ // don't use availableHeightPx because it subtracts bottom padding,
+ // but the workspace go behind it
+ int availableResponsiveHeight =
+ heightPx - mInsets.top - (isVerticalBarLayout() ? 0 : hotseatBarSizePx);
+ mResponsiveWidthSpec = mWorkspaceSpecs.getCalculatedWidthSpec(inv.numColumns,
+ availableResponsiveWidth);
+ mResponsiveHeightSpec = mWorkspaceSpecs.getCalculatedHeightSpec(inv.numRows,
+ availableResponsiveHeight);
+
+ mAllAppsSpecs = new AllAppsSpecs(new ResourceHelper(context, inv.allAppsSpecsId));
+ mAllAppsResponsiveWidthSpec = mAllAppsSpecs.getCalculatedWidthSpec(inv.numColumns,
+ mResponsiveWidthSpec.getAvailableSpace(), mResponsiveWidthSpec);
+ mAllAppsResponsiveHeightSpec = mAllAppsSpecs.getCalculatedHeightSpec(inv.numRows,
+ mResponsiveHeightSpec.getAvailableSpace(),
+ mResponsiveHeightSpec);
+
+ mFolderSpecs = new FolderSpecs(new ResourceHelper(context, inv.folderSpecsId));
+ mResponsiveFolderWidthSpec = mFolderSpecs.getWidthSpec(inv.numFolderColumns,
+ mResponsiveWidthSpec.getAvailableSpace(), mResponsiveWidthSpec);
+ mResponsiveFolderHeightSpec = mFolderSpecs.getHeightSpec(inv.numFolderRows,
+ mResponsiveHeightSpec.getAvailableSpace(), mResponsiveHeightSpec);
+ }
+
+ desiredWorkspaceHorizontalMarginPx = getHorizontalMarginPx(inv, res);
+ desiredWorkspaceHorizontalMarginOriginalPx = desiredWorkspaceHorizontalMarginPx;
+
overviewTaskMarginPx = res.getDimensionPixelSize(R.dimen.overview_task_margin);
overviewTaskIconSizePx = res.getDimensionPixelSize(R.dimen.task_thumbnail_icon_size);
overviewTaskIconDrawableSizePx =
@@ -542,7 +581,9 @@
overviewTaskIconDrawableSizeGridPx =
res.getDimensionPixelSize(R.dimen.task_thumbnail_icon_drawable_size_grid);
overviewTaskThumbnailTopMarginPx = overviewTaskIconSizePx + overviewTaskMarginPx;
- overviewActionsTopMarginPx = res.getDimensionPixelSize(R.dimen.overview_actions_top_margin);
+ // Don't add margin with floating search bar to minimize risk of overlapping.
+ overviewActionsTopMarginPx = FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get() ? 0
+ : res.getDimensionPixelSize(R.dimen.overview_actions_top_margin);
overviewPageSpacing = res.getDimensionPixelSize(R.dimen.overview_page_spacing);
overviewActionsButtonSpacing = res.getDimensionPixelSize(
R.dimen.overview_actions_button_spacing);
@@ -555,21 +596,7 @@
// Calculate all of the remaining variables.
extraSpace = updateAvailableDimensions(res);
- // Now that we have all of the variables calculated, we can tune certain sizes.
- if (isScalableGrid && inv.devicePaddingId != INVALID_RESOURCE_HANDLE) {
- // Paddings were created assuming no scaling, so we first unscale the extra space.
- int unscaledExtraSpace = (int) (extraSpace / cellScaleToFit);
- DevicePaddings devicePaddings = new DevicePaddings(context, inv.devicePaddingId);
- DevicePadding padding = devicePaddings.getDevicePadding(unscaledExtraSpace);
- maxEmptySpace = padding.getMaxEmptySpacePx();
-
- int paddingWorkspaceTop = padding.getWorkspaceTopPadding(unscaledExtraSpace);
- int paddingWorkspaceBottom = padding.getWorkspaceBottomPadding(unscaledExtraSpace);
- int paddingHotseatBottom = padding.getHotseatBottomPadding(unscaledExtraSpace);
-
- workspaceTopPadding = Math.round(paddingWorkspaceTop * cellScaleToFit);
- workspaceBottomPadding = Math.round(paddingWorkspaceBottom * cellScaleToFit);
- }
+ calculateAndSetWorkspaceVerticalPadding(context, inv, extraSpace);
int cellLayoutPadding =
isTwoPanels ? cellLayoutBorderSpacePx.x / 2 : res.getDimensionPixelSize(
@@ -650,15 +677,40 @@
}
private int getHorizontalMarginPx(InvariantDeviceProfile idp, Resources res) {
+ if (mIsResponsiveGrid) {
+ return mResponsiveWidthSpec.getStartPaddingPx();
+ }
+
if (isVerticalBarLayout()) {
return 0;
}
- return isScalableGrid
+ return mIsScalableGrid
? pxFromDp(idp.horizontalMargin[mTypeIndex], mMetrics)
: res.getDimensionPixelSize(R.dimen.dynamic_grid_left_right_margin);
}
+ private void calculateAndSetWorkspaceVerticalPadding(Context context,
+ InvariantDeviceProfile inv,
+ int extraSpace) {
+ if (mIsResponsiveGrid) {
+ workspaceTopPadding = mResponsiveHeightSpec.getStartPaddingPx();
+ workspaceBottomPadding = mResponsiveHeightSpec.getEndPaddingPx();
+ } else if (mIsScalableGrid && inv.devicePaddingId != INVALID_RESOURCE_HANDLE) {
+ // Paddings were created assuming no scaling, so we first unscale the extra space.
+ int unscaledExtraSpace = (int) (extraSpace / cellScaleToFit);
+ DevicePaddings devicePaddings = new DevicePaddings(context, inv.devicePaddingId);
+ DevicePadding padding = devicePaddings.getDevicePadding(unscaledExtraSpace);
+ maxEmptySpace = padding.getMaxEmptySpacePx();
+
+ int paddingWorkspaceTop = padding.getWorkspaceTopPadding(unscaledExtraSpace);
+ int paddingWorkspaceBottom = padding.getWorkspaceBottomPadding(unscaledExtraSpace);
+
+ workspaceTopPadding = Math.round(paddingWorkspaceTop * cellScaleToFit);
+ workspaceBottomPadding = Math.round(paddingWorkspaceBottom * cellScaleToFit);
+ }
+ }
+
/** Updates hotseatCellHeightPx and hotseatBarSizePx */
private void updateHotseatSizes(int hotseatIconSizePx) {
// Ensure there is enough space for folder icons, which have a slightly larger radius.
@@ -683,7 +735,7 @@
* necessary.
*/
public void recalculateHotseatWidthAndBorderSpace() {
- if (!isScalableGrid) return;
+ if (!mIsScalableGrid) return;
int columns = inv.hotseatColumnSpan[mTypeIndex];
float hotseatWidthPx = getIconToIconWidthForColumns(columns);
@@ -736,12 +788,16 @@
}
private Point getCellLayoutBorderSpace(InvariantDeviceProfile idp, float scale) {
- if (!isScalableGrid) {
- return new Point(0, 0);
- }
+ int horizontalSpacePx = 0;
+ int verticalSpacePx = 0;
- int horizontalSpacePx = pxFromDp(idp.borderSpaces[mTypeIndex].x, mMetrics, scale);
- int verticalSpacePx = pxFromDp(idp.borderSpaces[mTypeIndex].y, mMetrics, scale);
+ if (mIsResponsiveGrid) {
+ horizontalSpacePx = mResponsiveWidthSpec.getGutterPx();
+ verticalSpacePx = mResponsiveHeightSpec.getGutterPx();
+ } else if (mIsScalableGrid) {
+ horizontalSpacePx = pxFromDp(idp.borderSpaces[mTypeIndex].x, mMetrics, scale);
+ verticalSpacePx = pxFromDp(idp.borderSpaces[mTypeIndex].y, mMetrics, scale);
+ }
return new Point(horizontalSpacePx, verticalSpacePx);
}
@@ -817,44 +873,6 @@
}
/**
- * Re-computes the all-apps cell size to be independent of workspace
- */
- public void autoResizeAllAppsCells() {
- int textHeight = Utilities.calculateTextHeight(allAppsIconTextSizePx);
- int topBottomPadding = textHeight;
- allAppsCellHeightPx = allAppsIconSizePx + allAppsIconDrawablePaddingPx
- + textHeight + (topBottomPadding * 2);
- }
-
- private void updateAllAppsContainerWidth(Resources res) {
- int cellLayoutHorizontalPadding =
- (cellLayoutPaddingPx.left + cellLayoutPaddingPx.right) / 2;
- if (isTablet) {
- int usedWidth = (allAppsCellWidthPx * numShownAllAppsColumns)
- + (allAppsBorderSpacePx.x * (numShownAllAppsColumns - 1))
- + allAppsLeftRightPadding * 2;
- allAppsLeftRightMargin = Math.max(1, (availableWidthPx - usedWidth) / 2);
- } else {
- allAppsLeftRightPadding =
- desiredWorkspaceHorizontalMarginPx + cellLayoutHorizontalPadding;
- }
- }
-
- private void setupAllAppsStyle(Context context) {
- TypedArray allAppsStyle;
- if (inv.allAppsStyle != INVALID_RESOURCE_HANDLE) {
- allAppsStyle = context.obtainStyledAttributes(inv.allAppsStyle,
- R.styleable.AllAppsStyle);
- } else {
- allAppsStyle = context.obtainStyledAttributes(R.style.AllAppsStyleDefault,
- R.styleable.AllAppsStyle);
- }
- allAppsLeftRightPadding = allAppsStyle.getDimensionPixelSize(
- R.styleable.AllAppsStyle_horizontalPadding, 0);
- allAppsStyle.recycle();
- }
-
- /**
* Returns the amount of extra (or unused) vertical space.
*/
private int updateAvailableDimensions(Resources res) {
@@ -862,6 +880,7 @@
float invIconTextSizeSp = inv.iconTextSize[mTypeIndex];
iconSizePx = Math.max(1, pxFromDp(invIconSizeDp, mMetrics));
iconTextSizePx = pxFromSp(invIconTextSizeSp, mMetrics);
+ iconCenterVertically = mIsScalableGrid || mIsResponsiveGrid;
updateIconSize(1f, res);
@@ -875,7 +894,7 @@
boolean shouldScale = scaleY < 1f;
float scaleX = 1f;
- if (isScalableGrid) {
+ if (mIsScalableGrid) {
// We scale to fit the cellWidth and cellHeight in the available space.
// The benefit of scalable grids is that we can get consistent aspect ratios between
// devices.
@@ -920,8 +939,41 @@
final boolean isVerticalLayout = isVerticalBarLayout();
iconDrawablePaddingPx = (int) (iconDrawablePaddingOriginalPx * iconScale);
cellLayoutBorderSpacePx = getCellLayoutBorderSpace(inv, scale);
+ int cellTextAndPaddingHeight =
+ iconDrawablePaddingPx + Utilities.calculateTextHeight(iconTextSizePx);
- if (isScalableGrid) {
+ if (mIsResponsiveGrid) {
+ int cellContentHeight = iconSizePx + cellTextAndPaddingHeight;
+
+ cellWidthPx = mResponsiveWidthSpec.getCellSizePx();
+ cellHeightPx = mResponsiveHeightSpec.getCellSizePx();
+
+ if (cellWidthPx < iconSizePx) {
+ // get a smaller icon size
+ iconSizePx = mIconSizeSteps.getIconSmallerThan(cellWidthPx);
+ // calculate new cellContentHeight
+ cellContentHeight = iconSizePx + cellTextAndPaddingHeight;
+ }
+
+ while (iconSizePx > mIconSizeSteps.minimumIconSize()
+ && cellContentHeight > cellHeightPx) {
+ int extraHeightRequired = cellContentHeight - cellHeightPx;
+ int newPadding = iconDrawablePaddingPx - extraHeightRequired;
+ if (newPadding >= 0) {
+ // Responsive uses the padding without scaling
+ iconDrawablePaddingPx = iconDrawablePaddingOriginalPx = newPadding;
+ cellTextAndPaddingHeight =
+ iconDrawablePaddingPx + Utilities.calculateTextHeight(iconTextSizePx);
+ } else {
+ // get a smaller icon size
+ iconSizePx = mIconSizeSteps.getNextLowerIconSize(iconSizePx);
+ }
+ // calculate new cellContentHeight
+ cellContentHeight = iconSizePx + cellTextAndPaddingHeight;
+ }
+
+ cellYPaddingPx = Math.max(0, cellHeightPx - cellContentHeight) / 2;
+ } else if (mIsScalableGrid) {
cellWidthPx = pxFromDp(inv.minCellSize[mTypeIndex].x, mMetrics, scale);
cellHeightPx = pxFromDp(inv.minCellSize[mTypeIndex].y, mMetrics, scale);
@@ -943,8 +995,6 @@
}
}
- int cellTextAndPaddingHeight =
- iconDrawablePaddingPx + Utilities.calculateTextHeight(iconTextSizePx);
int cellContentHeight = iconSizePx + cellTextAndPaddingHeight;
if (cellHeightPx < cellContentHeight) {
// If cellHeight no longer fit iconSize, reduce borderSpace to make cellHeight
@@ -996,7 +1046,15 @@
}
// All apps
- updateAllAppsIconSize(scale, res);
+ if (mIsResponsiveGrid) {
+ updateAllAppsWithResponsiveMeasures();
+ } else {
+ updateAllAppsIconSize(scale, res);
+ }
+ updateAllAppsContainerWidth();
+ if (isVerticalBarLayout()) {
+ hideWorkspaceLabelsIfNotEnoughSpace();
+ }
updateHotseatSizes(iconSizePx);
@@ -1042,7 +1100,7 @@
+ allAppsBorderSpacePx.y;
// but width is just the cell,
// the border is added in #updateAllAppsContainerWidth
- if (isScalableGrid) {
+ if (mIsScalableGrid) {
allAppsIconSizePx = pxFromDp(inv.allAppsIconSize[mTypeIndex], mMetrics);
allAppsIconTextSizePx = pxFromSp(inv.allAppsIconTextSize[mTypeIndex], mMetrics);
allAppsIconDrawablePaddingPx = iconDrawablePaddingOriginalPx;
@@ -1082,22 +1140,71 @@
res.getDimensionPixelSize(R.dimen.all_apps_icon_drawable_padding);
allAppsCellWidthPx = allAppsIconSizePx + (2 * allAppsIconDrawablePaddingPx);
}
+ }
- updateAllAppsContainerWidth(res);
- if (isVerticalBarLayout()) {
- hideWorkspaceLabelsIfNotEnoughSpace();
+ private void updateAllAppsWithResponsiveMeasures() {
+ allAppsIconSizePx = iconSizePx;
+ allAppsIconTextSizePx = iconTextSizePx;
+ allAppsIconDrawablePaddingPx = iconDrawablePaddingOriginalPx;
+
+ allAppsBorderSpacePx = new Point(
+ mAllAppsResponsiveWidthSpec.getGutterPx(),
+ mAllAppsResponsiveHeightSpec.getGutterPx()
+ );
+ allAppsCellHeightPx = mAllAppsResponsiveHeightSpec.getCellSizePx()
+ + mAllAppsResponsiveHeightSpec.getGutterPx();
+ allAppsCellWidthPx = mAllAppsResponsiveWidthSpec.getCellSizePx();
+ allAppsLeftRightPadding = mAllAppsResponsiveWidthSpec.getStartPaddingPx();
+ }
+
+ /**
+ * Re-computes the all-apps cell size to be independent of workspace
+ */
+ public void autoResizeAllAppsCells() {
+ int textHeight = Utilities.calculateTextHeight(allAppsIconTextSizePx);
+ int topBottomPadding = textHeight;
+ allAppsCellHeightPx = allAppsIconSizePx + allAppsIconDrawablePaddingPx
+ + textHeight + (topBottomPadding * 2);
+ }
+
+ private void updateAllAppsContainerWidth() {
+ int cellLayoutHorizontalPadding =
+ (cellLayoutPaddingPx.left + cellLayoutPaddingPx.right) / 2;
+ if (isTablet) {
+ int usedWidth = (allAppsCellWidthPx * numShownAllAppsColumns)
+ + (allAppsBorderSpacePx.x * (numShownAllAppsColumns - 1))
+ + allAppsLeftRightPadding * 2;
+ allAppsLeftRightMargin = Math.max(1, (availableWidthPx - usedWidth) / 2);
+ } else {
+ allAppsLeftRightPadding =
+ Math.max(0, desiredWorkspaceHorizontalMarginPx + cellLayoutHorizontalPadding
+ - (allAppsBorderSpacePx.x / 2));
}
}
+ private void setupAllAppsStyle(Context context) {
+ TypedArray allAppsStyle = context.obtainStyledAttributes(
+ inv.allAppsStyle != INVALID_RESOURCE_HANDLE ? inv.allAppsStyle
+ : R.style.AllAppsStyleDefault, R.styleable.AllAppsStyle);
+
+ allAppsLeftRightPadding = allAppsStyle.getDimensionPixelSize(
+ R.styleable.AllAppsStyle_horizontalPadding, 0);
+ allAppsStyle.recycle();
+ }
+
+ // TODO(b/288075868): Resize the icon size to make sure it will fit inside the cell size
private void updateAvailableFolderCellDimensions(Resources res) {
updateFolderCellSize(1f, res);
+ // Responsive grid doesn't need to scale the folder
+ if (mIsResponsiveGrid) return;
+
// For usability we can't have the folder use the whole width of the screen
Point totalWorkspacePadding = getTotalWorkspacePadding();
// Check if the folder fit within the available height.
float contentUsedHeight = folderCellHeightPx * inv.numFolderRows
- + ((inv.numFolderRows - 1) * folderCellLayoutBorderSpacePx)
+ + ((inv.numFolderRows - 1) * folderCellLayoutBorderSpacePx.y)
+ folderFooterHeightPx
+ folderContentPaddingTop;
int contentMaxHeight = availableHeightPx - totalWorkspacePadding.y;
@@ -1105,7 +1212,7 @@
// Check if the folder fit within the available width.
float contentUsedWidth = folderCellWidthPx * inv.numFolderColumns
- + ((inv.numFolderColumns - 1) * folderCellLayoutBorderSpacePx)
+ + ((inv.numFolderColumns - 1) * folderCellLayoutBorderSpacePx.x)
+ folderContentPaddingLeftRight * 2;
int contentMaxWidth = availableWidthPx - totalWorkspacePadding.x;
float scaleX = contentMaxWidth / contentUsedWidth;
@@ -1125,7 +1232,19 @@
int textHeight = Utilities.calculateTextHeight(folderChildTextSizePx);
- if (isScalableGrid) {
+ if (mIsResponsiveGrid) {
+ folderCellWidthPx = mResponsiveFolderWidthSpec.getCellSizePx();
+
+ // Height
+ folderCellHeightPx = mResponsiveFolderHeightSpec.getCellSizePx();
+ folderContentPaddingTop = mResponsiveFolderHeightSpec.getStartPaddingPx();
+ folderFooterHeightPx = mResponsiveFolderHeightSpec.getEndPaddingPx();
+
+ folderCellLayoutBorderSpacePx = new Point(mResponsiveFolderWidthSpec.getGutterPx(),
+ mResponsiveHeightSpec.getGutterPx());
+
+ folderContentPaddingLeftRight = mResponsiveFolderWidthSpec.getStartPaddingPx();
+ } else if (mIsScalableGrid) {
if (inv.folderStyle == INVALID_RESOURCE_HANDLE) {
folderCellWidthPx = roundPxValueFromFloat(getCellSize().x * scale);
folderCellHeightPx = roundPxValueFromFloat(getCellSize().y * scale);
@@ -1135,11 +1254,13 @@
}
folderContentPaddingTop = roundPxValueFromFloat(folderContentPaddingTop * scale);
- folderCellLayoutBorderSpacePx = roundPxValueFromFloat(
- folderCellLayoutBorderSpacePx * scale);
+ folderCellLayoutBorderSpacePx = new Point(
+ roundPxValueFromFloat(folderCellLayoutBorderSpacePx.x * scale),
+ roundPxValueFromFloat(folderCellLayoutBorderSpacePx.y * scale)
+ );
folderFooterHeightPx = roundPxValueFromFloat(folderFooterHeightPx * scale);
- folderContentPaddingLeftRight = folderCellLayoutBorderSpacePx;
+ folderContentPaddingLeftRight = folderCellLayoutBorderSpacePx.x;
} else {
int cellPaddingX = (int) (res.getDimensionPixelSize(R.dimen.folder_cell_x_padding)
* scale);
@@ -1300,10 +1421,12 @@
} else {
// Pad the bottom of the workspace with hotseat bar
// and leave a bit of space in case a widget go all the way down
- int paddingBottom = hotseatBarSizePx + workspaceBottomPadding
- + workspacePageIndicatorHeight - mWorkspacePageIndicatorOverlapWorkspace
- - mInsets.bottom;
- int paddingTop = workspaceTopPadding + (isScalableGrid ? 0 : edgeMarginPx);
+ int paddingBottom = hotseatBarSizePx + workspaceBottomPadding - mInsets.bottom;
+ if (!mIsResponsiveGrid) {
+ paddingBottom +=
+ workspacePageIndicatorHeight - mWorkspacePageIndicatorOverlapWorkspace;
+ }
+ int paddingTop = workspaceTopPadding + (mIsScalableGrid ? 0 : edgeMarginPx);
int paddingSide = desiredWorkspaceHorizontalMarginPx;
padding.set(paddingSide, paddingTop, paddingSide, paddingBottom);
@@ -1379,7 +1502,7 @@
hotseatBarPadding.right = endSpacing;
}
- } else if (isScalableGrid) {
+ } else if (mIsScalableGrid) {
int sideSpacing = (availableWidthPx - hotseatQsbWidth) / 2;
hotseatBarPadding.set(sideSpacing,
0,
@@ -1404,6 +1527,25 @@
return hotseatBarPadding;
}
+ /** The margin between the edge of all apps and the edge of the first icon. */
+ public int getAllAppsIconStartMargin() {
+ int allAppsSpacing;
+ if (isVerticalBarLayout()) {
+ // On phones, the landscape layout uses a different setup.
+ allAppsSpacing = workspacePadding.left + workspacePadding.right;
+ } else {
+ allAppsSpacing = allAppsLeftRightPadding * 2 + allAppsLeftRightMargin * 2;
+ }
+
+ int cellWidth = DeviceProfile.calculateCellWidth(
+ availableWidthPx - allAppsSpacing,
+ 0 /* borderSpace */,
+ numShownAllAppsColumns);
+ int iconVisibleSize = Math.round(ICON_VISIBLE_AREA_FACTOR * allAppsIconSizePx);
+ int iconAlignmentMargin = (cellWidth - iconVisibleSize) / 2;
+ return allAppsLeftRightPadding + iconAlignmentMargin;
+ }
+
private int getAdditionalQsbSpace() {
return isQsbInline ? hotseatQsbWidth + hotseatBorderSpace : 0;
}
@@ -1599,7 +1741,7 @@
writer.println(prefix + "\taspectRatio:" + aspectRatio);
writer.println(prefix + "\tisResponsiveGrid:" + mIsResponsiveGrid);
- writer.println(prefix + "\tisScalableGrid:" + isScalableGrid);
+ writer.println(prefix + "\tisScalableGrid:" + mIsScalableGrid);
writer.println(prefix + "\tinv.numRows: " + inv.numRows);
writer.println(prefix + "\tinv.numColumns: " + inv.numColumns);
@@ -1639,8 +1781,10 @@
writer.println(prefix + pxToDpStr("folderChildTextSizePx", folderChildTextSizePx));
writer.println(prefix + pxToDpStr("folderChildDrawablePaddingPx",
folderChildDrawablePaddingPx));
- writer.println(prefix + pxToDpStr("folderCellLayoutBorderSpacePx",
- folderCellLayoutBorderSpacePx));
+ writer.println(prefix + pxToDpStr("folderCellLayoutBorderSpacePx.x",
+ folderCellLayoutBorderSpacePx.x));
+ writer.println(prefix + pxToDpStr("folderCellLayoutBorderSpacePx.y",
+ folderCellLayoutBorderSpacePx.y));
writer.println(prefix + pxToDpStr("folderContentPaddingLeftRight",
folderContentPaddingLeftRight));
writer.println(prefix + pxToDpStr("folderTopPadding", folderContentPaddingTop));
@@ -1753,6 +1897,16 @@
getWorkspaceSpringLoadScale(context)));
writer.println(prefix + pxToDpStr("getCellLayoutHeight()", getCellLayoutHeight()));
writer.println(prefix + pxToDpStr("getCellLayoutWidth()", getCellLayoutWidth()));
+ if (mIsResponsiveGrid) {
+ writer.println(prefix + "\tmResponsiveHeightSpec:" + mResponsiveHeightSpec.toString());
+ writer.println(prefix + "\tmResponsiveWidthSpec:" + mResponsiveWidthSpec.toString());
+ writer.println(prefix + "\tmAllAppsResponsiveHeightSpec:"
+ + mAllAppsResponsiveHeightSpec.toString());
+ writer.println(prefix + "\tmAllAppsResponsiveWidthSpec:"
+ + mAllAppsResponsiveWidthSpec.toString());
+ writer.println(prefix + "\tmResponsiveFolderHeightSpec:" + mResponsiveFolderHeightSpec);
+ writer.println(prefix + "\tmResponsiveFolderWidthSpec:" + mResponsiveFolderWidthSpec);
+ }
}
/** Returns a reduced representation of this DeviceProfile. */
diff --git a/src/com/android/launcher3/DropTargetBar.java b/src/com/android/launcher3/DropTargetBar.java
index addcac9..8bdf61a 100644
--- a/src/com/android/launcher3/DropTargetBar.java
+++ b/src/com/android/launcher3/DropTargetBar.java
@@ -30,7 +30,7 @@
import android.view.ViewPropertyAnimator;
import android.widget.FrameLayout;
-import com.android.launcher3.anim.Interpolators;
+import com.android.app.animation.Interpolators;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragController.DragListener;
import com.android.launcher3.dragndrop.DragOptions;
@@ -42,7 +42,7 @@
implements DragListener, Insettable {
protected static final int DEFAULT_DRAG_FADE_DURATION = 175;
- protected static final TimeInterpolator DEFAULT_INTERPOLATOR = Interpolators.ACCEL;
+ protected static final TimeInterpolator DEFAULT_INTERPOLATOR = Interpolators.ACCELERATE;
private final Runnable mFadeAnimationEndRunnable =
() -> updateVisibility(DropTargetBar.this);
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 376f54d..4eaacdc 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -18,7 +18,6 @@
import static com.android.launcher3.LauncherPrefs.GRID_NAME;
import static com.android.launcher3.Utilities.dpiFromPx;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_TWO_PANEL_HOME;
import static com.android.launcher3.testing.shared.ResourceUtils.INVALID_RESOURCE_HANDLE;
import static com.android.launcher3.util.DisplayController.CHANGE_DENSITY;
import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
@@ -180,6 +179,10 @@
public int devicePaddingId = INVALID_RESOURCE_HANDLE;
@XmlRes
public int workspaceSpecsId = INVALID_RESOURCE_HANDLE;
+ @XmlRes
+ public int allAppsSpecsId = INVALID_RESOURCE_HANDLE;
+ @XmlRes
+ public int folderSpecsId = INVALID_RESOURCE_HANDLE;
public String dbFile;
public int defaultLayoutId;
@@ -302,7 +305,7 @@
int type = displayInfo.supportedBounds.stream()
.mapToInt(bounds -> displayInfo.isTablet(bounds) ? flagTablet : flagPhone)
.reduce(0, (a, b) -> a | b);
- if ((type == (flagPhone | flagTablet)) && ENABLE_TWO_PANEL_HOME.get()) {
+ if ((type == (flagPhone | flagTablet))) {
// device has profiles supporting both phone and table modes
return TYPE_MULTI_DISPLAY;
} else if (type == flagTablet) {
@@ -354,6 +357,8 @@
isScalable = closestProfile.isScalable;
devicePaddingId = closestProfile.devicePaddingId;
workspaceSpecsId = closestProfile.mWorkspaceSpecsId;
+ allAppsSpecsId = closestProfile.mAllAppsSpecsId;
+ folderSpecsId = closestProfile.mFolderSpecsId;
this.deviceType = deviceType;
inlineNavButtonsEndSpacing = closestProfile.inlineNavButtonsEndSpacing;
@@ -800,6 +805,8 @@
private final boolean isScalable;
private final int devicePaddingId;
private final int mWorkspaceSpecsId;
+ private final int mAllAppsSpecsId;
+ private final int mFolderSpecsId;
public GridOption(Context context, AttributeSet attrs) {
TypedArray a = context.obtainStyledAttributes(
@@ -864,8 +871,14 @@
if (FeatureFlags.ENABLE_RESPONSIVE_WORKSPACE.get()) {
mWorkspaceSpecsId = a.getResourceId(
R.styleable.GridDisplayOption_workspaceSpecsId, INVALID_RESOURCE_HANDLE);
+ mAllAppsSpecsId = a.getResourceId(
+ R.styleable.GridDisplayOption_allAppsSpecsId, INVALID_RESOURCE_HANDLE);
+ mFolderSpecsId = a.getResourceId(
+ R.styleable.GridDisplayOption_folderSpecsId, INVALID_RESOURCE_HANDLE);
} else {
mWorkspaceSpecsId = INVALID_RESOURCE_HANDLE;
+ mAllAppsSpecsId = INVALID_RESOURCE_HANDLE;
+ mFolderSpecsId = INVALID_RESOURCE_HANDLE;
}
int inlineForRotation = a.getInt(R.styleable.GridDisplayOption_inlineQsb,
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 25eb160..7e43002 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -21,6 +21,7 @@
import static android.content.pm.ActivityInfo.CONFIG_UI_MODE;
import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
+import static com.android.app.animation.Interpolators.EMPHASIZED;
import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
import static com.android.launcher3.AbstractFloatingView.TYPE_FOLDER;
import static com.android.launcher3.AbstractFloatingView.TYPE_ICON_SURFACE;
@@ -43,8 +44,6 @@
import static com.android.launcher3.LauncherState.SPRING_LOADED;
import static com.android.launcher3.Utilities.postAsyncCallback;
import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.getSupportedActions;
-import static com.android.launcher3.anim.Interpolators.EMPHASIZED;
-import static com.android.launcher3.config.FeatureFlags.FOLDABLE_SINGLE_PAGE;
import static com.android.launcher3.config.FeatureFlags.MULTI_SELECT_EDIT_MODE;
import static com.android.launcher3.config.FeatureFlags.SHOW_DOT_PAGINATION;
import static com.android.launcher3.logging.StatsLogManager.EventEnum;
@@ -183,7 +182,6 @@
import com.android.launcher3.notification.NotificationListener;
import com.android.launcher3.pageindicators.WorkspacePageIndicator;
import com.android.launcher3.pm.PinRequestHelper;
-import com.android.launcher3.pm.UserCache;
import com.android.launcher3.popup.ArrowPopup;
import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.popup.PopupDataProvider;
@@ -208,7 +206,6 @@
import com.android.launcher3.util.PendingRequestArgs;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.RunnableList;
-import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.util.ScreenOnTracker;
import com.android.launcher3.util.ScreenOnTracker.ScreenOnListener;
import com.android.launcher3.util.SystemUiController;
@@ -411,8 +408,6 @@
protected long mLastTouchUpTime = -1;
private boolean mTouchInProgress;
- private SafeCloseable mUserChangedCallbackCloseable;
-
// New InstanceId is assigned to mAllAppsSessionLogId for each AllApps sessions.
// When Launcher is not in AllApps state mAllAppsSessionLogId will be null.
// User actions within AllApps state are logged with this InstanceId, to recreate AllApps
@@ -445,8 +440,7 @@
Trace.beginAsyncSection(DISPLAY_ALL_APPS_TRACE_METHOD_NAME,
DISPLAY_ALL_APPS_TRACE_COOKIE);
}
- Object traceToken = TraceHelper.INSTANCE.beginSection(ON_CREATE_EVT,
- TraceHelper.FLAG_UI_EVENT);
+ TraceHelper.INSTANCE.beginSection(ON_CREATE_EVT);
if (DEBUG_STRICT_MODE) {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
@@ -580,10 +574,7 @@
LauncherOverlayPlugin.class, false /* allowedMultiple */);
mRotationHelper.initialize();
- TraceHelper.INSTANCE.endSection(traceToken);
-
- mUserChangedCallbackCloseable = UserCache.INSTANCE.get(this).addUserChangeListener(
- () -> getStateManager().goToState(NORMAL));
+ TraceHelper.INSTANCE.endSection();
if (Utilities.ATLEAST_R) {
getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
@@ -768,7 +759,7 @@
}
onDeviceProfileInitiated();
- if (FOLDABLE_SINGLE_PAGE.get() && mDeviceProfile.isTwoPanels) {
+ if (mDeviceProfile.isTwoPanels) {
mCellPosMapper = new TwoPanelCellPosMapper(mDeviceProfile.inv.numColumns);
} else {
mCellPosMapper = CellPosMapper.DEFAULT;
@@ -1079,15 +1070,14 @@
@Override
protected void onStart() {
- Object traceToken = TraceHelper.INSTANCE.beginSection(ON_START_EVT,
- TraceHelper.FLAG_UI_EVENT);
+ TraceHelper.INSTANCE.beginSection(ON_START_EVT);
super.onStart();
if (!mDeferOverlayCallbacks) {
mOverlayManager.onActivityStarted(this);
}
mAppWidgetHolder.setActivityStarted(true);
- TraceHelper.INSTANCE.endSection(traceToken);
+ TraceHelper.INSTANCE.endSection();
}
@Override
@@ -1258,8 +1248,7 @@
@Override
protected void onResume() {
- Object traceToken = TraceHelper.INSTANCE.beginSection(ON_RESUME_EVT,
- TraceHelper.FLAG_UI_EVENT);
+ TraceHelper.INSTANCE.beginSection(ON_RESUME_EVT);
super.onResume();
if (mDeferOverlayCallbacks) {
@@ -1269,7 +1258,7 @@
}
DragView.removeAllViews(this);
- TraceHelper.INSTANCE.endSection(traceToken);
+ TraceHelper.INSTANCE.endSection();
}
@Override
@@ -1657,7 +1646,7 @@
if (Utilities.isRunningInTestHarness()) {
Log.d(TestProtocol.PERMANENT_DIAG_TAG, "Launcher.onNewIntent: " + intent);
}
- Object traceToken = TraceHelper.INSTANCE.beginSection(ON_NEW_INTENT_EVT);
+ TraceHelper.INSTANCE.beginSection(ON_NEW_INTENT_EVT);
super.onNewIntent(intent);
boolean alreadyOnHome = hasWindowFocus() && ((intent.getFlags() &
@@ -1704,7 +1693,7 @@
showAllAppsWorkTabFromIntent(alreadyOnHome);
}
- TraceHelper.INSTANCE.endSection(traceToken);
+ TraceHelper.INSTANCE.endSection();
}
protected void toggleAllAppsFromIntent(boolean alreadyOnHome) {
@@ -1808,7 +1797,6 @@
LauncherAppState.getIDP(this).removeOnChangeListener(this);
mOverlayManager.onActivityDestroyed(this);
- mUserChangedCallbackCloseable.close();
}
public LauncherAccessibilityDelegate getAccessibilityDelegate() {
@@ -1933,7 +1921,7 @@
case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
addAppWidgetFromDrop((PendingAddWidgetInfo) info);
break;
- case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
+ case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
processShortcutFromDrop((PendingAddShortcutInfo) info);
break;
default:
@@ -2303,7 +2291,7 @@
* Implementation of the method from LauncherModel.Callbacks.
*/
public void startBinding() {
- Object traceToken = TraceHelper.INSTANCE.beginSection("startBinding");
+ TraceHelper.INSTANCE.beginSection("startBinding");
// Floating panels (except the full widget sheet) are associated with individual icons. If
// we are starting a fresh bind, close all such panels as all the icons are about
// to go away.
@@ -2321,7 +2309,7 @@
if (mHotseat != null) {
mHotseat.resetLayout(getDeviceProfile().isVerticalBarLayout());
}
- TraceHelper.INSTANCE.endSection(traceToken);
+ TraceHelper.INSTANCE.endSection();
}
@Override
@@ -2440,7 +2428,6 @@
final View view;
switch (item.itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
- case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT: {
WorkspaceItemInfo info = (WorkspaceItemInfo) item;
view = createShortcut(info);
@@ -2575,7 +2562,7 @@
return view;
}
- Object traceToken = TraceHelper.INSTANCE.beginSection("BIND_WIDGET_id=" + item.appWidgetId);
+ TraceHelper.INSTANCE.beginSection("BIND_WIDGET_id=" + item.appWidgetId);
try {
final LauncherAppWidgetProviderInfo appWidgetInfo;
@@ -2705,7 +2692,7 @@
}
prepareAppWidget(view, item);
} finally {
- TraceHelper.INSTANCE.endSection(traceToken);
+ TraceHelper.INSTANCE.endSection();
}
return view;
@@ -2798,7 +2785,7 @@
* Implementation of the method from LauncherModel.Callbacks.
*/
public void finishBindingItems(IntSet pagesBoundFirst) {
- Object traceToken = TraceHelper.INSTANCE.beginSection("finishBindingItems");
+ TraceHelper.INSTANCE.beginSection("finishBindingItems");
mWorkspace.restoreInstanceStateForRemainingPages();
setWorkspaceLoading(false);
@@ -2823,7 +2810,7 @@
mDeviceProfile.inv.numFolderColumns * mDeviceProfile.inv.numFolderRows);
getViewCache().setCacheSize(R.layout.folder_page, 2);
- TraceHelper.INSTANCE.endSection(traceToken);
+ TraceHelper.INSTANCE.endSection();
mWorkspace.removeExtraEmptyScreen(true);
}
@@ -2979,9 +2966,14 @@
public void bindAllApplications(AppInfo[] apps, int flags,
Map<PackageUserKey, Integer> packageUserKeytoUidMap) {
Preconditions.assertUIThread();
+ boolean hadWorkApps = mAppsView.shouldShowTabs();
AllAppsStore appsStore = mAppsView.getAppsStore();
appsStore.setApps(apps, flags, packageUserKeytoUidMap);
PopupContainerWithArrow.dismissInvalidPopup(this);
+ if (hadWorkApps != mAppsView.shouldShowTabs()) {
+ getStateManager().goToState(NORMAL);
+ }
+
if (Utilities.ATLEAST_S) {
Trace.endAsyncSection(DISPLAY_ALL_APPS_TRACE_METHOD_NAME,
DISPLAY_ALL_APPS_TRACE_COOKIE);
@@ -3053,6 +3045,7 @@
@Override
public void bindStringCache(StringCache cache) {
mStringCache = cache;
+ mAppsView.updateWorkUI();
}
@Override
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 4d15ac7..eb1c4d4 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -62,6 +62,7 @@
public static final String ACTION_FORCE_ROLOAD = "force-reload-launcher";
public static final String KEY_ICON_STATE = "pref_icon_shape_path";
+ public static final String KEY_ALL_APPS_OVERVIEW_THRESHOLD = "pref_all_apps_overview_threshold";
// We do not need any synchronization for this variable as its only written on UI thread.
public static final MainThreadInitializedObject<LauncherAppState> INSTANCE =
@@ -104,14 +105,12 @@
});
mContext.getSystemService(LauncherApps.class).registerCallback(mModel);
+ mOnTerminateCallback.add(() ->
+ mContext.getSystemService(LauncherApps.class).unregisterCallback(mModel));
SimpleBroadcastReceiver modelChangeReceiver =
new SimpleBroadcastReceiver(mModel::onBroadcastIntent);
modelChangeReceiver.register(mContext, Intent.ACTION_LOCALE_CHANGED,
- Intent.ACTION_MANAGED_PROFILE_AVAILABLE,
- Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE,
- Intent.ACTION_MANAGED_PROFILE_UNLOCKED,
- Intent.ACTION_PROFILE_INACCESSIBLE,
ACTION_DEVICE_POLICY_RESOURCE_UPDATED);
if (FeatureFlags.IS_STUDIO_BUILD) {
modelChangeReceiver.register(mContext, ACTION_FORCE_ROLOAD);
@@ -119,12 +118,13 @@
mOnTerminateCallback.add(() -> mContext.unregisterReceiver(modelChangeReceiver));
SafeCloseable userChangeListener = UserCache.INSTANCE.get(mContext)
- .addUserChangeListener(mModel::forceReload);
+ .addUserEventListener(mModel::onUserEvent);
mOnTerminateCallback.add(userChangeListener::close);
LockedUserState.get(context).runOnUserUnlocked(() -> {
- CustomWidgetManager.INSTANCE.get(mContext)
- .setWidgetRefreshCallback(mModel::refreshAndBindWidgetsAndShortcuts);
+ CustomWidgetManager cwm = CustomWidgetManager.INSTANCE.get(mContext);
+ cwm.setWidgetRefreshCallback(mModel::refreshAndBindWidgetsAndShortcuts);
+ mOnTerminateCallback.add(() -> cwm.setWidgetRefreshCallback(null));
IconObserver observer = new IconObserver();
SafeCloseable iconChangeTracker = mIconProvider.registerIconChangeListener(
@@ -159,6 +159,7 @@
mModel = new LauncherModel(context, this, mIconCache, new AppFilter(mContext),
iconCacheFileName != null);
mOnTerminateCallback.add(mIconCache::close);
+ mOnTerminateCallback.add(mModel::destroy);
}
private void onNotificationSettingsChanged(boolean areNotificationDotsEnabled) {
@@ -180,9 +181,6 @@
*/
@Override
public void close() {
- mModel.destroy();
- mContext.getSystemService(LauncherApps.class).unregisterCallback(mModel);
- CustomWidgetManager.INSTANCE.get(mContext).setWidgetRefreshCallback(null);
mOnTerminateCallback.executeAllAndDestroy();
}
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 4e066b0..ddafd53 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -95,15 +95,6 @@
static final String TAG = "Launcher.Model";
- // Broadcast intent to track when the profile gets locked:
- // ACTION_MANAGED_PROFILE_UNAVAILABLE can be used until Android U where profile no longer gets
- // locked when paused.
- // ACTION_PROFILE_INACCESSIBLE always means that the profile is getting locked but it only
- // appeared in Android S.
- private static final String ACTION_PROFILE_LOCKED = Utilities.ATLEAST_U
- ? Intent.ACTION_PROFILE_INACCESSIBLE
- : Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE;
-
@NonNull
private final LauncherAppState mApp;
@NonNull
@@ -143,6 +134,8 @@
@NonNull
private final ModelDelegate mModelDelegate;
+ private int mLastLoadId = -1;
+
// Runnable to check if the shortcuts permission has changed.
@NonNull
private final Runnable mDataValidationCheck = new Runnable() {
@@ -301,28 +294,6 @@
if (Intent.ACTION_LOCALE_CHANGED.equals(action)) {
// If we have changed locale we need to clear out the labels in all apps/workspace.
forceReload();
- } else if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action)
- || Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action)
- || Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action)
- || Intent.ACTION_PROFILE_INACCESSIBLE.equals(action)) {
- UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER);
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.WORK_TAB_MISSING, "onBroadcastIntent intentAction: " + action +
- " user: " + user);
- }
- if (user != null) {
- if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action) ||
- Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action)) {
- enqueueModelUpdateTask(new PackageUpdatedTask(
- PackageUpdatedTask.OP_USER_AVAILABILITY_CHANGE, user));
- }
-
- if (ACTION_PROFILE_LOCKED.equals(action)
- || Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action)) {
- enqueueModelUpdateTask(new UserLockStateChangedTask(
- user, Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action)));
- }
- }
} else if (ACTION_DEVICE_POLICY_RESOURCE_UPDATED.equals(action)) {
enqueueModelUpdateTask(new ReloadStringCacheTask(mModelDelegate));
} else if (IS_STUDIO_BUILD && ACTION_FORCE_ROLOAD.equals(action)) {
@@ -335,6 +306,33 @@
}
/**
+ * Called then there use a user event
+ * @see UserCache#addUserEventListener
+ */
+ public void onUserEvent(UserHandle user, String action) {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.WORK_TAB_MISSING, "onBroadcastIntent intentAction: "
+ + action + " user: " + user);
+ }
+
+ if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action)
+ || Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action)) {
+ enqueueModelUpdateTask(new PackageUpdatedTask(
+ PackageUpdatedTask.OP_USER_AVAILABILITY_CHANGE, user));
+ }
+
+ if (UserCache.ACTION_PROFILE_LOCKED.equals(action)
+ || UserCache.ACTION_PROFILE_UNLOCKED.equals(action)) {
+ enqueueModelUpdateTask(new UserLockStateChangedTask(
+ user, UserCache.ACTION_PROFILE_UNLOCKED.equals(action)));
+ }
+ if (UserCache.ACTION_PROFILE_ADDED.equals(action)
+ || UserCache.ACTION_PROFILE_REMOVED.equals(action)) {
+ forceReload();
+ }
+ }
+
+ /**
* Reloads the workspace items from the DB and re-binds the workspace. This should generally
* not be called as DB updates are automatically followed by UI update
*/
@@ -553,6 +551,7 @@
if (mLoaderTask != task) {
throw new CancellationException("Loader already stopped");
}
+ mLastLoadId++;
mTask = task;
mIsLoaderTaskRunning = true;
mModelLoaded = false;
@@ -722,4 +721,12 @@
return mCallbacksList.toArray(new Callbacks[mCallbacksList.size()]);
}
}
+
+ /**
+ * Returns the ID for the last model load. If the load ID doesn't match for a transaction, the
+ * transaction should be ignored.
+ */
+ public int getLastLoadId() {
+ return mLastLoadId;
+ }
}
diff --git a/src/com/android/launcher3/LauncherPrefs.kt b/src/com/android/launcher3/LauncherPrefs.kt
index c98df1b..177f883 100644
--- a/src/com/android/launcher3/LauncherPrefs.kt
+++ b/src/com/android/launcher3/LauncherPrefs.kt
@@ -275,6 +275,9 @@
const val TASKBAR_PINNING_KEY = "TASKBAR_PINNING_KEY"
@JvmField val ICON_STATE = nonRestorableItem(LauncherAppState.KEY_ICON_STATE, "", true)
+ @JvmField
+ val ALL_APPS_OVERVIEW_THRESHOLD =
+ nonRestorableItem(LauncherAppState.KEY_ALL_APPS_OVERVIEW_THRESHOLD, 200, true)
@JvmField val THEMED_ICONS = backedUpItem(Themes.KEY_THEMED_ICONS, false, true)
@JvmField val PROMISE_ICON_IDS = backedUpItem(InstallSessionHelper.PROMISE_ICON_IDS, "")
@JvmField val WORK_EDU_STEP = backedUpItem(WorkProfileManager.KEY_WORK_EDU_STEP, 0)
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 9abec50..440e146 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -16,21 +16,18 @@
package com.android.launcher3;
-import android.annotation.TargetApi;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+
import android.appwidget.AppWidgetManager;
import android.content.ComponentName;
import android.content.ContentProvider;
-import android.content.ContentProviderOperation;
-import android.content.ContentProviderResult;
import android.content.ContentUris;
import android.content.ContentValues;
-import android.content.OperationApplicationException;
import android.database.Cursor;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.os.Binder;
-import android.os.Build;
-import android.os.Bundle;
import android.os.Process;
import android.text.TextUtils;
import android.util.Log;
@@ -38,12 +35,11 @@
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.ModelDbController;
-import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
import com.android.launcher3.widget.LauncherWidgetHolder;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.util.ArrayList;
+import java.util.function.ToIntFunction;
public class LauncherProvider extends ContentProvider {
private static final String TAG = "LauncherProvider";
@@ -74,10 +70,6 @@
return true;
}
- public ModelDbController getModelDbController() {
- return LauncherAppState.getInstance(getContext()).getModel().getModelDbController();
- }
-
@Override
public String getType(Uri uri) {
SqlArguments args = new SqlArguments(uri, null, null);
@@ -91,180 +83,91 @@
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
-
SqlArguments args = new SqlArguments(uri, selection, selectionArgs);
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
qb.setTables(args.table);
- Cursor result = getModelDbController().query(
- args.table, projection, args.where, args.args, sortOrder);
- result.setNotificationUri(getContext().getContentResolver(), uri);
- return result;
- }
-
- private void reloadLauncherIfExternal() {
- if (Binder.getCallingPid() != Process.myPid()) {
- LauncherAppState app = LauncherAppState.getInstanceNoCreate();
- if (app != null) {
- app.getModel().forceReload();
- }
- }
+ Cursor[] result = new Cursor[1];
+ executeControllerTask(controller -> {
+ result[0] = controller.query(args.table, projection, args.where, args.args, sortOrder);
+ return 0;
+ });
+ return result[0];
}
@Override
- public Uri insert(Uri uri, ContentValues initialValues) {
- // In very limited cases, we support system|signature permission apps to modify the db.
- if (Binder.getCallingPid() != Process.myPid()) {
- if (!initializeExternalAdd(initialValues)) {
- return null;
- }
- }
+ public Uri insert(Uri uri, ContentValues values) {
+ int rowId = executeControllerTask(controller -> {
+ // 1. Ensure that externally added items have a valid item id
+ int id = controller.generateNewItemId();
+ values.put(LauncherSettings.Favorites._ID, id);
- SqlArguments args = new SqlArguments(uri);
- int rowId = getModelDbController().insert(args.table, initialValues);
- if (rowId < 0) return null;
+ // 2. In the case of an app widget, and if no app widget id is specified, we
+ // attempt allocate and bind the widget.
+ Integer itemType = values.getAsInteger(Favorites.ITEM_TYPE);
+ if (itemType != null
+ && itemType.intValue() == Favorites.ITEM_TYPE_APPWIDGET
+ && !values.containsKey(Favorites.APPWIDGET_ID)) {
- uri = ContentUris.withAppendedId(uri, rowId);
- reloadLauncherIfExternal();
- return uri;
- }
+ ComponentName cn = ComponentName.unflattenFromString(
+ values.getAsString(Favorites.APPWIDGET_PROVIDER));
+ if (cn == null) {
+ return 0;
+ }
- private boolean initializeExternalAdd(ContentValues values) {
- // 1. Ensure that externally added items have a valid item id
- int id = getModelDbController().generateNewItemId();
- values.put(LauncherSettings.Favorites._ID, id);
-
- // 2. In the case of an app widget, and if no app widget id is specified, we
- // attempt allocate and bind the widget.
- Integer itemType = values.getAsInteger(LauncherSettings.Favorites.ITEM_TYPE);
- if (itemType != null &&
- itemType.intValue() == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET &&
- !values.containsKey(LauncherSettings.Favorites.APPWIDGET_ID)) {
-
- final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(getContext());
- ComponentName cn = ComponentName.unflattenFromString(
- values.getAsString(Favorites.APPWIDGET_PROVIDER));
-
- if (cn != null) {
LauncherWidgetHolder widgetHolder = LauncherWidgetHolder.newInstance(getContext());
try {
int appWidgetId = widgetHolder.allocateAppWidgetId();
values.put(LauncherSettings.Favorites.APPWIDGET_ID, appWidgetId);
- if (!appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId,cn)) {
+ if (!AppWidgetManager.getInstance(getContext())
+ .bindAppWidgetIdIfAllowed(appWidgetId, cn)) {
widgetHolder.deleteAppWidgetId(appWidgetId);
- return false;
+ return 0;
}
} catch (RuntimeException e) {
Log.e(TAG, "Failed to initialize external widget", e);
- return false;
+ return 0;
} finally {
// Necessary to destroy the holder to free up possible activity context
widgetHolder.destroy();
}
- } else {
- return false;
}
- }
- return true;
- }
+ SqlArguments args = new SqlArguments(uri);
+ return controller.insert(args.table, values);
+ });
- @Override
- public int bulkInsert(Uri uri, ContentValues[] values) {
- SqlArguments args = new SqlArguments(uri);
- getModelDbController().bulkInsert(args.table, values);
- reloadLauncherIfExternal();
- return values.length;
- }
-
- @TargetApi(Build.VERSION_CODES.M)
- @Override
- public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
- throws OperationApplicationException {
- try (SQLiteTransaction t = getModelDbController().newTransaction()) {
- final int numOperations = operations.size();
- final ContentProviderResult[] results = new ContentProviderResult[numOperations];
- for (int i = 0; i < numOperations; i++) {
- ContentProviderOperation op = operations.get(i);
- results[i] = op.apply(this, results, i);
- }
- t.commit();
- reloadLauncherIfExternal();
- return results;
- }
+ return rowId < 0 ? null : ContentUris.withAppendedId(uri, rowId);
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
SqlArguments args = new SqlArguments(uri, selection, selectionArgs);
- int count = getModelDbController().delete(args.table, args.where, args.args);
- if (count > 0) {
- reloadLauncherIfExternal();
- }
- return count;
+ return executeControllerTask(c -> c.delete(args.table, args.where, args.args));
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
SqlArguments args = new SqlArguments(uri, selection, selectionArgs);
- int count = getModelDbController().update(args.table, values, args.where, args.args);
- reloadLauncherIfExternal();
- return count;
+ return executeControllerTask(c -> c.update(args.table, values, args.where, args.args));
}
- @Override
- public Bundle call(String method, final String arg, final Bundle extras) {
- if (Binder.getCallingUid() != Process.myUid()) {
- return null;
+ private int executeControllerTask(ToIntFunction<ModelDbController> task) {
+ if (Binder.getCallingPid() == Process.myPid()) {
+ throw new IllegalArgumentException("Same process should call model directly");
}
-
- switch (method) {
- case LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG: {
- getModelDbController().clearEmptyDbFlag();
- return null;
- }
- case LauncherSettings.Settings.METHOD_DELETE_EMPTY_FOLDERS: {
- Bundle result = new Bundle();
- result.putIntArray(LauncherSettings.Settings.EXTRA_VALUE,
- getModelDbController().deleteEmptyFolders().toArray());
- return result;
- }
- case LauncherSettings.Settings.METHOD_NEW_ITEM_ID: {
- Bundle result = new Bundle();
- result.putInt(LauncherSettings.Settings.EXTRA_VALUE,
- getModelDbController().generateNewItemId());
- return result;
- }
- case LauncherSettings.Settings.METHOD_NEW_SCREEN_ID: {
- Bundle result = new Bundle();
- result.putInt(LauncherSettings.Settings.EXTRA_VALUE,
- getModelDbController().getNewScreenId());
- return result;
- }
- case LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB: {
- getModelDbController().createEmptyDB();
- return null;
- }
- case LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES: {
- getModelDbController().loadDefaultFavoritesIfNecessary();
- return null;
- }
- case LauncherSettings.Settings.METHOD_REMOVE_GHOST_WIDGETS: {
- getModelDbController().removeGhostWidgets();
- return null;
- }
- case LauncherSettings.Settings.METHOD_NEW_TRANSACTION: {
- Bundle result = new Bundle();
- result.putBinder(LauncherSettings.Settings.EXTRA_VALUE,
- getModelDbController().newTransaction());
- return result;
- }
- case LauncherSettings.Settings.METHOD_REFRESH_HOTSEAT_RESTORE_TABLE: {
- getModelDbController().refreshHotseatRestoreTable();
- return null;
- }
+ try {
+ return MODEL_EXECUTOR.submit(() -> {
+ LauncherModel model = LauncherAppState.getInstance(getContext()).getModel();
+ int count = task.applyAsInt(model.getModelDbController());
+ if (count > 0) {
+ MAIN_EXECUTOR.submit(model::forceReload);
+ }
+ return count;
+ }).get();
+ } catch (Exception e) {
+ throw new IllegalStateException(e);
}
- return null;
}
static class SqlArguments {
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index 7fda326..105d5f3 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -16,10 +16,7 @@
package com.android.launcher3;
-import android.content.ContentResolver;
import android.database.sqlite.SQLiteDatabase;
-import android.net.Uri;
-import android.os.Bundle;
import android.provider.BaseColumns;
import com.android.launcher3.model.data.ItemInfo;
@@ -89,7 +86,9 @@
/**
* The gesture is an application created shortcut
+ * @deprecated This is no longer supported. Use {@link #ITEM_TYPE_DEEP_SHORTCUT} instead
*/
+ @Deprecated
public static final int ITEM_TYPE_SHORTCUT = 1;
/**
@@ -153,24 +152,6 @@
public static final String TMP_TABLE = "favorites_tmp";
/**
- * The content:// style URL for "favorites" table
- */
- public static final Uri CONTENT_URI = Uri.parse("content://"
- + LauncherProvider.AUTHORITY + "/" + TABLE_NAME);
-
- /**
- * The content:// style URL for a given row, identified by its id.
- *
- * @param id The row id.
- *
- * @return The unique content URL for the specified row.
- */
- public static Uri getContentUri(int id) {
- return Uri.parse("content://" + LauncherProvider.AUTHORITY
- + "/" + TABLE_NAME + "/" + id);
- }
-
- /**
* The container holding the favorite
* <P>Type: INTEGER</P>
*/
@@ -213,7 +194,6 @@
public static final String itemTypeToString(int type) {
switch(type) {
case ITEM_TYPE_APPLICATION: return "APP";
- case ITEM_TYPE_SHORTCUT: return "SHORTCUT";
case ITEM_TYPE_FOLDER: return "FOLDER";
case ITEM_TYPE_APPWIDGET: return "WIDGET";
case ITEM_TYPE_CUSTOM_APPWIDGET: return "CUSTOMWIDGET";
@@ -338,42 +318,8 @@
* Launcher settings
*/
public static final class Settings {
-
- public static final Uri CONTENT_URI = Uri.parse("content://" +
- LauncherProvider.AUTHORITY + "/settings");
-
- public static final String METHOD_CLEAR_EMPTY_DB_FLAG = "clear_empty_db_flag";
-
- public static final String METHOD_DELETE_EMPTY_FOLDERS = "delete_empty_folders";
-
- public static final String METHOD_NEW_ITEM_ID = "generate_new_item_id";
- public static final String METHOD_NEW_SCREEN_ID = "generate_new_screen_id";
-
- public static final String METHOD_CREATE_EMPTY_DB = "create_empty_db";
-
- public static final String METHOD_LOAD_DEFAULT_FAVORITES = "load_default_favorites";
-
- public static final String METHOD_REMOVE_GHOST_WIDGETS = "remove_ghost_widgets";
-
- public static final String METHOD_NEW_TRANSACTION = "new_db_transaction";
-
- public static final String METHOD_REFRESH_HOTSEAT_RESTORE_TABLE = "restore_hotseat_table";
-
- public static final String EXTRA_VALUE = "value";
-
- public static final String EXTRA_DB_NAME = "db_name";
-
public static final String LAYOUT_DIGEST_KEY = "launcher3.layout.provider.blob";
public static final String LAYOUT_DIGEST_LABEL = "launcher-layout";
public static final String LAYOUT_DIGEST_TAG = "ignore";
-
- public static Bundle call(ContentResolver cr, String method) {
- return call(cr, method, null /* arg */);
- }
-
- public static Bundle call(ContentResolver cr, String method, String arg) {
- return cr.call(CONTENT_URI, method, arg, null);
- }
-
}
}
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 8b124dc..bfbca65 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -15,8 +15,8 @@
*/
package com.android.launcher3;
-import static com.android.launcher3.anim.Interpolators.ACCEL_2;
-import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
+import static com.android.app.animation.Interpolators.ACCELERATE_2;
+import static com.android.app.animation.Interpolators.DECELERATE_2;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
import static com.android.launcher3.testing.shared.TestProtocol.ALL_APPS_STATE_ORDINAL;
@@ -33,6 +33,7 @@
import android.content.Context;
import android.graphics.Color;
+import android.view.View;
import android.view.animation.Interpolator;
import androidx.annotation.FloatRange;
@@ -66,6 +67,7 @@
public static final int CLEAR_ALL_BUTTON = 1 << 4;
public static final int WORKSPACE_PAGE_INDICATOR = 1 << 5;
public static final int SPLIT_PLACHOLDER_VIEW = 1 << 6;
+ public static final int FLOATING_SEARCH_BAR = 1 << 7;
// Flag indicating workspace has multiple pages visible.
public static final int FLAG_MULTI_PAGE = BaseState.getFlag(0);
@@ -90,7 +92,7 @@
public static final float NO_SCALE = 1;
protected static final PageAlphaProvider DEFAULT_ALPHA_PROVIDER =
- new PageAlphaProvider(ACCEL_2) {
+ new PageAlphaProvider(ACCELERATE_2) {
@Override
public float getPageAlpha(int pageIndex) {
return 1;
@@ -98,7 +100,7 @@
};
protected static final PageTranslationProvider DEFAULT_PAGE_TRANSLATION_PROVIDER =
- new PageTranslationProvider(DEACCEL_2) {
+ new PageTranslationProvider(DECELERATE_2) {
@Override
public float getPageTranslation(int pageIndex) {
return 0;
@@ -202,8 +204,61 @@
return 0;
}
+ /**
+ * How far from the bottom of the screen the <em>floating</em> search bar should rest in this
+ * state when the IME is not present.
+ * <p>
+ * To hide offscreen, use a negative value.
+ * <p>
+ * Note: if the provided value is non-negative but less than the current bottom insets, the
+ * insets will be applied. As such, you can use 0 to default to this.
+ */
+ public int getFloatingSearchBarRestingMarginBottom(Launcher launcher) {
+ DeviceProfile dp = launcher.getDeviceProfile();
+ return areElementsVisible(launcher, FLOATING_SEARCH_BAR) ? dp.getQsbOffsetY()
+ : -dp.hotseatQsbHeight;
+ }
+
+ /**
+ * How far from the start of the screen the <em>floating</em> search bar should rest.
+ * <p>
+ * To use original margin, return a negative value.
+ */
+ public int getFloatingSearchBarRestingMarginStart(Launcher launcher) {
+ boolean isRtl = Utilities.isRtl(launcher.getResources());
+ View qsb = launcher.getHotseat().getQsb();
+ return isRtl ? launcher.getHotseat().getRight() - qsb.getRight() : qsb.getLeft();
+ }
+
+ /**
+ * How far from the end of the screen the <em>floating</em> search bar should rest.
+ * <p>
+ * To use original margin, return a negative value.
+ */
+ public int getFloatingSearchBarRestingMarginEnd(Launcher launcher) {
+ DeviceProfile dp = launcher.getDeviceProfile();
+ if (dp.isQsbInline) {
+ int marginStart = getFloatingSearchBarRestingMarginStart(launcher);
+ return dp.widthPx - marginStart - dp.hotseatQsbWidth;
+ }
+
+ boolean isRtl = Utilities.isRtl(launcher.getResources());
+ View qsb = launcher.getHotseat().getQsb();
+ return isRtl ? qsb.getLeft() : launcher.getHotseat().getRight() - qsb.getRight();
+ }
+
+ /** Whether the <em>floating</em> search bar should use the pill UI when not focused. */
+ public boolean shouldFloatingSearchBarUsePillWhenUnfocused(Launcher launcher) {
+ return false;
+ }
+
public int getVisibleElements(Launcher launcher) {
- return HOTSEAT_ICONS | WORKSPACE_PAGE_INDICATOR | VERTICAL_SWIPE_INDICATOR;
+ int elements = HOTSEAT_ICONS | WORKSPACE_PAGE_INDICATOR | VERTICAL_SWIPE_INDICATOR;
+ // Floating search bar is visible in normal state except in landscape on phones.
+ if (!(launcher.getDeviceProfile().isPhone && launcher.getDeviceProfile().isLandscape)) {
+ elements |= FLOATING_SEARCH_BAR;
+ }
+ return elements;
}
/**
@@ -319,7 +374,7 @@
return DEFAULT_ALPHA_PROVIDER;
}
final int centerPage = launcher.getWorkspace().getNextPage();
- return new PageAlphaProvider(ACCEL_2) {
+ return new PageAlphaProvider(ACCELERATE_2) {
@Override
public float getPageAlpha(int pageIndex) {
return pageIndex != centerPage ? 0 : 1f;
@@ -337,7 +392,7 @@
return DEFAULT_PAGE_TRANSLATION_PROVIDER;
}
final float quarterPageSpacing = launcher.getWorkspace().getPageSpacing() / 4f;
- return new PageTranslationProvider(DEACCEL_2) {
+ return new PageTranslationProvider(DECELERATE_2) {
@Override
public float getPageTranslation(int pageIndex) {
boolean isRtl = launcher.getWorkspace().mIsRtl;
diff --git a/src/com/android/launcher3/MotionEventsUtils.java b/src/com/android/launcher3/MotionEventsUtils.java
index 40de003..3228ec6 100644
--- a/src/com/android/launcher3/MotionEventsUtils.java
+++ b/src/com/android/launcher3/MotionEventsUtils.java
@@ -30,6 +30,9 @@
/** {@link MotionEvent#CLASSIFICATION_MULTI_FINGER_SWIPE} is hidden. */
public static final int CLASSIFICATION_MULTI_FINGER_SWIPE = 4;
+ /** {@link MotionEvent#AXIS_GESTURE_SWIPE_FINGER_COUNT} is hidden. */
+ private static final int AXIS_GESTURE_SWIPE_FINGER_COUNT = 53;
+
@TargetApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
public static boolean isTrackpadScroll(MotionEvent event) {
return ENABLE_TRACKPAD_GESTURE.get()
@@ -43,11 +46,13 @@
}
public static boolean isTrackpadThreeFingerSwipe(MotionEvent event) {
- return isTrackpadMultiFingerSwipe(event) && event.getPointerCount() == 3;
+ return isTrackpadMultiFingerSwipe(event) && event.getAxisValue(
+ AXIS_GESTURE_SWIPE_FINGER_COUNT) == 3;
}
public static boolean isTrackpadFourFingerSwipe(MotionEvent event) {
- return isTrackpadMultiFingerSwipe(event) && event.getPointerCount() == 4;
+ return isTrackpadMultiFingerSwipe(event) && event.getAxisValue(
+ AXIS_GESTURE_SWIPE_FINGER_COUNT) == 4;
}
public static boolean isTrackpadMotionEvent(MotionEvent event) {
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index af64b3b..4b4a4a5 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -16,7 +16,7 @@
package com.android.launcher3;
-import static com.android.launcher3.anim.Interpolators.SCROLL;
+import static com.android.app.animation.Interpolators.SCROLL;
import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled;
import static com.android.launcher3.compat.AccessibilityManagerCompat.isObservedEventType;
import static com.android.launcher3.touch.OverScroll.OVERSCROLL_DAMP_FACTOR;
diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
index a0ceefb..f921d1d 100644
--- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java
+++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
@@ -154,14 +154,15 @@
mBorderSpace);
// Center the icon/folder
int cHeight = getCellContentHeight();
- int cellPaddingY = dp.isScalableGrid && mContainerType == WORKSPACE
- ? dp.cellYPaddingPx
- : (int) Math.max(0, ((lp.height - cHeight) / 2f));
+ int cellPaddingY =
+ dp.cellYPaddingPx >= 0 && mContainerType == WORKSPACE
+ ? dp.cellYPaddingPx
+ : (int) Math.max(0, ((lp.height - cHeight) / 2f));
// No need to add padding when cell layout border spacing is present.
boolean noPaddingX =
(dp.cellLayoutBorderSpacePx.x > 0 && mContainerType == WORKSPACE)
- || (dp.folderCellLayoutBorderSpacePx > 0 && mContainerType == FOLDER)
+ || (dp.folderCellLayoutBorderSpacePx.x > 0 && mContainerType == FOLDER)
|| (dp.hotseatBorderSpace > 0 && mContainerType == HOTSEAT);
int cellPaddingX = noPaddingX
? 0
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 73bb828..b141e18 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -28,7 +28,6 @@
import static com.android.launcher3.LauncherState.SPRING_LOADED;
import static com.android.launcher3.MotionEventsUtils.isTrackpadMultiFingerSwipe;
import static com.android.launcher3.anim.AnimatorListeners.forSuccessCallback;
-import static com.android.launcher3.config.FeatureFlags.FOLDABLE_SINGLE_PAGE;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SWIPELEFT;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SWIPERIGHT;
@@ -67,9 +66,9 @@
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
+import com.android.app.animation.Interpolators;
import com.android.launcher3.accessibility.AccessibleDragListenerAdapter;
import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper;
-import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.celllayout.CellPosMapper;
@@ -127,7 +126,6 @@
import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlayCallbacks;
import java.util.ArrayList;
-import java.util.Iterator;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -503,20 +501,15 @@
.log(LauncherEvent.LAUNCHER_ITEM_DRAG_STARTED);
}
- private boolean isTwoPanelEnabled() {
- return !FOLDABLE_SINGLE_PAGE.get() && mLauncher.mDeviceProfile.isTwoPanels;
- }
-
- @Override
- public int getPanelCount() {
- return isTwoPanelEnabled() ? 2 : super.getPanelCount();
- }
-
public void deferRemoveExtraEmptyScreen() {
mDeferRemoveExtraEmptyScreen = true;
}
@Override
+ public int getPanelCount() {
+ return super.getPanelCount();
+ }
+ @Override
public void onDragEnd() {
if (ENFORCE_DRAG_EVENT_ORDER) {
enforceDragParity("onDragEnd", 0, 0);
@@ -562,9 +555,9 @@
// Change the interpolators such that the fade animation plays before the move animation.
// This prevents empty adjacent pages to overlay during animation
mLayoutTransition.setInterpolator(LayoutTransition.DISAPPEARING,
- Interpolators.clampToProgress(Interpolators.ACCEL_DEACCEL, 0, 0.5f));
+ Interpolators.clampToProgress(Interpolators.ACCELERATE_DECELERATE, 0, 0.5f));
mLayoutTransition.setInterpolator(LayoutTransition.CHANGE_DISAPPEARING,
- Interpolators.clampToProgress(Interpolators.ACCEL_DEACCEL, 0.5f, 1));
+ Interpolators.clampToProgress(Interpolators.ACCELERATE_DECELERATE, 0.5f, 1));
mLayoutTransition.disableTransitionType(LayoutTransition.APPEARING);
mLayoutTransition.disableTransitionType(LayoutTransition.CHANGE_APPEARING);
@@ -668,7 +661,7 @@
// created CellLayout.
DeviceProfile dp = mLauncher.getDeviceProfile();
CellLayout newScreen;
- if (FOLDABLE_SINGLE_PAGE.get() && dp.isTwoPanels) {
+ if (dp.isTwoPanels) {
newScreen = (CellLayout) LayoutInflater.from(getContext()).inflate(
R.layout.workspace_screen_foldable, this, false /* attachToRoot */);
} else {
@@ -693,15 +686,6 @@
if (mDragSourceInternal != null) {
int dragSourceChildCount = mDragSourceInternal.getChildCount();
-
- // If the icon was dragged from Hotseat, there is no page pair
- if (isTwoPanelEnabled() && !(mDragSourceInternal.getParent() instanceof Hotseat)) {
- int pagePairScreenId = getScreenPair(getCellPosMapper().mapModelToPresenter(
- dragObject.dragInfo).screenId);
- CellLayout pagePair = mWorkspaceScreens.get(pagePairScreenId);
- dragSourceChildCount += pagePair.getShortcutsAndWidgets().getChildCount();
- }
-
// When the drag view content is a LauncherAppWidgetHostView, we should increment the
// drag source child count by 1 because the widget in drag has been detached from its
// original parent, ShortcutAndWidgetContainer, and reattached to the DragView.
@@ -712,11 +696,6 @@
if (dragSourceChildCount == 1) {
lastChildOnScreen = true;
}
- CellLayout cl = (CellLayout) mDragSourceInternal.getParent();
- if (!FOLDABLE_SINGLE_PAGE.get() && getLeftmostVisiblePageForIndex(indexOfChild(cl))
- == getLeftmostVisiblePageForIndex(getPageCount() - 1)) {
- childOnFinalScreen = true;
- }
}
// If this is the last item on the final screen
@@ -751,9 +730,6 @@
*/
private void forEachExtraEmptyPageId(Consumer<Integer> callback) {
callback.accept(EXTRA_EMPTY_SCREEN_ID);
- if (isTwoPanelEnabled()) {
- callback.accept(EXTRA_EMPTY_SCREEN_SECOND_ID);
- }
}
/**
@@ -867,9 +843,7 @@
public boolean hasExtraEmptyScreens() {
return mWorkspaceScreens.containsKey(EXTRA_EMPTY_SCREEN_ID)
- && getChildCount() > getPanelCount()
- && (!isTwoPanelEnabled()
- || mWorkspaceScreens.containsKey(EXTRA_EMPTY_SCREEN_SECOND_ID));
+ && getChildCount() > getPanelCount();
}
/**
@@ -897,9 +871,8 @@
mWorkspaceScreens.remove(emptyScreenId);
mScreenOrder.removeValue(emptyScreenId);
- int newScreenId = LauncherSettings.Settings.call(getContext().getContentResolver(),
- LauncherSettings.Settings.METHOD_NEW_SCREEN_ID)
- .getInt(LauncherSettings.Settings.EXTRA_VALUE);
+ int newScreenId = LauncherAppState.getInstance(getContext())
+ .getModel().getModelDbController().getNewScreenId();
// Launcher database isn't aware of empty pages that are already bound, so we need to
// skip those IDs manually.
while (mWorkspaceScreens.containsKey(newScreenId)) {
@@ -976,14 +949,7 @@
*/
@Nullable
public CellLayout getScreenPair(CellLayout cellLayout) {
- if (!isTwoPanelEnabled()) {
- return null;
- }
- int screenId = getIdForScreen(cellLayout);
- if (screenId == -1) {
- return null;
- }
- return getScreenWithId(getScreenPair(screenId));
+ return null;
}
public void stripEmptyScreens() {
@@ -1011,22 +977,6 @@
}
}
- // When two panel home is enabled we only remove an empty page if both visible pages are
- // empty.
- if (isTwoPanelEnabled()) {
- // We go through all the pages that were marked as removable and check their page pair
- Iterator<Integer> removeScreensIterator = removeScreens.iterator();
- while (removeScreensIterator.hasNext()) {
- int pageToRemove = removeScreensIterator.next();
- int pagePair = getScreenPair(pageToRemove);
- if (!removeScreens.contains(pagePair)) {
- // The page pair isn't empty so we want to remove the current page from the
- // removable pages' collection
- removeScreensIterator.remove();
- }
- }
- }
-
// We enforce at least one page (two pages on two panel home) to add new items to.
// In the case that we remove the last such screen(s), we convert the last screen(s)
// to the empty screen(s)
@@ -1047,12 +997,7 @@
removeView(cl);
} else {
// The last page(s) should be converted into extra empty page(s)
- int extraScreenId = isTwoPanelEnabled() && id % 2 == 1
- // This is the right panel in a two panel scenario
- ? EXTRA_EMPTY_SCREEN_SECOND_ID
- // This is either the last screen in a one panel scenario, or the left panel
- // in a two panel scenario when there are only two empty pages left
- : EXTRA_EMPTY_SCREEN_ID;
+ int extraScreenId = EXTRA_EMPTY_SCREEN_ID;
mWorkspaceScreens.put(extraScreenId, cl);
mScreenOrder.add(extraScreenId);
}
@@ -1840,7 +1785,6 @@
!= LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION);
boolean willBecomeShortcut =
(info.itemType == ITEM_TYPE_APPLICATION ||
- info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT ||
info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT);
return (aboveShortcut && willBecomeShortcut);
@@ -2574,8 +2518,7 @@
// Go through the pages and check if the dragged item is inside one of them. This block
// is responsible for determining whether we need to snap to a different screen.
int nextPage = getNextPage();
- IntSet pageIndexesToVerify = IntSet.wrap(nextPage - 1,
- nextPage + (isTwoPanelEnabled() ? 2 : 1));
+ IntSet pageIndexesToVerify = IntSet.wrap(nextPage - 1, nextPage + 1);
for (int pageIndex : pageIndexesToVerify) {
// When deciding whether to perform a page switch, we need to consider the most
@@ -2759,7 +2702,7 @@
final PendingAddItemInfo pendingInfo = (PendingAddItemInfo) info;
boolean findNearestVacantCell = true;
- if (pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
+ if (pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
mTargetCell = findNearestArea(touchXY[0], touchXY[1], spanX, spanY,
cellLayout, mTargetCell);
float distance = cellLayout.getDistanceFromWorkspaceCellVisualCenter(
@@ -2832,8 +2775,7 @@
View view;
switch (info.itemType) {
- case ITEM_TYPE_APPLICATION:
- case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
+ case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
case LauncherSettings.Favorites.ITEM_TYPE_SEARCH_ACTION:
if (info instanceof WorkspaceItemFactory) {
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index 565d7da..e4f34ae 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -18,6 +18,9 @@
import static androidx.dynamicanimation.animation.DynamicAnimation.MIN_VISIBLE_CHANGE_SCALE;
+import static com.android.app.animation.Interpolators.ACCELERATE_2;
+import static com.android.app.animation.Interpolators.LINEAR;
+import static com.android.app.animation.Interpolators.ZOOM_OUT;
import static com.android.launcher3.LauncherAnimUtils.HOTSEAT_SCALE_PROPERTY_FACTORY;
import static com.android.launcher3.LauncherAnimUtils.SCALE_INDEX_WORKSPACE_STATE;
import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
@@ -30,12 +33,8 @@
import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.WORKSPACE_PAGE_INDICATOR;
-import static com.android.launcher3.anim.Interpolators.ACCEL_2;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.anim.Interpolators.ZOOM_OUT;
import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
import static com.android.launcher3.graphics.Scrim.SCRIM_PROGRESS;
-import static com.android.launcher3.graphics.SysUiScrim.SYSUI_PROGRESS;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_SCALE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_TRANSLATE;
@@ -54,6 +53,7 @@
import com.android.launcher3.LauncherState.PageAlphaProvider;
import com.android.launcher3.LauncherState.PageTranslationProvider;
import com.android.launcher3.LauncherState.ScaleAndTranslation;
+import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.PropertySetter;
import com.android.launcher3.anim.SpringAnimationBuilder;
@@ -196,12 +196,12 @@
state.getWorkspaceBackgroundAlpha(mLauncher), LINEAR);
SysUiScrim sysUiScrim = mLauncher.getRootView().getSysUiScrim();
- propertySetter.setFloat(sysUiScrim, SYSUI_PROGRESS,
+ propertySetter.setFloat(sysUiScrim.getSysUIProgress(), AnimatedFloat.VALUE,
state.hasFlag(FLAG_HAS_SYS_UI_SCRIM) ? 1 : 0, LINEAR);
propertySetter.setViewBackgroundColor(mLauncher.getScrimView(),
state.getWorkspaceScrimColor(mLauncher),
- config.getInterpolator(ANIM_SCRIM_FADE, ACCEL_2));
+ config.getInterpolator(ANIM_SCRIM_FADE, ACCELERATE_2));
}
public void applyChildState(LauncherState state, CellLayout cl, int childIndex) {
diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
index d4140d8..4590125 100644
--- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
@@ -16,6 +16,8 @@
package com.android.launcher3.allapps;
import static com.android.launcher3.allapps.ActivityAllAppsContainerView.AdapterHolder.SEARCH;
+import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_WORK_DISABLED_CARD;
+import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_WORK_EDU_CARD;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_COUNT;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_TAP_ON_PERSONAL_TAB;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_TAP_ON_WORK_TAB;
@@ -70,13 +72,14 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.BaseAllAppsAdapter.AdapterItem;
-import com.android.launcher3.allapps.search.DefaultSearchAdapterProvider;
+import com.android.launcher3.allapps.search.AllAppsSearchUiDelegate;
import com.android.launcher3.allapps.search.SearchAdapterProvider;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.keyboard.FocusedItemDecorator;
import com.android.launcher3.model.StringCache;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.testing.shared.TestProtocol;
+import com.android.launcher3.util.Executors;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ActivityContext;
@@ -132,6 +135,7 @@
protected final Point mFastScrollerOffset = new Point();
protected final int mScrimColor;
protected final float mHeaderThreshold;
+ protected final AllAppsSearchUiDelegate mSearchUiDelegate;
// Used to animate Search results out and A-Z apps in, or vice-versa.
private final SearchTransitionController mSearchTransitionController;
@@ -217,11 +221,21 @@
getActiveRecyclerView().requestFocus();
}
});
+ mSearchUiDelegate = createSearchUiDelegate();
initContent();
mSearchTransitionController = new SearchTransitionController(this);
}
+ /** Creates the delegate for initializing search. */
+ protected AllAppsSearchUiDelegate createSearchUiDelegate() {
+ return new AllAppsSearchUiDelegate(this);
+ }
+
+ public AllAppsSearchUiDelegate getSearchUiDelegate() {
+ return mSearchUiDelegate;
+ }
+
/**
* Initializes the view hierarchy and internal variables. Any initialization which actually uses
* these members should be done in {@link #onFinishInflate()}.
@@ -231,7 +245,7 @@
* onFinishInflate -> onPostCreate
*/
protected void initContent() {
- mMainAdapterProvider = createMainAdapterProvider();
+ mMainAdapterProvider = mSearchUiDelegate.createMainAdapterProvider();
mAH.set(AdapterHolder.MAIN, new AdapterHolder(AdapterHolder.MAIN,
new AlphabeticalAppsList<>(mActivityContext, mAllAppsStore, null)));
@@ -248,9 +262,12 @@
mFastScroller = findViewById(R.id.fast_scroller);
mFastScroller.setPopupView(findViewById(R.id.fast_scroller_popup));
- // Add the search box above everything else.
- mSearchContainer = inflateSearchBox();
- addView(mSearchContainer);
+ mSearchContainer = inflateSearchBar();
+ if (!isSearchBarFloating()) {
+ // Add the search box above everything else in this container (if the flag is enabled,
+ // it's added to drag layer in onAttach instead).
+ addView(mSearchContainer);
+ }
mSearchUiManager = (SearchUiManager) mSearchContainer;
}
@@ -282,6 +299,13 @@
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
+ if (isSearchBarFloating()) {
+ // Note: for Taskbar this is removed in TaskbarAllAppsController#cleanUpOverlay when the
+ // panel is closed. Can't do so in onDetach because we are also a child of drag layer
+ // so can't remove its views during that dispatch.
+ mActivityContext.getDragLayer().addView(mSearchContainer);
+ mSearchUiDelegate.onInitializeSearchBar();
+ }
mActivityContext.addOnDeviceProfileChangeListener(this);
}
@@ -303,7 +327,7 @@
* Temporarily force the bottom sheet to be visible on non-tablets.
*
* @param force {@code true} means bottom sheet will be visible on phones until {@code reset()}.
- **/
+ */
public void forceBottomSheetVisible(boolean force) {
mForceBottomSheetVisible = force;
updateBackgroundVisibility(mActivityContext.getDeviceProfile());
@@ -341,6 +365,7 @@
*/
public void setSearchResults(ArrayList<AdapterItem> results, int searchResultCode) {
setSearchResults(results);
+ mSearchUiDelegate.onSearchResultsChanged(results, searchResultCode);
}
private void animateToSearchState(boolean goingToSearch) {
@@ -412,7 +437,7 @@
* A-Z apps list.
*
* @param animate Whether to animate the header during the reset (e.g. switching profile tabs).
- **/
+ */
public void reset(boolean animate) {
reset(animate, true);
}
@@ -422,7 +447,7 @@
*
* @param animate Whether to animate the header during the reset (e.g. switching profile tabs).
* @param exitSearch Whether to force exit the search state and return to A-Z apps list.
- **/
+ */
public void reset(boolean animate, boolean exitSearch) {
for (int i = 0; i < mAH.size(); i++) {
if (mAH.get(i).mRecyclerView != null) {
@@ -485,6 +510,9 @@
// Will be called at the end of the animation.
return;
}
+ if (currentActivePage != SEARCH) {
+ mActivityContext.hideKeyboard();
+ }
if (mAH.get(currentActivePage).mRecyclerView != null) {
mAH.get(currentActivePage).mRecyclerView.bindFastScrollbar(mFastScroller);
}
@@ -542,7 +570,6 @@
mActivityContext.getStatsLogManager().logger()
.log(LAUNCHER_ALLAPPS_TAP_ON_PERSONAL_TAB);
}
- mActivityContext.hideKeyboard();
});
findViewById(R.id.tab_work)
.setOnClickListener((View view) -> {
@@ -550,24 +577,24 @@
mActivityContext.getStatsLogManager().logger()
.log(LAUNCHER_ALLAPPS_TAP_ON_WORK_TAB);
}
- mActivityContext.hideKeyboard();
});
setDeviceManagementResources();
- onActivePageChanged(mViewPager.getNextPage());
+ if (mHeader.isSetUp()) {
+ onActivePageChanged(mViewPager.getNextPage());
+ }
} else {
mAH.get(AdapterHolder.MAIN).setup(findViewById(R.id.apps_list_view), null);
mAH.get(AdapterHolder.WORK).mRecyclerView = null;
}
setupHeader();
- if (isSearchBarOnBottom()) {
+ if (isSearchBarFloating()) {
// Keep the scroller above the search bar.
RelativeLayout.LayoutParams scrollerLayoutParams =
(LayoutParams) mFastScroller.getLayoutParams();
- scrollerLayoutParams.addRule(RelativeLayout.ABOVE, R.id.search_container_all_apps);
- scrollerLayoutParams.removeRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
- scrollerLayoutParams.bottomMargin = getResources().getDimensionPixelSize(
- R.dimen.fastscroll_bottom_margin_floating_search);
+ scrollerLayoutParams.bottomMargin = mSearchContainer.getHeight()
+ + getResources().getDimensionPixelSize(
+ R.dimen.fastscroll_bottom_margin_floating_search);
}
mAllAppsStore.registerIconContainer(mAH.get(AdapterHolder.MAIN).mRecyclerView);
@@ -619,11 +646,9 @@
removeCustomRules(getSearchRecyclerView());
if (!isSearchSupported()) {
layoutWithoutSearchContainer(rvContainer, showTabs);
- } else if (isSearchBarOnBottom()) {
+ } else if (isSearchBarFloating()) {
alignParentTop(rvContainer, showTabs);
alignParentTop(getSearchRecyclerView(), /* tabs= */ false);
- layoutAboveSearchContainer(rvContainer);
- layoutAboveSearchContainer(getSearchRecyclerView());
} else {
layoutBelowSearchContainer(rvContainer, showTabs);
layoutBelowSearchContainer(getSearchRecyclerView(), /* tabs= */ false);
@@ -654,7 +679,7 @@
removeCustomRules(mHeader);
if (!isSearchSupported()) {
layoutWithoutSearchContainer(mHeader, false /* includeTabsMargin */);
- } else if (isSearchBarOnBottom()) {
+ } else if (isSearchBarFloating()) {
alignParentTop(mHeader, false /* includeTabsMargin */);
} else {
layoutBelowSearchContainer(mHeader, false /* includeTabsMargin */);
@@ -694,16 +719,62 @@
}
/**
- * It is up to the search container view created by {@link #inflateSearchBox()} to use the
- * floating search bar flag to move itself to the bottom of this container. This method checks
- * if that had been done; otherwise the flag will be ignored.
- *
- * @return true if the search bar is at the bottom of the container (as opposed to the top).
- **/
- private boolean isSearchBarOnBottom() {
- return FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get()
- && ((RelativeLayout.LayoutParams) mSearchContainer.getLayoutParams()).getRule(
- ALIGN_PARENT_BOTTOM) == RelativeLayout.TRUE;
+ * @return true if the search bar is floating above this container (at the bottom of the screen)
+ */
+ protected boolean isSearchBarFloating() {
+ return mSearchUiDelegate.isSearchBarFloating();
+ }
+
+ /**
+ * Whether the <em>floating</em> search bar should appear as a small pill when not focused.
+ * <p>
+ * Note: This method mirrors one in LauncherState. For subclasses that use Launcher, it likely
+ * makes sense to use that method to derive an appropriate value for the current/target state.
+ */
+ public boolean shouldFloatingSearchBarBePillWhenUnfocused() {
+ return false;
+ }
+
+ /**
+ * How far from the bottom of the screen the <em>floating</em> search bar should rest when the
+ * IME is not present.
+ * <p>
+ * To hide offscreen, use a negative value.
+ * <p>
+ * Note: if the provided value is non-negative but less than the current bottom insets, the
+ * insets will be applied. As such, you can use 0 to default to this.
+ * <p>
+ * Note: This method mirrors one in LauncherState. For subclasses that use Launcher, it likely
+ * makes sense to use that method to derive an appropriate value for the current/target state.
+ */
+ public int getFloatingSearchBarRestingMarginBottom() {
+ return 0;
+ }
+
+ /**
+ * How far from the start of the screen the <em>floating</em> search bar should rest.
+ * <p>
+ * To use original margin, return a negative value.
+ * <p>
+ * Note: This method mirrors one in LauncherState. For subclasses that use Launcher, it likely
+ * makes sense to use that method to derive an appropriate value for the current/target state.
+ */
+ public int getFloatingSearchBarRestingMarginStart() {
+ DeviceProfile dp = mActivityContext.getDeviceProfile();
+ return dp.allAppsLeftRightMargin + dp.getAllAppsIconStartMargin();
+ }
+
+ /**
+ * How far from the end of the screen the <em>floating</em> search bar should rest.
+ * <p>
+ * To use original margin, return a negative value.
+ * <p>
+ * Note: This method mirrors one in LauncherState. For subclasses that use Launcher, it likely
+ * makes sense to use that method to derive an appropriate value for the current/target state.
+ */
+ public int getFloatingSearchBarRestingMarginEnd() {
+ DeviceProfile dp = mActivityContext.getDeviceProfile();
+ return dp.allAppsLeftRightMargin + dp.getAllAppsIconStartMargin();
}
private void layoutBelowSearchContainer(View v, boolean includeTabsMargin) {
@@ -723,15 +794,6 @@
layoutParams.topMargin = topMargin;
}
- private void layoutAboveSearchContainer(View v) {
- if (!(v.getLayoutParams() instanceof RelativeLayout.LayoutParams)) {
- return;
- }
-
- RelativeLayout.LayoutParams layoutParams = (LayoutParams) v.getLayoutParams();
- layoutParams.addRule(RelativeLayout.ABOVE, R.id.search_container_all_apps);
- }
-
private void alignParentTop(View v, boolean includeTabsMargin) {
if (!(v.getLayoutParams() instanceof RelativeLayout.LayoutParams)) {
return;
@@ -785,15 +847,10 @@
}
/**
- * Inflates the search box
+ * Inflates the search bar
*/
- protected View inflateSearchBox() {
- return getLayoutInflater().inflate(R.layout.search_container_all_apps, this, false);
- }
-
- /** Creates the adapter provider for the main section. */
- protected SearchAdapterProvider<?> createMainAdapterProvider() {
- return new DefaultSearchAdapterProvider(mActivityContext);
+ protected View inflateSearchBar() {
+ return mSearchUiDelegate.inflateSearchBar();
}
/** The adapter provider for the main section. */
@@ -973,7 +1030,7 @@
/**
* The container for A-Z apps (the ViewPager for main+work tabs, or main RV). This is currently
* hidden while searching.
- **/
+ */
public ViewGroup getAppsRecyclerViewContainer() {
return mViewPager != null ? mViewPager : findViewById(R.id.apps_list_view);
}
@@ -998,7 +1055,7 @@
}
public LayoutInflater getLayoutInflater() {
- return LayoutInflater.from(getContext());
+ return mSearchUiDelegate.getLayoutInflater();
}
@Override
@@ -1020,7 +1077,7 @@
setPadding(grid.workspacePadding.left, 0, grid.workspacePadding.right, 0);
} else {
int topPadding = grid.allAppsTopPadding;
- if (isSearchBarOnBottom() && !grid.isTablet) {
+ if (isSearchBarFloating() && !grid.isTablet) {
topPadding += getResources().getDimensionPixelSize(
R.dimen.all_apps_additional_top_padding_floating_search);
}
@@ -1096,7 +1153,10 @@
}
}
- protected boolean shouldShowTabs() {
+ /**
+ * Returns true if the container has work apps.
+ */
+ public boolean shouldShowTabs() {
return mHasWorkApps;
}
@@ -1110,6 +1170,30 @@
return view.getGlobalVisibleRect(new Rect());
}
+ /** Called in Launcher#bindStringCache() to update the UI when cache is updated. */
+ public void updateWorkUI() {
+ setDeviceManagementResources();
+ if (mWorkManager.getWorkModeSwitch() != null) {
+ mWorkManager.getWorkModeSwitch().updateStringFromCache();
+ }
+ inflateWorkCardsIfNeeded();
+ }
+
+ private void inflateWorkCardsIfNeeded() {
+ AllAppsRecyclerView workRV = mAH.get(AdapterHolder.WORK).mRecyclerView;
+ if (workRV != null) {
+ for (int i = 0; i < workRV.getChildCount(); i++) {
+ View currentView = workRV.getChildAt(i);
+ int currentItemViewType = workRV.getChildViewHolder(currentView).getItemViewType();
+ if (currentItemViewType == VIEW_TYPE_WORK_EDU_CARD) {
+ ((WorkEduCard) currentView).updateStringFromCache();
+ } else if (currentItemViewType == VIEW_TYPE_WORK_DISABLED_CARD) {
+ ((WorkPausedCard) currentView).updateStringFromCache();
+ }
+ }
+ }
+ }
+
@VisibleForTesting
public boolean isPersonalTabVisible() {
return isDescendantViewVisible(R.id.tab_personal);
@@ -1232,7 +1316,7 @@
final FloatingHeaderView headerView = getFloatingHeaderView();
if (hasBottomSheet) {
// Start adding header protection if search bar or tabs will attach to the top.
- if (!FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get() || mUsingTabs) {
+ if (!isSearchBarFloating() || mUsingTabs) {
mTmpRectF.set(
leftWithScale,
topWithScale,
@@ -1288,7 +1372,7 @@
/** Returns the position of the bottom edge of the header */
public int getHeaderBottom() {
int bottom = (int) getTranslationY() + mHeader.getClipTop();
- if (isSearchBarOnBottom()) {
+ if (isSearchBarFloating()) {
if (mActivityContext.getDeviceProfile().isTablet) {
return bottom + mBottomSheetBackground.getTop();
}
@@ -1306,6 +1390,7 @@
protected void onInitializeRecyclerView(RecyclerView rv) {
rv.addOnScrollListener(mScrollListener);
+ mSearchUiDelegate.onInitializeRecyclerView(rv);
}
/** Returns the instance of @{code SearchTransitionController}. */
@@ -1358,6 +1443,9 @@
if (isWork() && mWorkManager.getWorkModeSwitch() != null) {
bottomOffset = mInsets.bottom + mWorkManager.getWorkModeSwitch().getHeight();
}
+ if (isSearchBarFloating()) {
+ bottomOffset += mSearchContainer.getHeight();
+ }
mRecyclerView.setPadding(mPadding.left, mPadding.top, mPadding.right,
mPadding.bottom + bottomOffset);
}
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index d4f152a..0d7b736 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -15,14 +15,15 @@
*/
package com.android.launcher3.allapps;
+import static com.android.app.animation.Interpolators.DECELERATE_1_7;
+import static com.android.app.animation.Interpolators.INSTANT;
+import static com.android.app.animation.Interpolators.LINEAR;
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.ALL_APPS_CONTENT;
+import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7;
-import static com.android.launcher3.anim.Interpolators.INSTANT;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_BOTTOM_SHEET_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
@@ -45,6 +46,7 @@
import androidx.annotation.FloatRange;
import androidx.annotation.Nullable;
+import com.android.app.animation.Interpolators;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.Launcher;
@@ -53,7 +55,6 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.AnimatorListeners;
-import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.PropertySetter;
import com.android.launcher3.config.FeatureFlags;
@@ -234,7 +235,11 @@
*/
public void setProgress(float progress) {
mProgress = progress;
- getAppsViewProgressTranslationY().setValue(mProgress * mShiftRange);
+ boolean fromBackground =
+ mLauncher.getStateManager().getCurrentStableState() == BACKGROUND_APP;
+ // Allow apps panel to shift the full screen if coming from another app.
+ float shiftRange = fromBackground ? mLauncher.getDeviceProfile().heightPx : mShiftRange;
+ getAppsViewProgressTranslationY().setValue(mProgress * shiftRange);
mLauncher.onAllAppsTransition(1 - progress);
boolean hasScrim = progress < NAV_BAR_COLOR_FORCE_UPDATE_THRESHOLD
@@ -380,7 +385,7 @@
// need to decide depending on the release velocity
Interpolator verticalProgressInterpolator = config.getInterpolator(ANIM_VERTICAL_PROGRESS,
- config.userControlled ? LINEAR : DEACCEL_1_7);
+ config.userControlled ? LINEAR : DECELERATE_1_7);
Animator anim = createSpringAnimation(mProgress, targetProgress);
anim.setInterpolator(verticalProgressInterpolator);
anim.addListener(getProgressAnimatorListener());
diff --git a/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java b/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java
index aefedae..d78e453 100644
--- a/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java
@@ -22,6 +22,7 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.Utilities;
+import com.android.launcher3.statemanager.StateManager;
/**
* AllAppsContainerView with launcher specific callbacks
@@ -53,4 +54,75 @@
public boolean isInAllApps() {
return mActivityContext.getStateManager().isInStableState(LauncherState.ALL_APPS);
}
+
+ @Override
+ public boolean shouldFloatingSearchBarBePillWhenUnfocused() {
+ if (!isSearchBarFloating()) {
+ return false;
+ }
+ Launcher launcher = mActivityContext;
+ StateManager<LauncherState> manager = launcher.getStateManager();
+ if (manager.isInTransition() && manager.getTargetState() != null) {
+ return manager.getTargetState().shouldFloatingSearchBarUsePillWhenUnfocused(launcher);
+ }
+ return manager.getCurrentStableState()
+ .shouldFloatingSearchBarUsePillWhenUnfocused(launcher);
+ }
+
+ @Override
+ public int getFloatingSearchBarRestingMarginBottom() {
+ if (!isSearchBarFloating()) {
+ return super.getFloatingSearchBarRestingMarginBottom();
+ }
+ Launcher launcher = mActivityContext;
+ StateManager<LauncherState> stateManager = launcher.getStateManager();
+
+ // We want to rest at the current state's resting position, unless we are in transition and
+ // the target state's resting position is higher (that way if we are closing the keyboard,
+ // we can stop translating at that point).
+ int currentStateMarginBottom = stateManager.getCurrentStableState()
+ .getFloatingSearchBarRestingMarginBottom(launcher);
+ int targetStateMarginBottom = -1;
+ if (stateManager.isInTransition() && stateManager.getTargetState() != null) {
+ targetStateMarginBottom = stateManager.getTargetState()
+ .getFloatingSearchBarRestingMarginBottom(launcher);
+ if (targetStateMarginBottom < 0) {
+ // Go ahead and move offscreen.
+ return targetStateMarginBottom;
+ }
+ }
+ return Math.max(targetStateMarginBottom, currentStateMarginBottom);
+ }
+
+ @Override
+ public int getFloatingSearchBarRestingMarginStart() {
+ if (!isSearchBarFloating()) {
+ return super.getFloatingSearchBarRestingMarginStart();
+ }
+
+ StateManager<LauncherState> stateManager = mActivityContext.getStateManager();
+
+ if (stateManager.isInTransition() && stateManager.getTargetState() != null) {
+ return stateManager.getTargetState()
+ .getFloatingSearchBarRestingMarginStart(mActivityContext);
+ }
+ return stateManager.getCurrentStableState()
+ .getFloatingSearchBarRestingMarginStart(mActivityContext);
+ }
+
+ @Override
+ public int getFloatingSearchBarRestingMarginEnd() {
+ if (!isSearchBarFloating()) {
+ return super.getFloatingSearchBarRestingMarginEnd();
+ }
+
+ StateManager<LauncherState> stateManager = mActivityContext.getStateManager();
+
+ if (stateManager.isInTransition() && stateManager.getTargetState() != null) {
+ return stateManager.getTargetState()
+ .getFloatingSearchBarRestingMarginEnd(mActivityContext);
+ }
+ return stateManager.getCurrentStableState()
+ .getFloatingSearchBarRestingMarginEnd(mActivityContext);
+ }
}
diff --git a/src/com/android/launcher3/allapps/SearchTransitionController.java b/src/com/android/launcher3/allapps/SearchTransitionController.java
index b01ea53..eb1bc0a 100644
--- a/src/com/android/launcher3/allapps/SearchTransitionController.java
+++ b/src/com/android/launcher3/allapps/SearchTransitionController.java
@@ -20,12 +20,12 @@
import static androidx.recyclerview.widget.RecyclerView.NO_POSITION;
+import static com.android.app.animation.Interpolators.DECELERATE_1_7;
+import static com.android.app.animation.Interpolators.INSTANT;
+import static com.android.app.animation.Interpolators.clampToProgress;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
import static com.android.launcher3.anim.AnimatorListeners.forSuccessCallback;
-import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7;
-import static com.android.launcher3.anim.Interpolators.INSTANT;
-import static com.android.launcher3.anim.Interpolators.clampToProgress;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
@@ -48,7 +48,7 @@
private static final String LOG_TAG = "SearchTransitionCtrl";
// Interpolator when the user taps the QSB while already in All Apps.
- private static final Interpolator INTERPOLATOR_WITHIN_ALL_APPS = DEACCEL_1_7;
+ private static final Interpolator INTERPOLATOR_WITHIN_ALL_APPS = DECELERATE_1_7;
// Interpolator when the user taps the QSB from home screen, so transition to all apps is
// happening simultaneously.
private static final Interpolator INTERPOLATOR_TRANSITIONING_TO_ALL_APPS = INSTANT;
diff --git a/src/com/android/launcher3/allapps/SearchUiManager.java b/src/com/android/launcher3/allapps/SearchUiManager.java
index 2174936..bfd8967 100644
--- a/src/com/android/launcher3/allapps/SearchUiManager.java
+++ b/src/com/android/launcher3/allapps/SearchUiManager.java
@@ -49,6 +49,14 @@
ExtendedEditText getEditText();
/**
+ * Hint to the edit text that it is about to be focused or unfocused. This can be used to start
+ * animating the edit box accordingly, e.g. after a gesture completes.
+ *
+ * @param focused true if the edit text is about to be focused, false if it will be unfocused
+ */
+ default void prepareToFocusEditText(boolean focused) {}
+
+ /**
* Sets whether EditText background should be visible
* @param maxAlpha defines the maximum alpha the background should animates to
*/
diff --git a/src/com/android/launcher3/allapps/WorkEduCard.java b/src/com/android/launcher3/allapps/WorkEduCard.java
index b4cdc96..1059097 100644
--- a/src/com/android/launcher3/allapps/WorkEduCard.java
+++ b/src/com/android/launcher3/allapps/WorkEduCard.java
@@ -76,11 +76,7 @@
super.onFinishInflate();
findViewById(R.id.action_btn).setOnClickListener(this);
- StringCache cache = mActivityContext.getStringCache();
- if (cache != null) {
- TextView title = findViewById(R.id.work_apps_paused_title);
- title.setText(cache.workProfileEdu);
- }
+ updateStringFromCache();
}
@Override
@@ -121,4 +117,12 @@
public void setPosition(int position) {
mPosition = position;
}
+
+ public void updateStringFromCache() {
+ StringCache cache = mActivityContext.getStringCache();
+ if (cache != null) {
+ TextView title = findViewById(R.id.work_apps_paused_title);
+ title.setText(cache.workProfileEdu);
+ }
+ }
}
diff --git a/src/com/android/launcher3/allapps/WorkModeSwitch.java b/src/com/android/launcher3/allapps/WorkModeSwitch.java
index 8c2fb19..28a3312 100644
--- a/src/com/android/launcher3/allapps/WorkModeSwitch.java
+++ b/src/com/android/launcher3/allapps/WorkModeSwitch.java
@@ -36,7 +36,6 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.KeyboardInsetAnimationCallback;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.model.StringCache;
import com.android.launcher3.views.ActivityContext;
@@ -92,10 +91,7 @@
}
setInsets(mActivityContext.getDeviceProfile().getInsets());
- StringCache cache = mActivityContext.getStringCache();
- if (cache != null) {
- mTextView.setText(cache.workProfilePauseButton);
- }
+ updateStringFromCache();
getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
}
@@ -108,7 +104,7 @@
if (lp != null) {
int bottomMargin = getResources().getDimensionPixelSize(R.dimen.work_fab_margin_bottom);
DeviceProfile dp = ActivityContext.lookupContext(getContext()).getDeviceProfile();
- if (FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get()) {
+ if (mActivityContext.getAppsView().isSearchBarFloating()) {
bottomMargin += dp.hotseatQsbHeight;
}
@@ -213,4 +209,11 @@
public int getScrollThreshold() {
return mScrollThreshold;
}
+
+ public void updateStringFromCache(){
+ StringCache cache = mActivityContext.getStringCache();
+ if (cache != null) {
+ mTextView.setText(cache.workProfilePauseButton);
+ }
+ }
}
diff --git a/src/com/android/launcher3/allapps/WorkPausedCard.java b/src/com/android/launcher3/allapps/WorkPausedCard.java
index 26a7803..1882667 100644
--- a/src/com/android/launcher3/allapps/WorkPausedCard.java
+++ b/src/com/android/launcher3/allapps/WorkPausedCard.java
@@ -57,6 +57,10 @@
mBtn = findViewById(R.id.enable_work_apps);
mBtn.setOnClickListener(this);
+ updateStringFromCache();
+ }
+
+ public void updateStringFromCache() {
StringCache cache = mActivityContext.getStringCache();
if (cache != null) {
setWorkProfilePausedResources(cache);
diff --git a/src/com/android/launcher3/allapps/search/AllAppsSearchUiDelegate.java b/src/com/android/launcher3/allapps/search/AllAppsSearchUiDelegate.java
new file mode 100644
index 0000000..49cecca
--- /dev/null
+++ b/src/com/android/launcher3/allapps/search/AllAppsSearchUiDelegate.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.allapps.search;
+
+import android.view.LayoutInflater;
+import android.view.View;
+
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.launcher3.R;
+import com.android.launcher3.allapps.ActivityAllAppsContainerView;
+import com.android.launcher3.allapps.BaseAllAppsAdapter.AdapterItem;
+import com.android.launcher3.views.ActivityContext;
+
+import java.util.List;
+
+/** Initializes the search box and its interactions with All Apps. */
+public class AllAppsSearchUiDelegate {
+
+ protected final ActivityAllAppsContainerView<?> mAppsView;
+ protected final ActivityContext mActivityContext;
+
+ public AllAppsSearchUiDelegate(ActivityAllAppsContainerView<?> appsView) {
+ mAppsView = appsView;
+ mActivityContext = ActivityContext.lookupContext(mAppsView.getContext());
+ }
+
+ /** Invoked when an All Apps {@link RecyclerView} is initialized. */
+ public void onInitializeRecyclerView(RecyclerView rv) {
+ // Do nothing.
+ }
+
+ /** Invoked when search results are updated in All Apps. */
+ public void onSearchResultsChanged(List<AdapterItem> results, int searchResultCode) {
+ // Do nothing.
+ }
+
+ /** Invoked when the search bar has been added to All Apps. */
+ public void onInitializeSearchBar() {
+ // Do nothing.
+ }
+
+ /** Invoked when the search bar has been removed from All Apps. */
+ public void onDestroySearchBar() {
+ // Do nothing.
+ }
+
+ /** The layout inflater for All Apps and search UI. */
+ public LayoutInflater getLayoutInflater() {
+ return LayoutInflater.from(mAppsView.getContext());
+ }
+
+ /** Inflate the search bar for All Apps. */
+ public View inflateSearchBar() {
+ return getLayoutInflater().inflate(R.layout.search_container_all_apps, mAppsView, false);
+ }
+
+ /** Whether the search box is floating above the apps surface (inset by the IME). */
+ public boolean isSearchBarFloating() {
+ return false;
+ }
+
+ /** Creates the adapter provider for the main section. */
+ public SearchAdapterProvider<?> createMainAdapterProvider() {
+ return new DefaultSearchAdapterProvider(mActivityContext);
+ }
+}
diff --git a/src/com/android/launcher3/anim/AnimatorPlaybackController.java b/src/com/android/launcher3/anim/AnimatorPlaybackController.java
index 1cc0c21..d11a51f 100644
--- a/src/com/android/launcher3/anim/AnimatorPlaybackController.java
+++ b/src/com/android/launcher3/anim/AnimatorPlaybackController.java
@@ -15,10 +15,10 @@
*/
package com.android.launcher3.anim;
+import static com.android.app.animation.Interpolators.LINEAR;
+import static com.android.app.animation.Interpolators.clampToProgress;
+import static com.android.app.animation.Interpolators.scrollInterpolatorForVelocity;
import static com.android.launcher3.Utilities.boundToRange;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.anim.Interpolators.clampToProgress;
-import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
import android.animation.Animator;
diff --git a/src/com/android/launcher3/anim/Interpolators.java b/src/com/android/launcher3/anim/Interpolators.java
deleted file mode 100644
index e886543..0000000
--- a/src/com/android/launcher3/anim/Interpolators.java
+++ /dev/null
@@ -1,237 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.anim;
-
-import android.graphics.Path;
-import android.view.animation.AccelerateDecelerateInterpolator;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.DecelerateInterpolator;
-import android.view.animation.Interpolator;
-import android.view.animation.LinearInterpolator;
-import android.view.animation.OvershootInterpolator;
-import android.view.animation.PathInterpolator;
-
-import com.android.launcher3.Utilities;
-
-/**
- * Common interpolators used in Launcher
- */
-public class Interpolators {
-
- public static final Interpolator LINEAR = new LinearInterpolator();
-
- public static final Interpolator ACCEL = new AccelerateInterpolator();
- public static final Interpolator ACCEL_0_5 = new AccelerateInterpolator(0.5f);
- public static final Interpolator ACCEL_0_75 = new AccelerateInterpolator(0.75f);
- public static final Interpolator ACCEL_1_5 = new AccelerateInterpolator(1.5f);
- public static final Interpolator ACCEL_2 = new AccelerateInterpolator(2);
-
- public static final Interpolator DEACCEL = new DecelerateInterpolator();
- public static final Interpolator DEACCEL_1_5 = new DecelerateInterpolator(1.5f);
- public static final Interpolator DEACCEL_1_7 = new DecelerateInterpolator(1.7f);
- public static final Interpolator DEACCEL_2 = new DecelerateInterpolator(2);
- public static final Interpolator DEACCEL_2_5 = new DecelerateInterpolator(2.5f);
- public static final Interpolator DEACCEL_3 = new DecelerateInterpolator(3f);
-
- public static final Interpolator ACCEL_DEACCEL = new AccelerateDecelerateInterpolator();
-
- public static final Interpolator FAST_OUT_SLOW_IN = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
-
- public static final Interpolator AGGRESSIVE_EASE = new PathInterpolator(0.2f, 0f, 0f, 1f);
- public static final Interpolator AGGRESSIVE_EASE_IN_OUT = new PathInterpolator(0.6f,0, 0.4f, 1);
-
- public static final Interpolator DECELERATED_EASE = new PathInterpolator(0, 0, .2f, 1f);
- public static final Interpolator ACCELERATED_EASE = new PathInterpolator(0.4f, 0, 1f, 1f);
- public static final Interpolator PREDICTIVE_BACK_DECELERATED_EASE =
- new PathInterpolator(0, 0, 0, 1f);
-
- /**
- * The default emphasized interpolator. Used for hero / emphasized movement of content.
- */
- public static final Interpolator EMPHASIZED = createEmphasizedInterpolator();
- public static final Interpolator EMPHASIZED_ACCELERATE = new PathInterpolator(
- 0.3f, 0f, 0.8f, 0.15f);
- public static final Interpolator EMPHASIZED_DECELERATE = new PathInterpolator(
- 0.05f, 0.7f, 0.1f, 1f);
-
- public static final Interpolator EXAGGERATED_EASE;
-
- public static final Interpolator INSTANT = t -> 1;
- /**
- * All values of t map to 0 until t == 1. This is primarily useful for setting view visibility,
- * which should only happen at the very end of the animation (when it's already hidden).
- */
- public static final Interpolator FINAL_FRAME = t -> t < 1 ? 0 : 1;
-
- static {
- Path exaggeratedEase = new Path();
- exaggeratedEase.moveTo(0, 0);
- exaggeratedEase.cubicTo(0.05f, 0f, 0.133333f, 0.08f, 0.166666f, 0.4f);
- exaggeratedEase.cubicTo(0.225f, 0.94f, 0.5f, 1f, 1f, 1f);
- EXAGGERATED_EASE = new PathInterpolator(exaggeratedEase);
- }
-
- public static final Interpolator OVERSHOOT_0_75 = new OvershootInterpolator(0.75f);
- public static final Interpolator OVERSHOOT_1_2 = new OvershootInterpolator(1.2f);
- public static final Interpolator OVERSHOOT_1_7 = new OvershootInterpolator(1.7f);
-
- public static final Interpolator TOUCH_RESPONSE_INTERPOLATOR =
- new PathInterpolator(0.3f, 0f, 0.1f, 1f);
- public static final Interpolator TOUCH_RESPONSE_INTERPOLATOR_ACCEL_DEACCEL =
- v -> ACCEL_DEACCEL.getInterpolation(TOUCH_RESPONSE_INTERPOLATOR.getInterpolation(v));
-
- /**
- * Inversion of ZOOM_OUT, compounded with an ease-out.
- */
- public static final Interpolator ZOOM_IN = new Interpolator() {
- @Override
- public float getInterpolation(float v) {
- return DEACCEL_3.getInterpolation(1 - ZOOM_OUT.getInterpolation(1 - v));
- }
- };
-
- public static final Interpolator ZOOM_OUT = new Interpolator() {
-
- private static final float FOCAL_LENGTH = 0.35f;
-
- @Override
- public float getInterpolation(float v) {
- return zInterpolate(v);
- }
-
- /**
- * This interpolator emulates the rate at which the perceived scale of an object changes
- * as its distance from a camera increases. When this interpolator is applied to a scale
- * animation on a view, it evokes the sense that the object is shrinking due to moving away
- * from the camera.
- */
- private float zInterpolate(float input) {
- return (1.0f - FOCAL_LENGTH / (FOCAL_LENGTH + input)) /
- (1.0f - FOCAL_LENGTH / (FOCAL_LENGTH + 1.0f));
- }
- };
-
- public static final Interpolator SCROLL = new Interpolator() {
- @Override
- public float getInterpolation(float t) {
- t -= 1.0f;
- return t*t*t*t*t + 1;
- }
- };
-
- public static final Interpolator SCROLL_CUBIC = new Interpolator() {
- @Override
- public float getInterpolation(float t) {
- t -= 1.0f;
- return t*t*t + 1;
- }
- };
-
- private static final float FAST_FLING_PX_MS = 10;
-
- public static Interpolator scrollInterpolatorForVelocity(float velocity) {
- return Math.abs(velocity) > FAST_FLING_PX_MS ? SCROLL : SCROLL_CUBIC;
- }
-
- /**
- * Create an OvershootInterpolator with tension directly related to the velocity (in px/ms).
- * @param velocity The start velocity of the animation we want to overshoot.
- */
- public static Interpolator overshootInterpolatorForVelocity(float velocity) {
- return new OvershootInterpolator(Math.min(Math.abs(velocity), 3f));
- }
-
- /**
- * Returns a function that runs the given interpolator such that the entire progress is set
- * between the given bounds. That is, we set the interpolation to 0 until lowerBound and reach
- * 1 by upperBound.
- */
- public static Interpolator clampToProgress(Interpolator interpolator, float lowerBound,
- float upperBound) {
- if (upperBound < lowerBound) {
- throw new IllegalArgumentException(
- String.format("upperBound (%f) must be greater than lowerBound (%f)",
- upperBound, lowerBound));
- }
- return t -> clampToProgress(interpolator, t, lowerBound, upperBound);
- }
-
- /**
- * Returns the progress value's progress between the lower and upper bounds. That is, the
- * progress will be 0f from 0f to lowerBound, and reach 1f by upperBound.
- *
- * Between lowerBound and upperBound, the progress value will be interpolated using the provided
- * interpolator.
- */
- public static float clampToProgress(
- Interpolator interpolator, float progress, float lowerBound, float upperBound) {
- if (upperBound < lowerBound) {
- throw new IllegalArgumentException(
- String.format("upperBound (%f) must be greater than lowerBound (%f)",
- upperBound, lowerBound));
- }
-
- if (progress == lowerBound && progress == upperBound) {
- return progress == 0f ? 0 : 1;
- }
- if (progress < lowerBound) {
- return 0;
- }
- if (progress > upperBound) {
- return 1;
- }
- return interpolator.getInterpolation((progress - lowerBound) / (upperBound - lowerBound));
- }
-
- /**
- * Returns the progress value's progress between the lower and upper bounds. That is, the
- * progress will be 0f from 0f to lowerBound, and reach 1f by upperBound.
- */
- public static float clampToProgress(float progress, float lowerBound, float upperBound) {
- return clampToProgress(Interpolators.LINEAR, progress, lowerBound, upperBound);
- }
-
- /**
- * Runs the given interpolator such that the interpolated value is mapped to the given range.
- * This is useful, for example, if we only use this interpolator for part of the animation,
- * such as to take over a user-controlled animation when they let go.
- */
- public static Interpolator mapToProgress(Interpolator interpolator, float lowerBound,
- float upperBound) {
- return t -> Utilities.mapRange(interpolator.getInterpolation(t), lowerBound, upperBound);
- }
-
- /**
- * Returns the reverse of the provided interpolator, following the formula: g(x) = 1 - f(1 - x).
- * In practice, this means that if f is an interpolator used to model a value animating between
- * m and n, g is the interpolator to use to obtain the specular behavior when animating from n
- * to m.
- */
- public static Interpolator reverse(Interpolator interpolator) {
- return t -> 1 - interpolator.getInterpolation(1 - t);
- }
-
- // Create the default emphasized interpolator
- private static PathInterpolator createEmphasizedInterpolator() {
- Path path = new Path();
- // Doing the same as fast_out_extra_slow_in
- path.moveTo(0f, 0f);
- path.cubicTo(0.05f, 0f, 0.133333f, 0.06f, 0.166666f, 0.4f);
- path.cubicTo(0.208333f, 0.82f, 0.25f, 1f, 1f, 1f);
- return new PathInterpolator(path);
- }
-}
diff --git a/src/com/android/launcher3/anim/KeyboardInsetAnimationCallback.java b/src/com/android/launcher3/anim/KeyboardInsetAnimationCallback.java
index b911928..39386fa 100644
--- a/src/com/android/launcher3/anim/KeyboardInsetAnimationCallback.java
+++ b/src/com/android/launcher3/anim/KeyboardInsetAnimationCallback.java
@@ -44,14 +44,30 @@
private float mInitialTranslation;
private float mTerminalTranslation;
+ private KeyboardTranslationState mKeyboardTranslationState = KeyboardTranslationState.SYSTEM;
+
+ /** Current state of the keyboard. */
+ public enum KeyboardTranslationState {
+ // We are not controlling the keyboard, and it may or may not be translating.
+ SYSTEM,
+ // We are about to gain control of the keyboard, but the current state may be transient.
+ MANUAL_PREPARED,
+ // We are manually translating the keyboard.
+ MANUAL_ONGOING
+ }
public KeyboardInsetAnimationCallback(View view) {
super(DISPATCH_MODE_STOP);
mView = view;
}
+ public KeyboardTranslationState getKeyboardTranslationState() {
+ return mKeyboardTranslationState;
+ }
+
@Override
public void onPrepare(WindowInsetsAnimation animation) {
+ mKeyboardTranslationState = KeyboardTranslationState.MANUAL_PREPARED;
mInitialTranslation = mView.getTranslationY();
}
@@ -62,6 +78,7 @@
mTerminalTranslation = mView.getTranslationY();
// Reset the translation in case the view is drawn before onProgress gets called.
mView.setTranslationY(mInitialTranslation);
+ mKeyboardTranslationState = KeyboardTranslationState.MANUAL_ONGOING;
if (mView instanceof KeyboardInsetListener) {
((KeyboardInsetListener) mView).onTranslationStart();
}
@@ -90,6 +107,10 @@
mView.setTranslationY(translationY);
}
+ if (mView instanceof KeyboardInsetListener) {
+ ((KeyboardInsetListener) mView).onKeyboardAlphaChanged(animation.getAlpha());
+ }
+
return windowInsets;
}
@@ -98,7 +119,7 @@
if (mView instanceof KeyboardInsetListener) {
((KeyboardInsetListener) mView).onTranslationEnd();
}
- super.onEnd(animation);
+ mKeyboardTranslationState = KeyboardTranslationState.SYSTEM;
}
/**
@@ -111,6 +132,13 @@
void onTranslationStart();
/**
+ * Called from {@link KeyboardInsetAnimationCallback#onProgress}
+ *
+ * @param alpha the current IME alpha
+ */
+ default void onKeyboardAlphaChanged(float alpha) {}
+
+ /**
* Called from {@link KeyboardInsetAnimationCallback#onEnd}
*/
void onTranslationEnd();
diff --git a/src/com/android/launcher3/anim/SpringAnimationBuilder.java b/src/com/android/launcher3/anim/SpringAnimationBuilder.java
index 40fa0cf..bc7b7f0 100644
--- a/src/com/android/launcher3/anim/SpringAnimationBuilder.java
+++ b/src/com/android/launcher3/anim/SpringAnimationBuilder.java
@@ -15,7 +15,7 @@
*/
package com.android.launcher3.anim;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.app.animation.Interpolators.LINEAR;
import android.animation.Animator;
import android.animation.ValueAnimator;
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 0f60f7f..df24620 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -82,9 +82,6 @@
* <p>
*/
// TODO(Block 1): Clean up flags
- public static final BooleanFlag ENABLE_ONE_SEARCH_MOTION = getReleaseFlag(270394223,
- "ENABLE_ONE_SEARCH_MOTION", ENABLED, "Enables animations in OneSearch.");
-
public static final BooleanFlag ENABLE_SEARCH_RESULT_BACKGROUND_DRAWABLES = getReleaseFlag(
270394041, "ENABLE_SEARCH_RESULT_BACKGROUND_DRAWABLES", DISABLED,
"Enable option to replace decorator-based search result backgrounds with drawables");
@@ -119,7 +116,8 @@
// TODO(Block 4): Cleanup flags
public static final BooleanFlag ENABLE_FLOATING_SEARCH_BAR =
getReleaseFlag(268388460, "ENABLE_FLOATING_SEARCH_BAR", DISABLED,
- "Keep All Apps search bar at the bottom (but above keyboard if open)");
+ "Allow search bar to persist and animate across states, and attach to"
+ + " the keyboard from the bottom of the screen");
public static final BooleanFlag ENABLE_ALL_APPS_FROM_OVERVIEW =
getDebugFlag(275132633, "ENABLE_ALL_APPS_FROM_OVERVIEW", DISABLED,
@@ -184,18 +182,6 @@
"ENABLE_BACK_SWIPE_LAUNCHER_ANIMATION", DISABLED,
"Enables predictive back animation from all apps and widgets to home");
- // TODO(Block 11): Clean up flags
- public static final BooleanFlag ENABLE_TWO_PANEL_HOME = getDebugFlag(270392643,
- "ENABLE_TWO_PANEL_HOME", ENABLED,
- "Uses two panel on home screen. Only applicable on large screen devices.");
-
- public static final BooleanFlag FOLDABLE_WORKSPACE_REORDER = getDebugFlag(270395070,
- "FOLDABLE_WORKSPACE_REORDER", DISABLED,
- "In foldables, when reordering the icons and widgets, is now going to use both sides");
-
- public static final BooleanFlag FOLDABLE_SINGLE_PAGE = getDebugFlag(270395274,
- "FOLDABLE_SINGLE_PAGE", ENABLED, "Use a single page for the workspace");
-
// TODO(Block 12): Clean up flags
public static final BooleanFlag ENABLE_MULTI_INSTANCE = getDebugFlag(270396680,
"ENABLE_MULTI_INSTANCE", DISABLED,
@@ -253,6 +239,10 @@
"INJECT_FALLBACK_APP_CORPUS_RESULTS", DISABLED,
"Inject fallback app corpus result when AiAi fails to return it.");
+ public static final BooleanFlag ENABLE_LONG_PRESS_NAV_HANDLE =
+ getDebugFlag(282993230, "ENABLE_LONG_PRESS_NAV_HANDLE", DISABLED,
+ "Enables long pressing on the bottom bar nav handle to trigger events.");
+
// TODO(Block 17): Clean up flags
public static final BooleanFlag ENABLE_TASKBAR_PINNING = getDebugFlag(270396583,
"ENABLE_TASKBAR_PINNING", DISABLED,
@@ -302,18 +292,18 @@
"Enable widget transition animation when resizing the widgets");
public static final BooleanFlag PREEMPTIVE_UNFOLD_ANIMATION_START = getDebugFlag(270397209,
- "PREEMPTIVE_UNFOLD_ANIMATION_START", DISABLED,
+ "PREEMPTIVE_UNFOLD_ANIMATION_START", ENABLED,
"Enables starting the unfold animation preemptively when unfolding, without"
+ "waiting for SystemUI and then merging the SystemUI progress whenever we "
+ "start receiving the events");
// TODO(Block 23): Clean up flags
public static final BooleanFlag ENABLE_GRID_ONLY_OVERVIEW = getDebugFlag(270397206,
- "ENABLE_GRID_ONLY_OVERVIEW", DISABLED,
+ "ENABLE_GRID_ONLY_OVERVIEW", TEAMFOOD,
"Enable a grid-only overview without a focused task.");
public static final BooleanFlag ENABLE_CURSOR_HOVER_STATES = getDebugFlag(243191650,
- "ENABLE_CURSOR_HOVER_STATES", DISABLED,
+ "ENABLE_CURSOR_HOVER_STATES", TEAMFOOD,
"Enables cursor hover states for certain elements.");
// TODO(Block 24): Clean up flags
@@ -381,7 +371,7 @@
"Enable initiating split screen from workspace to workspace.");
public static final BooleanFlag ENABLE_TRACKPAD_GESTURE = getDebugFlag(271010401,
- "ENABLE_TRACKPAD_GESTURE", DISABLED, "Enables trackpad gesture.");
+ "ENABLE_TRACKPAD_GESTURE", ENABLED, "Enables trackpad gesture.");
// TODO(Block 29): Clean up flags
public static final BooleanFlag ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT = getDebugFlag(270393897,
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index 9867268..0d51d48 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -28,9 +28,9 @@
import androidx.annotation.Nullable;
+import com.android.app.animation.Interpolators;
import com.android.launcher3.DragSource;
import com.android.launcher3.DropTarget;
-import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.logging.InstanceId;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index 366870b..f18f900 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -19,11 +19,11 @@
import static android.animation.ObjectAnimator.ofFloat;
+import static com.android.app.animation.Interpolators.DECELERATE_1_5;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
import static com.android.launcher3.Utilities.mapRange;
import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
-import static com.android.launcher3.anim.Interpolators.DEACCEL_1_5;
import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
import android.animation.Animator;
@@ -42,6 +42,7 @@
import android.view.accessibility.AccessibilityManager;
import android.view.animation.Interpolator;
+import com.android.app.animation.Interpolators;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DropTargetBar;
import com.android.launcher3.Launcher;
@@ -49,7 +50,6 @@
import com.android.launcher3.ShortcutAndWidgetContainer;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
-import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.SpringProperty;
import com.android.launcher3.celllayout.CellLayoutLayoutParams;
@@ -340,14 +340,14 @@
if (duration < 0) {
duration = res.getInteger(R.integer.config_dropAnimMaxDuration);
if (dist < maxDist) {
- duration *= DEACCEL_1_5.getInterpolation(dist / maxDist);
+ duration *= DECELERATE_1_5.getInterpolation(dist / maxDist);
}
duration = Math.max(duration, res.getInteger(R.integer.config_dropAnimMinDuration));
}
// Fall back to cubic ease out interpolator for the animation if none is specified
TimeInterpolator interpolator =
- motionInterpolator == null ? DEACCEL_1_5 : motionInterpolator;
+ motionInterpolator == null ? DECELERATE_1_5 : motionInterpolator;
// Animate the view
PendingAnimation anim = new PendingAnimation(duration);
@@ -475,7 +475,7 @@
@Override
public void onOverlayScrollChanged(float progress) {
- float alpha = 1 - Interpolators.DEACCEL_3.getInterpolation(progress);
+ float alpha = 1 - Interpolators.DECELERATE_3.getInterpolation(progress);
float transX = getMeasuredWidth() * progress;
if (mIsRtl) {
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index 0d0717e..c26d673 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -55,9 +55,9 @@
import androidx.dynamicanimation.animation.SpringAnimation;
import androidx.dynamicanimation.animation.SpringForce;
+import com.android.app.animation.Interpolators;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
-import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.icons.FastBitmapDrawable;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.model.data.ItemInfo;
@@ -371,7 +371,7 @@
AnimatorSet anim = new AnimatorSet();
anim.play(ObjectAnimator.ofFloat(newContent, VIEW_ALPHA, 0, 1));
anim.play(ObjectAnimator.ofFloat(mContent, VIEW_ALPHA, 0));
- anim.setDuration(duration).setInterpolator(Interpolators.DEACCEL_1_5);
+ anim.setDuration(duration).setInterpolator(Interpolators.DECELERATE_1_5);
anim.start();
}
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 4ae54e6..f38cce1 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -881,7 +881,6 @@
final ItemInfo item = d.dragInfo;
final int itemType = item.itemType;
return ((itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
- itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT ||
itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT));
}
diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java
index dd82ecf..b09985c 100644
--- a/src/com/android/launcher3/folder/FolderAnimationManager.java
+++ b/src/com/android/launcher3/folder/FolderAnimationManager.java
@@ -236,9 +236,9 @@
mFolder, startRect, endRect, finalRadius, !mIsOpening));
// Create reveal animator for the folder content (capture the top 4 icons 2x2)
- int width = mDeviceProfile.folderCellLayoutBorderSpacePx
+ int width = mDeviceProfile.folderCellLayoutBorderSpacePx.x
+ mDeviceProfile.folderCellWidthPx * 2;
- int height = mDeviceProfile.folderCellLayoutBorderSpacePx
+ int height = mDeviceProfile.folderCellLayoutBorderSpacePx.y
+ mDeviceProfile.folderCellHeightPx * 2;
int page = mIsOpening ? mContent.getCurrentPage() : mContent.getDestinationPage();
int left = mContent.getPaddingLeft() + page * lp.width;
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 2c1100f..d78bfba 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -42,6 +42,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.app.animation.Interpolators;
import com.android.launcher3.Alarm;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.CellLayout;
@@ -56,7 +57,6 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
import com.android.launcher3.allapps.ActivityAllAppsContainerView;
-import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.dot.FolderDotInfo;
import com.android.launcher3.dragndrop.BaseItemDragListener;
@@ -260,7 +260,6 @@
private boolean willAcceptItem(ItemInfo item) {
final int itemType = item.itemType;
return ((itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
- itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT ||
itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) &&
item != mInfo && !mFolder.isOpen());
}
@@ -407,7 +406,7 @@
final int finalIndex = index;
dragLayer.animateView(animateView, to, finalAlpha,
finalScale, finalScale, DROP_IN_ANIMATION_DURATION,
- Interpolators.DEACCEL_2,
+ Interpolators.DECELERATE_2,
() -> {
mPreviewItemManager.hidePreviewItem(finalIndex, false);
mFolder.showItem(item);
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index 47677ea..7241b17 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -34,13 +34,9 @@
import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
import android.content.ContextWrapper;
-import android.content.Intent;
import android.content.res.TypedArray;
-import android.graphics.Color;
import android.graphics.PointF;
import android.graphics.Rect;
-import android.graphics.drawable.AdaptiveIconDrawable;
-import android.graphics.drawable.ColorDrawable;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
@@ -78,8 +74,6 @@
import com.android.launcher3.celllayout.CellPosMapper;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.FolderIcon;
-import com.android.launcher3.icons.BaseIconFactory;
-import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.model.BgDataModel;
import com.android.launcher3.model.BgDataModel.FixedContainerItems;
@@ -183,7 +177,6 @@
private final DeviceProfile mDp;
private final DeviceProfile mDpOrig;
private final Rect mInsets;
- private final WorkspaceItemInfo mWorkspaceItemInfo;
private final LayoutInflater mHomeElementInflater;
private final InsettableFrameLayout mRootView;
private final Hotseat mHotseat;
@@ -221,19 +214,6 @@
mDp.isTaskbarPresent ? 0 : currentWindowInsets.getSystemWindowInsetBottom());
mDp.updateInsets(mInsets);
- BaseIconFactory iconFactory =
- new BaseIconFactory(context, mIdp.fillResIconDpi, mIdp.iconBitmapSize) { };
- BitmapInfo iconInfo = iconFactory.createBadgedIconBitmap(
- new AdaptiveIconDrawable(
- new ColorDrawable(Color.WHITE),
- new ColorDrawable(Color.WHITE)));
-
- mWorkspaceItemInfo = new WorkspaceItemInfo();
- mWorkspaceItemInfo.bitmap = iconInfo;
- mWorkspaceItemInfo.intent = new Intent();
- mWorkspaceItemInfo.contentDescription = mWorkspaceItemInfo.title =
- context.getString(R.string.label_application);
-
mHomeElementInflater = LayoutInflater.from(
new ContextThemeWrapper(this, R.style.HomeScreenElementTheme));
mHomeElementInflater.setFactory2(this);
@@ -483,7 +463,6 @@
for (ItemInfo itemInfo : currentWorkspaceItems) {
switch (itemInfo.itemType) {
case Favorites.ITEM_TYPE_APPLICATION:
- case Favorites.ITEM_TYPE_SHORTCUT:
case Favorites.ITEM_TYPE_DEEP_SHORTCUT:
inflateAndAddIcon((WorkspaceItemInfo) itemInfo);
break;
diff --git a/src/com/android/launcher3/graphics/PreloadIconDrawable.java b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
index d366c4a..307052a 100644
--- a/src/com/android/launcher3/graphics/PreloadIconDrawable.java
+++ b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
@@ -17,8 +17,8 @@
package com.android.launcher3.graphics;
-import static com.android.launcher3.anim.Interpolators.EMPHASIZED;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.app.animation.Interpolators.EMPHASIZED;
+import static com.android.app.animation.Interpolators.LINEAR;
import static com.android.launcher3.config.FeatureFlags.ENABLE_DOWNLOAD_APP_UX_V2;
import static com.android.launcher3.config.FeatureFlags.ENABLE_DOWNLOAD_APP_UX_V3;
@@ -155,19 +155,19 @@
// Progress color
float[] m3HCT = new float[3];
ColorUtils.colorToM3HCT(primaryIconColor, m3HCT);
- mProgressColor = ColorUtils.M3HCTtoColor(
+ mProgressColor = ColorUtils.M3HCTToColor(
m3HCT[0],
m3HCT[1],
isDarkMode ? Math.max(m3HCT[2], 55) : Math.min(m3HCT[2], 40));
// Track color
- mTrackColor = ColorUtils.M3HCTtoColor(
+ mTrackColor = ColorUtils.M3HCTToColor(
m3HCT[0],
16,
isDarkMode ? 30 : 90
);
// Plate color
- mPlateColor = ColorUtils.M3HCTtoColor(
+ mPlateColor = ColorUtils.M3HCTToColor(
m3HCT[0],
isDarkMode ? 36 : 24,
isDarkMode ? (isThemed() ? 10 : 20) : 80
diff --git a/src/com/android/launcher3/graphics/SysUiScrim.java b/src/com/android/launcher3/graphics/SysUiScrim.java
index 21ebc98..a572a60 100644
--- a/src/com/android/launcher3/graphics/SysUiScrim.java
+++ b/src/com/android/launcher3/graphics/SysUiScrim.java
@@ -13,29 +13,30 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package com.android.launcher3.graphics;
+import static android.graphics.Paint.DITHER_FLAG;
+import static android.graphics.Paint.FILTER_BITMAP_FLAG;
+
import static com.android.launcher3.config.FeatureFlags.KEYGUARD_ANIMATION;
-import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
import android.animation.ObjectAnimator;
import android.graphics.Bitmap;
import android.graphics.Canvas;
-import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
-import android.graphics.drawable.Drawable;
import android.util.DisplayMetrics;
-import android.util.FloatProperty;
import android.view.View;
+import androidx.annotation.ColorInt;
+
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
+import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.testing.shared.ResourceUtils;
import com.android.launcher3.util.ScreenOnTracker;
import com.android.launcher3.util.ScreenOnTracker.ScreenOnListener;
@@ -46,33 +47,6 @@
*/
public class SysUiScrim implements View.OnAttachStateChangeListener {
- public static final FloatProperty<SysUiScrim> SYSUI_PROGRESS =
- new FloatProperty<SysUiScrim>("sysUiProgress") {
- @Override
- public Float get(SysUiScrim scrim) {
- return scrim.mSysUiProgress;
- }
-
- @Override
- public void setValue(SysUiScrim scrim, float value) {
- scrim.setSysUiProgress(value);
- }
- };
-
- private static final FloatProperty<SysUiScrim> SYSUI_ANIM_MULTIPLIER =
- new FloatProperty<SysUiScrim>("sysUiAnimMultiplier") {
- @Override
- public Float get(SysUiScrim scrim) {
- return scrim.mSysUiAnimMultiplier;
- }
-
- @Override
- public void setValue(SysUiScrim scrim, float value) {
- scrim.mSysUiAnimMultiplier = value;
- scrim.reapplySysUiAlpha();
- }
- };
-
/**
* Receiver used to get a signal that the user unlocked their device.
*/
@@ -92,44 +66,52 @@
}
};
- private static final int MAX_HOTSEAT_SCRIM_ALPHA = 100;
- private static final int ALPHA_MASK_HEIGHT_DP = 500;
- private static final int ALPHA_MASK_BITMAP_DP = 200;
- private static final int ALPHA_MASK_WIDTH_DP = 2;
+ private static final int MAX_SYSUI_SCRIM_ALPHA = 255;
+ private static final int ALPHA_MASK_BITMAP_WIDTH_DP = 2;
+
+ private static final int BOTTOM_MASK_HEIGHT_DP = 200;
+ private static final int TOP_MASK_HEIGHT_DP = 100;
private boolean mDrawTopScrim, mDrawBottomScrim;
- private final RectF mFinalMaskRect = new RectF();
- private final Paint mBottomMaskPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
- private final Bitmap mBottomMask;
- private final int mMaskHeight;
+ private final RectF mTopMaskRect = new RectF();
+ private final Paint mTopMaskPaint = new Paint(FILTER_BITMAP_FLAG | DITHER_FLAG);
+ private final Bitmap mTopMaskBitmap;
+ private final int mTopMaskHeight;
+
+ private final RectF mBottomMaskRect = new RectF();
+ private final Paint mBottomMaskPaint = new Paint(FILTER_BITMAP_FLAG | DITHER_FLAG);
+ private final Bitmap mBottomMaskBitmap;
+ private final int mBottomMaskHeight;
private final View mRoot;
private final BaseDraggingActivity mActivity;
- private final Drawable mTopScrim;
-
- private float mSysUiProgress = 1;
- private boolean mHideSysUiScrim;
+ private final boolean mHideSysUiScrim;
private boolean mAnimateScrimOnNextDraw = false;
- private float mSysUiAnimMultiplier = 1;
+ private final AnimatedFloat mSysUiAnimMultiplier = new AnimatedFloat(this::reapplySysUiAlpha);
+ private final AnimatedFloat mSysUiProgress = new AnimatedFloat(this::reapplySysUiAlpha);
public SysUiScrim(View view) {
mRoot = view;
mActivity = BaseDraggingActivity.fromContext(view.getContext());
- mMaskHeight = ResourceUtils.pxFromDp(ALPHA_MASK_BITMAP_DP,
- view.getResources().getDisplayMetrics());
- mTopScrim = Themes.getAttrDrawable(view.getContext(), R.attr.workspaceStatusBarScrim);
- if (mTopScrim != null) {
- mTopScrim.setDither(true);
- mBottomMask = createDitheredAlphaMask();
- mHideSysUiScrim = false;
- } else {
- mBottomMask = null;
- mHideSysUiScrim = true;
- }
+ DisplayMetrics dm = mActivity.getResources().getDisplayMetrics();
- view.addOnAttachStateChangeListener(this);
+ mTopMaskHeight = ResourceUtils.pxFromDp(TOP_MASK_HEIGHT_DP, dm);
+ mBottomMaskHeight = ResourceUtils.pxFromDp(BOTTOM_MASK_HEIGHT_DP, dm);
+ mHideSysUiScrim = Themes.getAttrBoolean(view.getContext(), R.attr.isWorkspaceDarkText);
+
+ mTopMaskBitmap = mHideSysUiScrim ? null : createDitheredAlphaMask(mTopMaskHeight,
+ new int[]{0x50FFFFFF, 0x0AFFFFFF, 0x00FFFFFF},
+ new float[]{0f, 0.7f, 1f});
+ mTopMaskPaint.setColor(0xFF222222);
+ mBottomMaskBitmap = mHideSysUiScrim ? null : createDitheredAlphaMask(mBottomMaskHeight,
+ new int[]{0x00FFFFFF, 0x2FFFFFFF},
+ new float[]{0f, 1f});
+
+ if (!KEYGUARD_ANIMATION.get() && !mHideSysUiScrim) {
+ view.addOnAttachStateChangeListener(this);
+ }
}
/**
@@ -137,16 +119,16 @@
*/
public void draw(Canvas canvas) {
if (!mHideSysUiScrim) {
- if (mSysUiProgress <= 0) {
+ if (mSysUiProgress.value <= 0) {
mAnimateScrimOnNextDraw = false;
return;
}
if (mAnimateScrimOnNextDraw) {
- mSysUiAnimMultiplier = 0;
+ mSysUiAnimMultiplier.value = 0;
reapplySysUiAlphaNoInvalidate();
- ObjectAnimator oa = createSysuiMultiplierAnim(1);
+ ObjectAnimator oa = mSysUiAnimMultiplier.animateToValue(1);
oa.setDuration(600);
oa.setStartDelay(mActivity.getWindow().getTransitionBackgroundFadeDuration());
oa.start();
@@ -154,21 +136,26 @@
}
if (mDrawTopScrim) {
- mTopScrim.draw(canvas);
+ canvas.drawBitmap(mTopMaskBitmap, null, mTopMaskRect, mTopMaskPaint);
}
if (mDrawBottomScrim) {
- canvas.drawBitmap(mBottomMask, null, mFinalMaskRect, mBottomMaskPaint);
+ canvas.drawBitmap(mBottomMaskBitmap, null, mBottomMaskRect, mBottomMaskPaint);
}
}
}
/**
- * @return an ObjectAnimator that controls the fade in/out of the sys ui scrim.
+ * Returns the sysui multiplier property for controlling fade in/out of the scrim
*/
- public ObjectAnimator createSysuiMultiplierAnim(float... values) {
- ObjectAnimator anim = ObjectAnimator.ofFloat(this, SYSUI_ANIM_MULTIPLIER, values);
- anim.setAutoCancel(true);
- return anim;
+ public AnimatedFloat getSysUIMultiplier() {
+ return mSysUiAnimMultiplier;
+ }
+
+ /**
+ * Returns the sysui progress property for controlling fade in/out of the scrim
+ */
+ public AnimatedFloat getSysUIProgress() {
+ return mSysUiProgress;
}
/**
@@ -180,44 +167,26 @@
*/
public void onInsetsChanged(Rect insets) {
DeviceProfile dp = mActivity.getDeviceProfile();
- mDrawTopScrim = mTopScrim != null && insets.top > 0;
- mDrawBottomScrim = mBottomMask != null
- && !dp.isVerticalBarLayout()
- && !dp.isGestureMode
- && !dp.isTaskbarPresent;
+ mDrawTopScrim = insets.top > 0;
+ mDrawBottomScrim = !dp.isVerticalBarLayout() && !dp.isGestureMode && !dp.isTaskbarPresent;
}
@Override
public void onViewAttachedToWindow(View view) {
- if (!KEYGUARD_ANIMATION.get() && mTopScrim != null) {
- ScreenOnTracker.INSTANCE.get(mActivity).addListener(mScreenOnListener);
- }
+ ScreenOnTracker.INSTANCE.get(mActivity).addListener(mScreenOnListener);
}
@Override
public void onViewDetachedFromWindow(View view) {
- if (!KEYGUARD_ANIMATION.get() && mTopScrim != null) {
- ScreenOnTracker.INSTANCE.get(mActivity).removeListener(mScreenOnListener);
- }
+ ScreenOnTracker.INSTANCE.get(mActivity).removeListener(mScreenOnListener);
}
/**
* Set the width and height of the view being scrimmed
- * @param w
- * @param h
*/
public void setSize(int w, int h) {
- if (mTopScrim != null) {
- mTopScrim.setBounds(0, 0, w, h);
- mFinalMaskRect.set(0, h - mMaskHeight, w, h);
- }
- }
-
- private void setSysUiProgress(float progress) {
- if (progress != mSysUiProgress) {
- mSysUiProgress = progress;
- reapplySysUiAlpha();
- }
+ mTopMaskRect.set(0, 0, w, mTopMaskHeight);
+ mBottomMaskRect.set(0, h - mBottomMaskHeight, w, h);
}
private void reapplySysUiAlpha() {
@@ -228,29 +197,21 @@
}
private void reapplySysUiAlphaNoInvalidate() {
- float factor = mSysUiProgress * mSysUiAnimMultiplier;
- mBottomMaskPaint.setAlpha(Math.round(MAX_HOTSEAT_SCRIM_ALPHA * factor));
- if (mTopScrim != null) {
- mTopScrim.setAlpha(Math.round(255 * factor));
- }
+ float factor = mSysUiProgress.value * mSysUiAnimMultiplier.value;
+ mBottomMaskPaint.setAlpha(Math.round(MAX_SYSUI_SCRIM_ALPHA * factor));
+ mTopMaskPaint.setAlpha(Math.round(MAX_SYSUI_SCRIM_ALPHA * factor));
}
- private Bitmap createDitheredAlphaMask() {
+ private Bitmap createDitheredAlphaMask(int height, @ColorInt int[] colors, float[] positions) {
DisplayMetrics dm = mActivity.getResources().getDisplayMetrics();
- int width = ResourceUtils.pxFromDp(ALPHA_MASK_WIDTH_DP, dm);
- int gradientHeight = ResourceUtils.pxFromDp(ALPHA_MASK_HEIGHT_DP, dm);
- Bitmap dst = Bitmap.createBitmap(width, mMaskHeight, Bitmap.Config.ALPHA_8);
+ int width = ResourceUtils.pxFromDp(ALPHA_MASK_BITMAP_WIDTH_DP, dm);
+ Bitmap dst = Bitmap.createBitmap(width, height, Bitmap.Config.ALPHA_8);
Canvas c = new Canvas(dst);
- Paint paint = new Paint(Paint.DITHER_FLAG);
- LinearGradient lg = new LinearGradient(0, 0, 0, gradientHeight,
- new int[]{
- 0x00FFFFFF,
- setColorAlphaBound(Color.WHITE, (int) (0xFF * 0.95)),
- 0xFFFFFFFF},
- new float[]{0f, 0.8f, 1f},
- Shader.TileMode.CLAMP);
+ Paint paint = new Paint(DITHER_FLAG);
+ LinearGradient lg = new LinearGradient(0, 0, 0, height,
+ colors, positions, Shader.TileMode.CLAMP);
paint.setShader(lg);
- c.drawRect(0, 0, width, gradientHeight, paint);
+ c.drawPaint(paint);
return dst;
}
}
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index 66ed779..7e065a9 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -831,6 +831,10 @@
*/
public interface StatsLatencyLogger {
+ /**
+ * Should be in sync with:
+ * google3/wireless/android/sysui/aster/asterstats/launcher_event_processed.proto
+ */
enum LatencyType {
UNKNOWN(0),
// example: launcher restart that happens via daily backup and restore
diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
index 27d1f78..5e86bd6 100644
--- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
+++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
@@ -91,8 +91,7 @@
List<ItemInfo> filteredItems = new ArrayList<>();
for (Pair<ItemInfo, Object> entry : mItemList) {
ItemInfo item = entry.first;
- if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
- item.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
+ if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
// Short-circuit this logic if the icon exists somewhere on the workspace
if (shortcutExists(dataModel, item.getIntent(), item.user)) {
continue;
diff --git a/src/com/android/launcher3/model/BaseLauncherBinder.java b/src/com/android/launcher3/model/BaseLauncherBinder.java
index 5d85b1c..556ac26 100644
--- a/src/com/android/launcher3/model/BaseLauncherBinder.java
+++ b/src/com/android/launcher3/model/BaseLauncherBinder.java
@@ -106,6 +106,7 @@
synchronized (mBgDataModel) {
if (incrementBindId) {
mBgDataModel.lastBindId++;
+ mBgDataModel.lastLoadId = mApp.getModel().getLastLoadId();
}
mMyBindingId = mBgDataModel.lastBindId;
return new DisjointWorkspaceBinder(workspacePages);
@@ -126,6 +127,7 @@
mBgDataModel.extraItems.forEach(extraItems::add);
if (incrementBindId) {
mBgDataModel.lastBindId++;
+ mBgDataModel.lastLoadId = mApp.getModel().getLastLoadId();
}
mMyBindingId = mBgDataModel.lastBindId;
workspaceItemCount = mBgDataModel.itemsIdMap.size();
@@ -312,7 +314,8 @@
currentScreenIds, pendingTasks, workspaceItemCount, isBindSync);
}, mUiExecutor);
- mCallbacks.bindStringCache(mBgDataModel.stringCache.clone());
+ StringCache cacheClone = mBgDataModel.stringCache.clone();
+ executeCallbacksTask(c -> c.bindStringCache(cacheClone), pendingExecutor);
}
private void bindWorkspaceItems(
@@ -440,9 +443,8 @@
.resumeModelPush(FLAG_LOADER_RUNNING);
});
- for (Callbacks cb : mCallbacksList) {
- cb.bindStringCache(mBgDataModel.stringCache.clone());
- }
+ StringCache cacheClone = mBgDataModel.stringCache.clone();
+ executeCallbacksTask(c -> c.bindStringCache(cacheClone), mUiExecutor);
}
private void bindWorkspaceItems(final ArrayList<ItemInfo> workspaceItems) {
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index d94df51..7bcd038 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -126,6 +126,11 @@
public int lastBindId = 0;
/**
+ * Load id for which the callbacks were successfully bound
+ */
+ public int lastLoadId = -1;
+
+ /**
* Clears all the data
*/
public synchronized void clear() {
@@ -211,7 +216,6 @@
// Fall through.
}
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
- case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
workspaceItems.remove(item);
break;
case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
@@ -246,7 +250,6 @@
break;
case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
- case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP ||
item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
workspaceItems.add(item);
diff --git a/src/com/android/launcher3/model/DeviceGridState.java b/src/com/android/launcher3/model/DeviceGridState.java
index edc8c1b..f24d1d2 100644
--- a/src/com/android/launcher3/model/DeviceGridState.java
+++ b/src/com/android/launcher3/model/DeviceGridState.java
@@ -33,6 +33,7 @@
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.logging.StatsLogManager.LauncherEvent;
+import com.android.launcher3.util.MainThreadInitializedObject.SandboxContext;
import java.util.Locale;
import java.util.Objects;
@@ -92,6 +93,9 @@
* Stores the device state to shared preferences
*/
public void writeToPrefs(Context context) {
+ if (context instanceof SandboxContext) {
+ return;
+ }
LauncherPrefs.get(context).put(
WORKSPACE_SIZE.to(mGridSizeString),
HOTSEAT_COUNT.to(mNumHotseat),
diff --git a/src/com/android/launcher3/model/GridSizeMigrationUtil.java b/src/com/android/launcher3/model/GridSizeMigrationUtil.java
index 9a6cde6..c233872 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationUtil.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationUtil.java
@@ -45,7 +45,6 @@
import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
import com.android.launcher3.util.GridOccupancy;
import com.android.launcher3.util.IntArray;
-import com.android.launcher3.util.MainThreadInitializedObject.SandboxContext;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.android.launcher3.widget.WidgetManagerHelper;
@@ -133,10 +132,8 @@
Log.v(TAG, "Workspace migration completed in "
+ (System.currentTimeMillis() - migrationStartTime));
- if (!(context instanceof SandboxContext)) {
- // Save current configuration, so that the migration does not run again.
- destDeviceState.writeToPrefs(context);
- }
+ // Save current configuration, so that the migration does not run again.
+ destDeviceState.writeToPrefs(context);
}
}
@@ -455,7 +452,6 @@
try {
// calculate weight
switch (entry.itemType) {
- case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: {
entry.mIntent = c.getString(indexIntent);
@@ -531,7 +527,6 @@
try {
// calculate weight
switch (entry.itemType) {
- case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: {
entry.mIntent = c.getString(indexIntent);
diff --git a/src/com/android/launcher3/model/ItemInstallQueue.java b/src/com/android/launcher3/model/ItemInstallQueue.java
index fa0511c..9a3abd4 100644
--- a/src/com/android/launcher3/model/ItemInstallQueue.java
+++ b/src/com/android/launcher3/model/ItemInstallQueue.java
@@ -286,7 +286,6 @@
final WorkspaceItemInfo si = new WorkspaceItemInfo();
si.user = user;
- si.itemType = ITEM_TYPE_APPLICATION;
LauncherActivityInfo lai;
boolean usePackageIcon = laiList.isEmpty();
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index 2054d93..33332f0 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -193,9 +193,7 @@
public IconRequestInfo<WorkspaceItemInfo> createIconRequestInfo(
WorkspaceItemInfo wai, boolean useLowResIcon) {
- byte[] iconBlob = itemType == Favorites.ITEM_TYPE_SHORTCUT
- || itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT
- || restoreFlag != 0
+ byte[] iconBlob = itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT || restoreFlag != 0
? getIconBlob() : null;
return new IconRequestInfo<>(wai, mActivityInfo, iconBlob, useLowResIcon);
@@ -347,7 +345,6 @@
}
final WorkspaceItemInfo info = new WorkspaceItemInfo();
- info.itemType = Favorites.ITEM_TYPE_APPLICATION;
info.user = user;
info.intent = newIntent;
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index ca356b0..3daf4af 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -33,7 +33,6 @@
import android.annotation.SuppressLint;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -61,7 +60,6 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.LauncherSettings.Settings;
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.Folder;
@@ -203,7 +201,7 @@
}
}
- Object traceToken = TraceHelper.INSTANCE.beginSection(TAG);
+ TraceHelper.INSTANCE.beginSection(TAG);
LoaderMemoryLogger memoryLogger = new LoaderMemoryLogger();
try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
List<ShortcutInfo> allShortcuts = new ArrayList<>();
@@ -327,7 +325,7 @@
memoryLogger.printLogs();
throw e;
}
- TraceHelper.INSTANCE.endSection(traceToken);
+ TraceHelper.INSTANCE.endSection();
}
public synchronized void stopLocked() {
@@ -361,25 +359,15 @@
String selection,
@Nullable LoaderMemoryLogger memoryLogger) {
final Context context = mApp.getContext();
- final ContentResolver contentResolver = context.getContentResolver();
final PackageManagerHelper pmHelper = new PackageManagerHelper(context);
final boolean isSafeMode = pmHelper.isSafeMode();
final boolean isSdCardReady = Utilities.isBootCompleted();
final WidgetManagerHelper widgetHelper = new WidgetManagerHelper(context);
- boolean clearDb = false;
- if (!mApp.getModel().getModelDbController().migrateGridIfNeeded()) {
- // Migration failed. Clear workspace.
- clearDb = true;
- }
-
- if (clearDb) {
- Log.d(TAG, "loadWorkspace: resetting launcher database");
- Settings.call(contentResolver, Settings.METHOD_CREATE_EMPTY_DB);
- }
-
+ ModelDbController dbController = mApp.getModel().getModelDbController();
+ dbController.tryMigrateDB();
Log.d(TAG, "loadWorkspace: loading default favorites");
- Settings.call(contentResolver, Settings.METHOD_LOAD_DEFAULT_FAVORITES);
+ dbController.loadDefaultFavoritesIfNecessary();
synchronized (mBgDataModel) {
mBgDataModel.clear();
@@ -393,12 +381,11 @@
mFirstScreenBroadcast = new FirstScreenBroadcast(installingPkgs);
mShortcutKeyToPinnedShortcuts = new HashMap<>();
- ModelDbController dbController = mApp.getModel().getModelDbController();
final LoaderCursor c = new LoaderCursor(
dbController.query(TABLE_NAME, null, selection, null, null),
mApp, mUserManagerState);
final Bundle extras = c.getExtras();
- mDbName = extras == null ? null : extras.getString(Settings.EXTRA_DB_NAME);
+ mDbName = extras == null ? null : extras.getString(ModelDbController.EXTRA_DB_NAME);
try {
final LongSparseArray<Boolean> unlockedUsers = new LongSparseArray<>();
@@ -505,7 +492,6 @@
boolean allowMissingTarget = false;
switch (c.itemType) {
- case Favorites.ITEM_TYPE_SHORTCUT:
case Favorites.ITEM_TYPE_APPLICATION:
case Favorites.ITEM_TYPE_DEEP_SHORTCUT:
Intent intent = c.parseIntent();
@@ -519,9 +505,8 @@
ComponentName cn = intent.getComponent();
String targetPkg = cn == null ? intent.getPackage() : cn.getPackageName();
- if (TextUtils.isEmpty(targetPkg)
- && c.itemType != Favorites.ITEM_TYPE_SHORTCUT) {
- c.markDeleted("Only legacy shortcuts can have null package");
+ if (TextUtils.isEmpty(targetPkg)) {
+ c.markDeleted("Shortcuts can't have null package");
return;
}
@@ -917,9 +902,7 @@
private void sanitizeFolders(boolean itemsDeleted) {
if (itemsDeleted) {
// Remove any empty folder
- int[] deletedFolderIds = Settings.call(mApp.getContext().getContentResolver(),
- Settings.METHOD_DELETE_EMPTY_FOLDERS)
- .getIntArray(Settings.EXTRA_VALUE);
+ IntArray deletedFolderIds = mApp.getModel().getModelDbController().deleteEmptyFolders();
synchronized (mBgDataModel) {
for (int folderId : deletedFolderIds) {
mBgDataModel.workspaceItems.remove(mBgDataModel.folders.get(folderId));
@@ -932,11 +915,9 @@
private void sanitizeWidgetsShortcutsAndPackages() {
Context context = mApp.getContext();
- ContentResolver contentResolver = context.getContentResolver();
// Remove any ghost widgets
- Settings.call(contentResolver,
- Settings.METHOD_REMOVE_GHOST_WIDGETS);
+ mApp.getModel().getModelDbController().removeGhostWidgets();
// Update pinned state of model shortcuts
mBgDataModel.updateShortcutPinnedState(context);
diff --git a/src/com/android/launcher3/model/ModelDbController.java b/src/com/android/launcher3/model/ModelDbController.java
index f0e5ef6..1b1b38f 100644
--- a/src/com/android/launcher3/model/ModelDbController.java
+++ b/src/com/android/launcher3/model/ModelDbController.java
@@ -37,7 +37,6 @@
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
-import android.os.Binder;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.Process;
@@ -85,6 +84,7 @@
private static final String TAG = "LauncherProvider";
private static final String EMPTY_DATABASE_CREATED = "EMPTY_DATABASE_CREATED";
+ public static final String EXTRA_DB_NAME = "db_name";
protected DatabaseHelper mOpenHelper;
@@ -140,7 +140,7 @@
table, projection, selection, selectionArgs, null, null, sortOrder);
final Bundle extra = new Bundle();
- extra.putString(LauncherSettings.Settings.EXTRA_DB_NAME, mOpenHelper.getDatabaseName());
+ extra.putString(EXTRA_DB_NAME, mOpenHelper.getDatabaseName());
result.setExtras(extra);
return result;
}
@@ -162,28 +162,6 @@
}
/**
- * Similar to insert but for adding multiple values in a transaction.
- */
- @WorkerThread
- public int bulkInsert(String table, ContentValues[] values) {
- createDbIfNotExists();
-
- SQLiteDatabase db = mOpenHelper.getWritableDatabase();
- try (SQLiteTransaction t = new SQLiteTransaction(db)) {
- int numValues = values.length;
- for (int i = 0; i < numValues; i++) {
- addModifiedTime(values[i]);
- if (mOpenHelper.dbInsertAndCheck(db, table, values[i]) < 0) {
- return 0;
- }
- }
- onAddOrDeleteOp(db);
- t.commit();
- }
- return values.length;
- }
-
- /**
* Refer {@link SQLiteDatabase#delete(String, String, String[])}
*/
@WorkerThread
@@ -191,10 +169,6 @@
createDbIfNotExists();
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
- if (Binder.getCallingPid() != Process.myPid()
- && Favorites.TABLE_NAME.equalsIgnoreCase(table)) {
- mOpenHelper.removeGhostWidgets(mOpenHelper.getWritableDatabase());
- }
int count = db.delete(table, selection, selectionArgs);
if (count > 0) {
onAddOrDeleteOp(db);
@@ -250,6 +224,7 @@
public void createEmptyDB() {
createDbIfNotExists();
mOpenHelper.createEmptyDB(mOpenHelper.getWritableDatabase());
+ LauncherPrefs.get(mContext).putSync(getEmptyDbCreatedKey().to(true));
}
/**
@@ -280,14 +255,34 @@
mOpenHelper.getReadableDatabase(), Favorites.HYBRID_HOTSEAT_BACKUP_TABLE);
}
+
+ /**
+ * Migrates the DB if needed. If the migration failed, it clears the DB.
+ */
+ public void tryMigrateDB() {
+ if (!migrateGridIfNeeded()) {
+ Log.d(TAG, "Migration failed: resetting launcher database");
+ createEmptyDB();
+ LauncherPrefs.get(mContext).putSync(
+ getEmptyDbCreatedKey(mOpenHelper.getDatabaseName()).to(true));
+
+ // Write the grid state to avoid another migration
+ new DeviceGridState(LauncherAppState.getIDP(mContext)).writeToPrefs(mContext);
+ }
+ }
+
/**
* Migrates the DB if needed, and returns false if the migration failed
* and DB needs to be cleared.
* @return true if migration was success or ignored, false if migration failed
* and the DB should be reset.
*/
- public boolean migrateGridIfNeeded() {
+ private boolean migrateGridIfNeeded() {
createDbIfNotExists();
+ if (LauncherPrefs.get(mContext).get(getEmptyDbCreatedKey())) {
+ // If we have already create a new DB, ignore migration
+ return false;
+ }
InvariantDeviceProfile idp = LauncherAppState.getIDP(mContext);
if (!GridSizeMigrationUtil.needsToMigrate(mContext, idp)) {
return true;
@@ -358,7 +353,7 @@
}
private void clearFlagEmptyDbCreated() {
- LauncherPrefs.get(mContext).removeSync(getEmptyDbCreatedKey(mOpenHelper.getDatabaseName()));
+ LauncherPrefs.get(mContext).removeSync(getEmptyDbCreatedKey());
}
/**
@@ -372,7 +367,7 @@
public synchronized void loadDefaultFavoritesIfNecessary() {
createDbIfNotExists();
- if (LauncherPrefs.get(mContext).get(getEmptyDbCreatedKey(mOpenHelper.getDatabaseName()))) {
+ if (LauncherPrefs.get(mContext).get(getEmptyDbCreatedKey())) {
Log.d(TAG, "loading default workspace");
LauncherWidgetHolder widgetHolder = mOpenHelper.newLauncherWidgetHolder();
@@ -491,6 +486,10 @@
mOpenHelper, mContext.getResources(), defaultLayout);
}
+ private ConstantItem<Boolean> getEmptyDbCreatedKey() {
+ return getEmptyDbCreatedKey(mOpenHelper.getDatabaseName());
+ }
+
/**
* Re-composite given key in respect to database. If the current db is
* {@link LauncherFiles#LAUNCHER_DB}, return the key as-is. Otherwise append the db name to
diff --git a/src/com/android/launcher3/model/ModelWriter.java b/src/com/android/launcher3/model/ModelWriter.java
index ddb8b05..a6b4d59 100644
--- a/src/com/android/launcher3/model/ModelWriter.java
+++ b/src/com/android/launcher3/model/ModelWriter.java
@@ -16,13 +16,12 @@
package com.android.launcher3.model;
+import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME;
+import static com.android.launcher3.provider.LauncherDbUtils.itemIdMatch;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
-import android.content.ContentProviderOperation;
-import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
-import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
@@ -32,9 +31,7 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherModel.CallbackTask;
-import com.android.launcher3.LauncherProvider;
import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.LauncherSettings.Settings;
import com.android.launcher3.Utilities;
import com.android.launcher3.celllayout.CellPosMapper;
import com.android.launcher3.celllayout.CellPosMapper.CellPos;
@@ -45,6 +42,7 @@
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
import com.android.launcher3.util.ContentWriter;
import com.android.launcher3.util.Executors;
import com.android.launcher3.util.ItemInfoMatcher;
@@ -80,7 +78,7 @@
private final boolean mVerifyChanges;
// Keep track of delete operations that occur when an Undo option is present; we may not commit.
- private final List<Runnable> mDeleteRunnables = new ArrayList<>();
+ private final List<ModelTask> mDeleteRunnables = new ArrayList<>();
private boolean mPreparingToUndo;
private final CellPosMapper mCellPosMapper;
@@ -217,8 +215,7 @@
item.spanX = spanX;
item.spanY = spanY;
notifyItemModified(item);
-
- MODEL_EXECUTOR.execute(new UpdateItemRunnable(item, () ->
+ new UpdateItemRunnable(item, () ->
new ContentWriter(mContext)
.put(Favorites.CONTAINER, item.container)
.put(Favorites.CELLX, item.cellX)
@@ -226,7 +223,8 @@
.put(Favorites.RANK, item.rank)
.put(Favorites.SPANX, item.spanX)
.put(Favorites.SPANY, item.spanY)
- .put(Favorites.SCREEN, item.screenId)));
+ .put(Favorites.SCREEN, item.screenId))
+ .executeOnModelThread();
}
/**
@@ -234,11 +232,11 @@
*/
public void updateItemInDatabase(ItemInfo item) {
notifyItemModified(item);
- MODEL_EXECUTOR.execute(new UpdateItemRunnable(item, () -> {
+ new UpdateItemRunnable(item, () -> {
ContentWriter writer = new ContentWriter(mContext);
item.onAddToDatabase(writer);
return writer;
- }));
+ }).executeOnModelThread();
}
private void notifyItemModified(ItemInfo item) {
@@ -253,13 +251,12 @@
int container, int screenId, int cellX, int cellY) {
updateItemInfoProps(item, container, screenId, cellX, cellY);
- final ContentResolver cr = mContext.getContentResolver();
- item.id = Settings.call(cr, Settings.METHOD_NEW_ITEM_ID).getInt(Settings.EXTRA_VALUE);
+ item.id = mModel.getModelDbController().generateNewItemId();
notifyOtherCallbacks(c -> c.bindItems(Collections.singletonList(item), false));
ModelVerifier verifier = new ModelVerifier();
final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
- MODEL_EXECUTOR.execute(() -> {
+ newModelTask(() -> {
// Write the item on background thread, as some properties might have been updated in
// the background.
final ContentWriter writer = new ContentWriter(mContext);
@@ -272,7 +269,7 @@
mBgDataModel.addItem(mContext, item, true);
verifier.verifyModel();
}
- });
+ }).executeOnModelThread();
}
/**
@@ -303,15 +300,13 @@
Collectors.joining(","))
+ ". Reason: [" + (TextUtils.isEmpty(reason) ? "unknown" : reason) + "]");
notifyDelete(items);
- enqueueDeleteRunnable(() -> {
+ enqueueDeleteRunnable(newModelTask(() -> {
for (ItemInfo item : items) {
- final Uri uri = Favorites.getContentUri(item.id);
- mContext.getContentResolver().delete(uri, null, null);
-
+ mModel.getModelDbController().delete(TABLE_NAME, itemIdMatch(item.id), null);
mBgDataModel.removeItem(mContext, item);
verifier.verifyModel();
}
- });
+ }));
}
/**
@@ -321,7 +316,7 @@
ModelVerifier verifier = new ModelVerifier();
notifyDelete(Collections.singleton(info));
- enqueueDeleteRunnable(() -> {
+ enqueueDeleteRunnable(newModelTask(() -> {
mModel.getModelDbController().delete(Favorites.TABLE_NAME,
Favorites.CONTAINER + "=" + info.id, null);
mBgDataModel.removeItem(mContext, info.contents);
@@ -331,7 +326,7 @@
Favorites._ID + "=" + info.id, null);
mBgDataModel.removeItem(mContext, info);
verifier.verifyModel();
- });
+ }));
}
/**
@@ -343,7 +338,7 @@
if (holder != null && !info.isCustomWidget() && info.isWidgetIdAllocated()) {
// Deleting an app widget ID is a void call but writes to disk before returning
// to the caller...
- enqueueDeleteRunnable(() -> holder.deleteAppWidgetId(info.appWidgetId));
+ enqueueDeleteRunnable(newModelTask(() -> holder.deleteAppWidgetId(info.appWidgetId)));
}
deleteItemFromDatabase(info, reason);
}
@@ -373,19 +368,17 @@
* {@link #commitDelete()} is called (or abandoned if {@link #abortDelete} is called).
* Otherwise, we run the Runnable immediately.
*/
- private void enqueueDeleteRunnable(Runnable r) {
+ private void enqueueDeleteRunnable(ModelTask r) {
if (mPreparingToUndo) {
mDeleteRunnables.add(r);
} else {
- MODEL_EXECUTOR.execute(r);
+ r.executeOnModelThread();
}
}
public void commitDelete() {
mPreparingToUndo = false;
- for (Runnable runnable : mDeleteRunnables) {
- MODEL_EXECUTOR.execute(runnable);
- }
+ mDeleteRunnables.forEach(ModelTask::executeOnModelThread);
mDeleteRunnables.clear();
}
@@ -426,10 +419,9 @@
}
@Override
- public void run() {
- Uri uri = Favorites.getContentUri(mItemId);
- mContext.getContentResolver().update(uri, mWriter.get().getValues(mContext),
- null, null);
+ public void runImpl() {
+ mModel.getModelDbController().update(
+ TABLE_NAME, mWriter.get().getValues(mContext), itemIdMatch(mItemId), null);
updateItemArrays(mItem, mItemId);
}
}
@@ -444,27 +436,24 @@
}
@Override
- public void run() {
- ArrayList<ContentProviderOperation> ops = new ArrayList<>();
- int count = mItems.size();
- for (int i = 0; i < count; i++) {
- ItemInfo item = mItems.get(i);
- final int itemId = item.id;
- final Uri uri = Favorites.getContentUri(itemId);
- ContentValues values = mValues.get(i);
-
- ops.add(ContentProviderOperation.newUpdate(uri).withValues(values).build());
- updateItemArrays(item, itemId);
- }
- try {
- mContext.getContentResolver().applyBatch(LauncherProvider.AUTHORITY, ops);
+ public void runImpl() {
+ try (SQLiteTransaction t = mModel.getModelDbController().newTransaction()) {
+ int count = mItems.size();
+ for (int i = 0; i < count; i++) {
+ ItemInfo item = mItems.get(i);
+ final int itemId = item.id;
+ mModel.getModelDbController().update(
+ TABLE_NAME, mValues.get(i), itemIdMatch(itemId), null);
+ updateItemArrays(item, itemId);
+ }
+ t.commit();
} catch (Exception e) {
e.printStackTrace();
}
}
}
- private abstract class UpdateItemBaseRunnable implements Runnable {
+ private abstract class UpdateItemBaseRunnable extends ModelTask {
private final StackTraceElement[] mStackTrace;
private final ModelVerifier mVerifier = new ModelVerifier();
@@ -498,7 +487,6 @@
modelItem.container == Favorites.CONTAINER_HOTSEAT)) {
switch (modelItem.itemType) {
case Favorites.ITEM_TYPE_APPLICATION:
- case Favorites.ITEM_TYPE_SHORTCUT:
case Favorites.ITEM_TYPE_DEEP_SHORTCUT:
case Favorites.ITEM_TYPE_FOLDER:
if (!mBgDataModel.workspaceItems.contains(modelItem)) {
@@ -516,6 +504,35 @@
}
}
+ private abstract class ModelTask implements Runnable {
+
+ private final int mLoadId = mBgDataModel.lastLoadId;
+
+ @Override
+ public final void run() {
+ if (mLoadId != mModel.getLastLoadId()) {
+ Log.d(TAG, "Model changed before the task could execute");
+ return;
+ }
+ runImpl();
+ }
+
+ public final void executeOnModelThread() {
+ MODEL_EXECUTOR.execute(this);
+ }
+
+ public abstract void runImpl();
+ }
+
+ private ModelTask newModelTask(Runnable r) {
+ return new ModelTask() {
+ @Override
+ public void runImpl() {
+ r.run();
+ }
+ };
+ }
+
/**
* Utility class to verify model updates are propagated properly to the callback.
*/
diff --git a/src/com/android/launcher3/model/WorkspaceItemSpaceFinder.java b/src/com/android/launcher3/model/WorkspaceItemSpaceFinder.java
index 93fc6a5..1fc8a03 100644
--- a/src/com/android/launcher3/model/WorkspaceItemSpaceFinder.java
+++ b/src/com/android/launcher3/model/WorkspaceItemSpaceFinder.java
@@ -82,9 +82,7 @@
if (!found) {
// Still no position found. Add a new screen to the end.
- screenId = LauncherSettings.Settings.call(app.getContext().getContentResolver(),
- LauncherSettings.Settings.METHOD_NEW_SCREEN_ID)
- .getInt(LauncherSettings.Settings.EXTRA_VALUE);
+ screenId = app.getModel().getModelDbController().getNewScreenId();
// Save the screen id for binding in the workspace
workspaceScreens.add(screenId);
diff --git a/src/com/android/launcher3/model/data/ItemInfo.java b/src/com/android/launcher3/model/data/ItemInfo.java
index 660929c..ba1547f 100644
--- a/src/com/android/launcher3/model/data/ItemInfo.java
+++ b/src/com/android/launcher3/model/data/ItemInfo.java
@@ -30,7 +30,6 @@
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
-import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_TASK;
import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.CONTAINER_NOT_SET;
import static com.android.launcher3.shortcuts.ShortcutKey.EXTRA_SHORTCUT_ID;
@@ -88,7 +87,6 @@
/**
* One of {@link Favorites#ITEM_TYPE_APPLICATION},
- * {@link Favorites#ITEM_TYPE_SHORTCUT},
* {@link Favorites#ITEM_TYPE_DEEP_SHORTCUT}
* {@link Favorites#ITEM_TYPE_FOLDER},
* {@link Favorites#ITEM_TYPE_APP_PAIR},
@@ -368,13 +366,6 @@
})
.orElse(LauncherAtom.Shortcut.newBuilder()));
break;
- case ITEM_TYPE_SHORTCUT:
- itemBuilder
- .setShortcut(nullableComponent
- .map(component -> LauncherAtom.Shortcut.newBuilder()
- .setShortcutName(component.flattenToShortString()))
- .orElse(LauncherAtom.Shortcut.newBuilder()));
- break;
case ITEM_TYPE_APPWIDGET:
itemBuilder
.setWidget(nullableComponent
diff --git a/src/com/android/launcher3/model/data/WorkspaceItemInfo.java b/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
index 01606d4..3ce194d 100644
--- a/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
+++ b/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
@@ -96,7 +96,7 @@
public WorkspaceItemInfo() {
- itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
+ itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
}
public WorkspaceItemInfo(WorkspaceItemInfo info) {
@@ -205,8 +205,8 @@
@Override
public ComponentName getTargetComponent() {
ComponentName cn = super.getTargetComponent();
- if (cn == null && (itemType == Favorites.ITEM_TYPE_SHORTCUT || hasStatusFlag(
- FLAG_SUPPORTS_WEB_UI | FLAG_AUTOINSTALL_ICON | FLAG_RESTORED_ICON))) {
+ if (cn == null && hasStatusFlag(
+ FLAG_SUPPORTS_WEB_UI | FLAG_AUTOINSTALL_ICON | FLAG_RESTORED_ICON)) {
// Legacy shortcuts and promise icons with web UI may not have a componentName but just
// a packageName. In that case create a empty componentName instead of adding additional
// check everywhere.
diff --git a/src/com/android/launcher3/notification/NotificationContainer.java b/src/com/android/launcher3/notification/NotificationContainer.java
index 9eb05cd..7cc9ad3 100644
--- a/src/com/android/launcher3/notification/NotificationContainer.java
+++ b/src/com/android/launcher3/notification/NotificationContainer.java
@@ -15,7 +15,7 @@
*/
package com.android.launcher3.notification;
-import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
+import static com.android.app.animation.Interpolators.scrollInterpolatorForVelocity;
import static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL;
import android.animation.Animator;
diff --git a/src/com/android/launcher3/notification/NotificationMainView.java b/src/com/android/launcher3/notification/NotificationMainView.java
index 16a4057..ecd018b 100644
--- a/src/com/android/launcher3/notification/NotificationMainView.java
+++ b/src/com/android/launcher3/notification/NotificationMainView.java
@@ -16,8 +16,8 @@
package com.android.launcher3.notification;
+import static com.android.app.animation.Interpolators.LINEAR;
import static com.android.launcher3.Utilities.mapToRange;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NOTIFICATION_DISMISSED;
import android.animation.AnimatorSet;
diff --git a/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java b/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java
index b24ee34..06da8c5 100644
--- a/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java
+++ b/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java
@@ -72,7 +72,7 @@
}
public int getItemType() {
- return LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
+ return LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
}
@Override
diff --git a/src/com/android/launcher3/pm/UserCache.java b/src/com/android/launcher3/pm/UserCache.java
index 24a9609..c313886 100644
--- a/src/com/android/launcher3/pm/UserCache.java
+++ b/src/com/android/launcher3/pm/UserCache.java
@@ -16,16 +16,21 @@
package com.android.launcher3.pm;
+import static com.android.launcher3.Utilities.ATLEAST_U;
import static com.android.launcher3.testing.shared.TestProtocol.WORK_TAB_MISSING;
-import static com.android.launcher3.testing.shared.TestProtocol.sDebugTracing;
import static com.android.launcher3.testing.shared.TestProtocol.testLogD;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import android.content.Context;
import android.content.Intent;
+import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.ArrayMap;
-import android.util.LongSparseArray;
+
+import androidx.annotation.AnyThread;
+import androidx.annotation.NonNull;
+import androidx.annotation.WorkerThread;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.SafeCloseable;
@@ -34,136 +39,123 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
+import java.util.function.BiConsumer;
/**
* Class which manages a local cache of user handles to avoid system rpc
*/
-public class UserCache {
+public class UserCache implements SafeCloseable {
+
+ public static final String ACTION_PROFILE_ADDED = ATLEAST_U
+ ? Intent.ACTION_PROFILE_ADDED : Intent.ACTION_MANAGED_PROFILE_ADDED;
+ public static final String ACTION_PROFILE_REMOVED = ATLEAST_U
+ ? Intent.ACTION_PROFILE_REMOVED : Intent.ACTION_MANAGED_PROFILE_REMOVED;
+
+ public static final String ACTION_PROFILE_UNLOCKED = ATLEAST_U
+ ? Intent.ACTION_PROFILE_ACCESSIBLE : Intent.ACTION_MANAGED_PROFILE_UNLOCKED;
+ public static final String ACTION_PROFILE_LOCKED = ATLEAST_U
+ ? Intent.ACTION_PROFILE_INACCESSIBLE : Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE;
public static final MainThreadInitializedObject<UserCache> INSTANCE =
new MainThreadInitializedObject<>(UserCache::new);
- private final Context mContext;
- private final UserManager mUserManager;
- private final ArrayList<Runnable> mUserChangeListeners = new ArrayList<>();
+ private final List<BiConsumer<UserHandle, String>> mUserEventListeners = new ArrayList<>();
private final SimpleBroadcastReceiver mUserChangeReceiver =
new SimpleBroadcastReceiver(this::onUsersChanged);
- private LongSparseArray<UserHandle> mUsers;
- // Create a separate reverse map as LongSparseArray.indexOfValue checks if objects are same
- // and not {@link Object#equals}
- private ArrayMap<UserHandle, Long> mUserToSerialMap;
+ private final Context mContext;
+
+ @NonNull
+ private Map<UserHandle, Long> mUserToSerialMap;
private UserCache(Context context) {
mContext = context;
- mUserManager = context.getSystemService(UserManager.class);
+ mUserToSerialMap = Collections.emptyMap();
+ MODEL_EXECUTOR.execute(this::initAsync);
}
+ @Override
+ public void close() {
+ MODEL_EXECUTOR.execute(() -> mUserChangeReceiver.unregisterReceiverSafely(mContext));
+ }
+
+ @WorkerThread
+ private void initAsync() {
+ mUserChangeReceiver.register(mContext,
+ Intent.ACTION_MANAGED_PROFILE_AVAILABLE,
+ Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE,
+ ACTION_PROFILE_ADDED,
+ ACTION_PROFILE_REMOVED,
+ ACTION_PROFILE_UNLOCKED,
+ ACTION_PROFILE_LOCKED);
+ updateCache();
+ }
+
+ @AnyThread
private void onUsersChanged(Intent intent) {
testLogD(WORK_TAB_MISSING, "onUsersChanged intent: " + intent);
- enableAndResetCache();
- mUserChangeListeners.forEach(Runnable::run);
+
+ MODEL_EXECUTOR.execute(this::updateCache);
+ UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER);
+ if (user == null) {
+ return;
+ }
+ String action = intent.getAction();
+ mUserEventListeners.forEach(l -> l.accept(user, action));
+ }
+
+ @WorkerThread
+ private void updateCache() {
+ mUserToSerialMap = queryAllUsers(mContext.getSystemService(UserManager.class));
}
/**
* Adds a listener for user additions and removals
*/
- public SafeCloseable addUserChangeListener(Runnable command) {
- synchronized (this) {
- if (mUserChangeListeners.isEmpty()) {
- // Enable caching and start listening for user broadcast
- mUserChangeReceiver.register(mContext,
- Intent.ACTION_MANAGED_PROFILE_ADDED,
- Intent.ACTION_MANAGED_PROFILE_REMOVED);
- enableAndResetCache();
- }
- mUserChangeListeners.add(command);
- return () -> removeUserChangeListener(command);
- }
- }
-
- private void enableAndResetCache() {
- synchronized (this) {
- mUsers = new LongSparseArray<>();
- mUserToSerialMap = new ArrayMap<>();
- List<UserHandle> users = mUserManager.getUserProfiles();
- if (users != null) {
- for (UserHandle user : users) {
- testLogD(WORK_TAB_MISSING, "caching user: " + user);
- long serial = mUserManager.getSerialNumberForUser(user);
- mUsers.put(serial, user);
- mUserToSerialMap.put(user, serial);
- }
- }
- }
- }
-
- private void removeUserChangeListener(Runnable command) {
- synchronized (this) {
- mUserChangeListeners.remove(command);
- if (mUserChangeListeners.isEmpty()) {
- // Disable cache and stop listening
- mContext.unregisterReceiver(mUserChangeReceiver);
-
- mUsers = null;
- mUserToSerialMap = null;
- }
- }
+ public SafeCloseable addUserEventListener(BiConsumer<UserHandle, String> listener) {
+ mUserEventListeners.add(listener);
+ return () -> mUserEventListeners.remove(listener);
}
/**
* @see UserManager#getSerialNumberForUser(UserHandle)
*/
public long getSerialNumberForUser(UserHandle user) {
- synchronized (this) {
- if (mUserToSerialMap != null) {
- Long serial = mUserToSerialMap.get(user);
- return serial == null ? 0 : serial;
- }
- }
- return mUserManager.getSerialNumberForUser(user);
+ Long serial = mUserToSerialMap.get(user);
+ return serial == null ? 0 : serial;
}
/**
* @see UserManager#getUserForSerialNumber(long)
*/
public UserHandle getUserForSerialNumber(long serialNumber) {
- synchronized (this) {
- if (mUsers != null) {
- return mUsers.get(serialNumber);
- }
- }
- return mUserManager.getUserForSerialNumber(serialNumber);
+ Long value = serialNumber;
+ return mUserToSerialMap
+ .entrySet()
+ .stream()
+ .filter(entry -> value.equals(entry.getValue()))
+ .findFirst()
+ .map(Map.Entry::getKey)
+ .orElse(Process.myUserHandle());
}
/**
* @see UserManager#getUserProfiles()
*/
public List<UserHandle> getUserProfiles() {
- StringBuilder usersToReturn = new StringBuilder();
- List<UserHandle> users;
- if (sDebugTracing) {
- users = mUserManager.getUserProfiles();
- for (UserHandle u : users) {
- usersToReturn.append(u).append(" && ");
- }
- testLogD(WORK_TAB_MISSING, "users from userManager: " + usersToReturn);
- }
+ return List.copyOf(mUserToSerialMap.keySet());
+ }
- synchronized (this) {
- if (mUsers != null) {
- usersToReturn = new StringBuilder();
- for (UserHandle u : mUserToSerialMap.keySet()) {
- usersToReturn.append(u).append(" && ");
- }
- testLogD(WORK_TAB_MISSING, "users from cache: " + usersToReturn);
- return new ArrayList<>(mUserToSerialMap.keySet());
- } else {
- testLogD(WORK_TAB_MISSING, "users from cache null");
+ private static Map<UserHandle, Long> queryAllUsers(UserManager userManager) {
+ Map<UserHandle, Long> users = new ArrayMap<>();
+ List<UserHandle> usersActual = userManager.getUserProfiles();
+ if (usersActual != null) {
+ for (UserHandle user : usersActual) {
+ long serial = userManager.getSerialNumberForUser(user);
+ users.put(user, serial);
}
}
-
- users = mUserManager.getUserProfiles();
- return users == null ? Collections.emptyList() : users;
+ return users;
}
}
diff --git a/src/com/android/launcher3/popup/ArrowPopup.java b/src/com/android/launcher3/popup/ArrowPopup.java
index 72f99cb..08be026 100644
--- a/src/com/android/launcher3/popup/ArrowPopup.java
+++ b/src/com/android/launcher3/popup/ArrowPopup.java
@@ -18,11 +18,11 @@
import static androidx.core.content.ContextCompat.getColorStateList;
-import static com.android.launcher3.anim.Interpolators.ACCELERATED_EASE;
-import static com.android.launcher3.anim.Interpolators.DECELERATED_EASE;
-import static com.android.launcher3.anim.Interpolators.EMPHASIZED_ACCELERATE;
-import static com.android.launcher3.anim.Interpolators.EMPHASIZED_DECELERATE;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.app.animation.Interpolators.ACCELERATED_EASE;
+import static com.android.app.animation.Interpolators.DECELERATED_EASE;
+import static com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE;
+import static com.android.app.animation.Interpolators.EMPHASIZED_DECELERATE;
+import static com.android.app.animation.Interpolators.LINEAR;
import static com.android.launcher3.config.FeatureFlags.ENABLE_MATERIAL_U_POPUP;
import android.animation.Animator;
diff --git a/src/com/android/launcher3/provider/LauncherDbUtils.java b/src/com/android/launcher3/provider/LauncherDbUtils.java
index c718dcc..1f908eb 100644
--- a/src/com/android/launcher3/provider/LauncherDbUtils.java
+++ b/src/com/android/launcher3/provider/LauncherDbUtils.java
@@ -28,7 +28,6 @@
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Icon;
-import android.os.Binder;
import android.os.PersistableBundle;
import android.os.Process;
import android.os.UserManager;
@@ -50,6 +49,13 @@
*/
public class LauncherDbUtils {
+ /**
+ * Returns a string which can be used as a where clause for DB query to match the given itemId
+ */
+ public static String itemIdMatch(int itemId) {
+ return "_id=" + itemId;
+ }
+
public static IntArray queryIntArray(boolean distinct, SQLiteDatabase db, String tableName,
String columnName, String selection, String groupBy, String orderBy) {
IntArray out = new IntArray();
@@ -166,7 +172,7 @@
/**
* Utility class to simplify managing sqlite transactions
*/
- public static class SQLiteTransaction extends Binder implements AutoCloseable {
+ public static class SQLiteTransaction implements AutoCloseable {
private final SQLiteDatabase mDb;
public SQLiteTransaction(SQLiteDatabase db) {
diff --git a/src/com/android/launcher3/responsive/AllAppsSpecs.kt b/src/com/android/launcher3/responsive/AllAppsSpecs.kt
new file mode 100644
index 0000000..85e383e
--- /dev/null
+++ b/src/com/android/launcher3/responsive/AllAppsSpecs.kt
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.responsive
+
+import android.content.res.XmlResourceParser
+import android.util.AttributeSet
+import android.util.Log
+import android.util.Xml
+import com.android.launcher3.R
+import com.android.launcher3.util.ResourceHelper
+import com.android.launcher3.workspace.CalculatedWorkspaceSpec
+import java.io.IOException
+import kotlin.math.roundToInt
+import org.xmlpull.v1.XmlPullParser
+import org.xmlpull.v1.XmlPullParserException
+
+private const val LOG_TAG = "AllAppsSpecs"
+
+class AllAppsSpecs(resourceHelper: ResourceHelper) {
+ object XmlTags {
+ const val ALL_APPS_SPECS = "allAppsSpecs"
+
+ const val ALL_APPS_SPEC = "allAppsSpec"
+ const val START_PADDING = "startPadding"
+ const val END_PADDING = "endPadding"
+ const val GUTTER = "gutter"
+ const val CELL_SIZE = "cellSize"
+ }
+
+ val allAppsHeightSpecList = mutableListOf<AllAppsSpec>()
+ val allAppsWidthSpecList = mutableListOf<AllAppsSpec>()
+
+ // TODO(b/286538013) Remove this init after a more generic or reusable parser is created
+ init {
+ var parser: XmlResourceParser? = null
+ try {
+ parser = resourceHelper.getXml()
+ val depth = parser.depth
+ var type: Int
+ while (
+ (parser.next().also { type = it } != XmlPullParser.END_TAG ||
+ parser.depth > depth) && type != XmlPullParser.END_DOCUMENT
+ ) {
+ if (type == XmlPullParser.START_TAG && XmlTags.ALL_APPS_SPECS == parser.name) {
+ val displayDepth = parser.depth
+ while (
+ (parser.next().also { type = it } != XmlPullParser.END_TAG ||
+ parser.depth > displayDepth) && type != XmlPullParser.END_DOCUMENT
+ ) {
+ if (
+ type == XmlPullParser.START_TAG && XmlTags.ALL_APPS_SPEC == parser.name
+ ) {
+ val attrs =
+ resourceHelper.obtainStyledAttributes(
+ Xml.asAttributeSet(parser),
+ R.styleable.AllAppsSpec
+ )
+ val maxAvailableSize =
+ attrs.getDimensionPixelSize(
+ R.styleable.AllAppsSpec_maxAvailableSize,
+ 0
+ )
+ val specType =
+ AllAppsSpec.SpecType.values()[
+ attrs.getInt(
+ R.styleable.AllAppsSpec_specType,
+ AllAppsSpec.SpecType.HEIGHT.ordinal
+ )]
+ attrs.recycle()
+
+ var startPadding: SizeSpec? = null
+ var endPadding: SizeSpec? = null
+ var gutter: SizeSpec? = null
+ var cellSize: SizeSpec? = null
+
+ val limitDepth = parser.depth
+ while (
+ (parser.next().also { type = it } != XmlPullParser.END_TAG ||
+ parser.depth > limitDepth) && type != XmlPullParser.END_DOCUMENT
+ ) {
+ val attr: AttributeSet = Xml.asAttributeSet(parser)
+ if (type == XmlPullParser.START_TAG) {
+ when (parser.name) {
+ XmlTags.START_PADDING -> {
+ startPadding = SizeSpec.create(resourceHelper, attr)
+ }
+ XmlTags.END_PADDING -> {
+ endPadding = SizeSpec.create(resourceHelper, attr)
+ }
+ XmlTags.GUTTER -> {
+ gutter = SizeSpec.create(resourceHelper, attr)
+ }
+ XmlTags.CELL_SIZE -> {
+ cellSize = SizeSpec.create(resourceHelper, attr)
+ }
+ }
+ }
+ }
+
+ if (
+ startPadding == null ||
+ endPadding == null ||
+ gutter == null ||
+ cellSize == null
+ ) {
+ throw IllegalStateException(
+ "All attributes in AllAppsSpec must be defined"
+ )
+ }
+
+ val allAppsSpec =
+ AllAppsSpec(
+ maxAvailableSize,
+ specType,
+ startPadding,
+ endPadding,
+ gutter,
+ cellSize
+ )
+ if (allAppsSpec.isValid()) {
+ if (allAppsSpec.specType == AllAppsSpec.SpecType.HEIGHT)
+ allAppsHeightSpecList.add(allAppsSpec)
+ else allAppsWidthSpecList.add(allAppsSpec)
+ } else {
+ throw IllegalStateException("Invalid AllAppsSpec found.")
+ }
+ }
+ }
+
+ if (allAppsWidthSpecList.isEmpty() || allAppsHeightSpecList.isEmpty()) {
+ throw IllegalStateException(
+ "AllAppsSpecs is incomplete - " +
+ "height list size = ${allAppsHeightSpecList.size}; " +
+ "width list size = ${allAppsWidthSpecList.size}."
+ )
+ }
+ }
+ }
+ } catch (e: Exception) {
+ when (e) {
+ is IOException,
+ is XmlPullParserException -> {
+ throw RuntimeException("Failure parsing all apps specs file.", e)
+ }
+ else -> throw e
+ }
+ } finally {
+ parser?.close()
+ }
+ }
+
+ /**
+ * Returns the CalculatedAllAppsSpec for width, based on the available width, the AllAppsSpecs
+ * and the CalculatedWorkspaceSpec.
+ */
+ fun getCalculatedWidthSpec(
+ columns: Int,
+ availableWidth: Int,
+ calculatedWorkspaceSpec: CalculatedWorkspaceSpec
+ ): CalculatedAllAppsSpec {
+ val widthSpec = allAppsWidthSpecList.first { availableWidth <= it.maxAvailableSize }
+
+ return CalculatedAllAppsSpec(availableWidth, columns, widthSpec, calculatedWorkspaceSpec)
+ }
+
+ /**
+ * Returns the CalculatedAllAppsSpec for height, based on the available height, the AllAppsSpecs
+ * and the CalculatedWorkspaceSpec.
+ */
+ fun getCalculatedHeightSpec(
+ rows: Int,
+ availableHeight: Int,
+ calculatedWorkspaceSpec: CalculatedWorkspaceSpec
+ ): CalculatedAllAppsSpec {
+ val heightSpec = allAppsHeightSpecList.first { availableHeight <= it.maxAvailableSize }
+
+ return CalculatedAllAppsSpec(availableHeight, rows, heightSpec, calculatedWorkspaceSpec)
+ }
+}
+
+class CalculatedAllAppsSpec(
+ val availableSpace: Int,
+ val cells: Int,
+ private val allAppsSpec: AllAppsSpec,
+ calculatedWorkspaceSpec: CalculatedWorkspaceSpec
+) {
+ var startPaddingPx: Int = 0
+ private set
+ var endPaddingPx: Int = 0
+ private set
+ var gutterPx: Int = 0
+ private set
+ var cellSizePx: Int = 0
+ private set
+ init {
+ // Copy values from workspace
+ if (allAppsSpec.startPadding.matchWorkspace)
+ startPaddingPx = calculatedWorkspaceSpec.startPaddingPx
+ if (allAppsSpec.endPadding.matchWorkspace)
+ endPaddingPx = calculatedWorkspaceSpec.endPaddingPx
+ if (allAppsSpec.gutter.matchWorkspace) gutterPx = calculatedWorkspaceSpec.gutterPx
+ if (allAppsSpec.cellSize.matchWorkspace) cellSizePx = calculatedWorkspaceSpec.cellSizePx
+
+ // Calculate all fixed size first
+ if (allAppsSpec.startPadding.fixedSize > 0)
+ startPaddingPx = allAppsSpec.startPadding.fixedSize.roundToInt()
+ if (allAppsSpec.endPadding.fixedSize > 0)
+ endPaddingPx = allAppsSpec.endPadding.fixedSize.roundToInt()
+ if (allAppsSpec.gutter.fixedSize > 0) gutterPx = allAppsSpec.gutter.fixedSize.roundToInt()
+ if (allAppsSpec.cellSize.fixedSize > 0)
+ cellSizePx = allAppsSpec.cellSize.fixedSize.roundToInt()
+
+ // Calculate all available space next
+ if (allAppsSpec.startPadding.ofAvailableSpace > 0)
+ startPaddingPx =
+ (allAppsSpec.startPadding.ofAvailableSpace * availableSpace).roundToInt()
+ if (allAppsSpec.endPadding.ofAvailableSpace > 0)
+ endPaddingPx = (allAppsSpec.endPadding.ofAvailableSpace * availableSpace).roundToInt()
+ if (allAppsSpec.gutter.ofAvailableSpace > 0)
+ gutterPx = (allAppsSpec.gutter.ofAvailableSpace * availableSpace).roundToInt()
+ if (allAppsSpec.cellSize.ofAvailableSpace > 0)
+ cellSizePx = (allAppsSpec.cellSize.ofAvailableSpace * availableSpace).roundToInt()
+
+ // Calculate remainder space last
+ val gutters = cells - 1
+ val usedSpace = startPaddingPx + endPaddingPx + (gutterPx * gutters) + (cellSizePx * cells)
+ val remainderSpace = availableSpace - usedSpace
+ if (allAppsSpec.startPadding.ofRemainderSpace > 0)
+ startPaddingPx =
+ (allAppsSpec.startPadding.ofRemainderSpace * remainderSpace).roundToInt()
+ if (allAppsSpec.endPadding.ofRemainderSpace > 0)
+ endPaddingPx = (allAppsSpec.endPadding.ofRemainderSpace * remainderSpace).roundToInt()
+ if (allAppsSpec.gutter.ofRemainderSpace > 0)
+ gutterPx = (allAppsSpec.gutter.ofRemainderSpace * remainderSpace).roundToInt()
+ if (allAppsSpec.cellSize.ofRemainderSpace > 0)
+ cellSizePx = (allAppsSpec.cellSize.ofRemainderSpace * remainderSpace).roundToInt()
+ }
+
+ override fun toString(): String {
+ return "CalculatedAllAppsSpec(availableSpace=$availableSpace, " +
+ "cells=$cells, startPaddingPx=$startPaddingPx, endPaddingPx=$endPaddingPx, " +
+ "gutterPx=$gutterPx, cellSizePx=$cellSizePx, " +
+ "AllAppsSpec.maxAvailableSize=${allAppsSpec.maxAvailableSize})"
+ }
+}
+
+data class AllAppsSpec(
+ val maxAvailableSize: Int,
+ val specType: SpecType,
+ val startPadding: SizeSpec,
+ val endPadding: SizeSpec,
+ val gutter: SizeSpec,
+ val cellSize: SizeSpec
+) {
+
+ enum class SpecType {
+ HEIGHT,
+ WIDTH
+ }
+
+ fun isValid(): Boolean {
+ if (maxAvailableSize <= 0) {
+ Log.e(LOG_TAG, "AllAppsSpec#isValid - maxAvailableSize <= 0")
+ return false
+ }
+
+ // All specs need to be individually valid
+ if (!allSpecsAreValid()) {
+ Log.e(LOG_TAG, "AllAppsSpec#isValid - !allSpecsAreValid()")
+ return false
+ }
+
+ return true
+ }
+
+ private fun allSpecsAreValid(): Boolean =
+ startPadding.isValid() && endPadding.isValid() && gutter.isValid() && cellSize.isValid()
+}
diff --git a/src/com/android/launcher3/responsive/FolderSpecs.kt b/src/com/android/launcher3/responsive/FolderSpecs.kt
new file mode 100644
index 0000000..f4446bc
--- /dev/null
+++ b/src/com/android/launcher3/responsive/FolderSpecs.kt
@@ -0,0 +1,280 @@
+package com.android.launcher3.responsive
+
+import android.content.res.XmlResourceParser
+import android.util.AttributeSet
+import android.util.Log
+import android.util.Xml
+import com.android.launcher3.R
+import com.android.launcher3.responsive.FolderSpec.*
+import com.android.launcher3.util.ResourceHelper
+import com.android.launcher3.workspace.CalculatedWorkspaceSpec
+import com.android.launcher3.workspace.WorkspaceSpec
+import java.io.IOException
+import org.xmlpull.v1.XmlPullParser
+import org.xmlpull.v1.XmlPullParserException
+
+private const val LOG_TAG = "FolderSpecs"
+
+class FolderSpecs(resourceHelper: ResourceHelper) {
+
+ object XmlTags {
+ const val FOLDER_SPECS = "folderSpecs"
+
+ const val FOLDER_SPEC = "folderSpec"
+ const val START_PADDING = "startPadding"
+ const val END_PADDING = "endPadding"
+ const val GUTTER = "gutter"
+ const val CELL_SIZE = "cellSize"
+ }
+
+ private val _heightSpecs = mutableListOf<FolderSpec>()
+ val heightSpecs: List<FolderSpec>
+ get() = _heightSpecs
+
+ private val _widthSpecs = mutableListOf<FolderSpec>()
+ val widthSpecs: List<FolderSpec>
+ get() = _widthSpecs
+
+ // TODO(b/286538013) Remove this init after a more generic or reusable parser is created
+ init {
+ var parser: XmlResourceParser? = null
+ try {
+ parser = resourceHelper.getXml()
+ val depth = parser.depth
+ var type: Int
+ while (
+ (parser.next().also { type = it } != XmlPullParser.END_TAG ||
+ parser.depth > depth) && type != XmlPullParser.END_DOCUMENT
+ ) {
+ if (type == XmlPullParser.START_TAG && XmlTags.FOLDER_SPECS == parser.name) {
+ val displayDepth = parser.depth
+ while (
+ (parser.next().also { type = it } != XmlPullParser.END_TAG ||
+ parser.depth > displayDepth) && type != XmlPullParser.END_DOCUMENT
+ ) {
+ if (type == XmlPullParser.START_TAG && XmlTags.FOLDER_SPEC == parser.name) {
+ val attrs =
+ resourceHelper.obtainStyledAttributes(
+ Xml.asAttributeSet(parser),
+ R.styleable.FolderSpec
+ )
+ val maxAvailableSize =
+ attrs.getDimensionPixelSize(
+ R.styleable.FolderSpec_maxAvailableSize,
+ 0
+ )
+ val specType =
+ SpecType.values()[
+ attrs.getInt(
+ R.styleable.FolderSpec_specType,
+ SpecType.HEIGHT.ordinal
+ )]
+ attrs.recycle()
+
+ var startPadding: SizeSpec? = null
+ var endPadding: SizeSpec? = null
+ var gutter: SizeSpec? = null
+ var cellSize: SizeSpec? = null
+
+ val limitDepth = parser.depth
+ while (
+ (parser.next().also { type = it } != XmlPullParser.END_TAG ||
+ parser.depth > limitDepth) && type != XmlPullParser.END_DOCUMENT
+ ) {
+ val attr: AttributeSet = Xml.asAttributeSet(parser)
+ if (type == XmlPullParser.START_TAG) {
+ val sizeSpec = SizeSpec.create(resourceHelper, attr)
+ when (parser.name) {
+ XmlTags.START_PADDING -> startPadding = sizeSpec
+ XmlTags.END_PADDING -> endPadding = sizeSpec
+ XmlTags.GUTTER -> gutter = sizeSpec
+ XmlTags.CELL_SIZE -> cellSize = sizeSpec
+ }
+ }
+ }
+
+ checkNotNull(startPadding) {
+ "Attr 'startPadding' in FolderSpec must be defined."
+ }
+ checkNotNull(endPadding) {
+ "Attr 'endPadding' in FolderSpec must be defined."
+ }
+ checkNotNull(gutter) { "Attr 'gutter' in FolderSpec must be defined." }
+ checkNotNull(cellSize) {
+ "Attr 'cellSize' in FolderSpec must be defined."
+ }
+
+ val folderSpec =
+ FolderSpec(
+ maxAvailableSize,
+ specType,
+ startPadding,
+ endPadding,
+ gutter,
+ cellSize
+ )
+
+ check(folderSpec.isValid()) { "Invalid FolderSpec found." }
+
+ if (folderSpec.specType == SpecType.HEIGHT) {
+ _heightSpecs += folderSpec
+ } else {
+ _widthSpecs += folderSpec
+ }
+ }
+ }
+
+ check(_widthSpecs.isNotEmpty() && _heightSpecs.isNotEmpty()) {
+ "FolderSpecs is incomplete - " +
+ "height list size = ${_heightSpecs.size}; " +
+ "width list size = ${_widthSpecs.size}."
+ }
+ }
+ }
+ } catch (e: Exception) {
+ when (e) {
+ is IOException,
+ is XmlPullParserException -> {
+ throw RuntimeException("Failure parsing folder specs file.", e)
+ }
+ else -> throw e
+ }
+ } finally {
+ parser?.close()
+ }
+ }
+
+ /**
+ * Returns the [CalculatedFolderSpec] for width, based on the available width, FolderSpecs and
+ * WorkspaceSpecs.
+ */
+ fun getWidthSpec(
+ columns: Int,
+ availableWidth: Int,
+ workspaceSpec: CalculatedWorkspaceSpec
+ ): CalculatedFolderSpec {
+ check(workspaceSpec.workspaceSpec.specType == WorkspaceSpec.SpecType.WIDTH) {
+ "Invalid specType for CalculatedWorkspaceSpec. " +
+ "Expected: ${WorkspaceSpec.SpecType.WIDTH} - " +
+ "Found: ${workspaceSpec.workspaceSpec.specType}}"
+ }
+
+ val widthSpec = _widthSpecs.firstOrNull { availableWidth <= it.maxAvailableSize }
+ check(widthSpec != null) { "No FolderSpec for width spec found with $availableWidth." }
+
+ return convertToCalculatedFolderSpec(widthSpec, availableWidth, columns, workspaceSpec)
+ }
+
+ /**
+ * Returns the [CalculatedFolderSpec] for height, based on the available height, FolderSpecs and
+ * WorkspaceSpecs.
+ */
+ fun getHeightSpec(
+ rows: Int,
+ availableHeight: Int,
+ workspaceSpec: CalculatedWorkspaceSpec
+ ): CalculatedFolderSpec {
+ check(workspaceSpec.workspaceSpec.specType == WorkspaceSpec.SpecType.HEIGHT) {
+ "Invalid specType for CalculatedWorkspaceSpec. " +
+ "Expected: ${WorkspaceSpec.SpecType.HEIGHT} - " +
+ "Found: ${workspaceSpec.workspaceSpec.specType}}"
+ }
+
+ val heightSpec = _heightSpecs.firstOrNull { availableHeight <= it.maxAvailableSize }
+ check(heightSpec != null) { "No FolderSpec for height spec found with $availableHeight." }
+
+ return convertToCalculatedFolderSpec(heightSpec, availableHeight, rows, workspaceSpec)
+ }
+}
+
+data class CalculatedFolderSpec(
+ val availableSpace: Int,
+ val cells: Int,
+ val startPaddingPx: Int,
+ val endPaddingPx: Int,
+ val gutterPx: Int,
+ val cellSizePx: Int
+)
+
+/**
+ * Responsive folder specs to be used to calculate the paddings, gutter and cell size for folders in
+ * the workspace.
+ *
+ * @param maxAvailableSize indicates the breakpoint to use this specification.
+ * @param specType indicates whether the paddings and gutters will be applied vertically or
+ * horizontally.
+ * @param startPadding padding used at the top or left (right in RTL) in the workspace folder.
+ * @param endPadding padding used at the bottom or right (left in RTL) in the workspace folder.
+ * @param gutter the space between the cells vertically or horizontally depending on the [specType].
+ * @param cellSize height or width of the cell depending on the [specType].
+ */
+data class FolderSpec(
+ val maxAvailableSize: Int,
+ val specType: SpecType,
+ val startPadding: SizeSpec,
+ val endPadding: SizeSpec,
+ val gutter: SizeSpec,
+ val cellSize: SizeSpec
+) {
+
+ enum class SpecType {
+ HEIGHT,
+ WIDTH
+ }
+
+ fun isValid(): Boolean {
+ if (maxAvailableSize <= 0) {
+ Log.e(LOG_TAG, "FolderSpec#isValid - maxAvailableSize <= 0")
+ return false
+ }
+
+ // All specs are valid
+ if (
+ !(startPadding.isValid() &&
+ endPadding.isValid() &&
+ gutter.isValid() &&
+ cellSize.isValid())
+ ) {
+ Log.e(LOG_TAG, "FolderSpec#isValid - !allSpecsAreValid()")
+ return false
+ }
+
+ return true
+ }
+}
+
+/** Helper function to convert [FolderSpec] to [CalculatedFolderSpec] */
+private fun convertToCalculatedFolderSpec(
+ folderSpec: FolderSpec,
+ availableSpace: Int,
+ cells: Int,
+ workspaceSpec: CalculatedWorkspaceSpec
+): CalculatedFolderSpec {
+ // Map if is fixedSize, ofAvailableSpace or matchWorkspace
+ var startPaddingPx =
+ folderSpec.startPadding.getCalculatedValue(availableSpace, workspaceSpec.startPaddingPx)
+ var endPaddingPx =
+ folderSpec.endPadding.getCalculatedValue(availableSpace, workspaceSpec.endPaddingPx)
+ var gutterPx = folderSpec.gutter.getCalculatedValue(availableSpace, workspaceSpec.gutterPx)
+ var cellSizePx =
+ folderSpec.cellSize.getCalculatedValue(availableSpace, workspaceSpec.cellSizePx)
+
+ // Remainder space
+ val gutters = cells - 1
+ val usedSpace = startPaddingPx + endPaddingPx + (gutterPx * gutters) + (cellSizePx * cells)
+ val remainderSpace = availableSpace - usedSpace
+
+ startPaddingPx = folderSpec.startPadding.getRemainderSpaceValue(remainderSpace, startPaddingPx)
+ endPaddingPx = folderSpec.endPadding.getRemainderSpaceValue(remainderSpace, endPaddingPx)
+ gutterPx = folderSpec.gutter.getRemainderSpaceValue(remainderSpace, gutterPx)
+ cellSizePx = folderSpec.cellSize.getRemainderSpaceValue(remainderSpace, cellSizePx)
+
+ return CalculatedFolderSpec(
+ availableSpace = availableSpace,
+ cells = cells,
+ startPaddingPx = startPaddingPx,
+ endPaddingPx = endPaddingPx,
+ gutterPx = gutterPx,
+ cellSizePx = cellSizePx
+ )
+}
diff --git a/src/com/android/launcher3/responsive/SizeSpec.kt b/src/com/android/launcher3/responsive/SizeSpec.kt
new file mode 100644
index 0000000..3d618f9
--- /dev/null
+++ b/src/com/android/launcher3/responsive/SizeSpec.kt
@@ -0,0 +1,120 @@
+package com.android.launcher3.responsive
+
+import android.content.res.TypedArray
+import android.util.AttributeSet
+import android.util.Log
+import android.util.TypedValue
+import com.android.launcher3.R
+import com.android.launcher3.util.ResourceHelper
+import kotlin.math.roundToInt
+
+/**
+ * [SizeSpec] is an attribute used to represent a property in the responsive grid specs.
+ *
+ * @param fixedSize a fixed size in dp to be used
+ * @param ofAvailableSpace a percentage of the available space
+ * @param ofRemainderSpace a percentage of the remaining space (available space minus used space)
+ * @param matchWorkspace indicates whether the workspace value will be used or not.
+ * @param maxSize restricts the maximum value allowed for the [SizeSpec].
+ */
+data class SizeSpec(
+ val fixedSize: Float = 0f,
+ val ofAvailableSpace: Float = 0f,
+ val ofRemainderSpace: Float = 0f,
+ val matchWorkspace: Boolean = false,
+ val maxSize: Int = Int.MAX_VALUE
+) {
+
+ /** Retrieves the correct value for [SizeSpec]. */
+ fun getCalculatedValue(availableSpace: Int, workspaceValue: Int): Int {
+ val calculatedValue =
+ when {
+ fixedSize > 0 -> fixedSize.roundToInt()
+ matchWorkspace -> workspaceValue
+ ofAvailableSpace > 0 -> (ofAvailableSpace * availableSpace).roundToInt()
+ else -> 0
+ }
+
+ return calculatedValue.coerceAtMost(maxSize)
+ }
+
+ /**
+ * Calculates the [SizeSpec] value when remainder space value is defined. If no remainderSpace
+ * is 0, returns a default value.
+ */
+ fun getRemainderSpaceValue(remainderSpace: Int, defaultValue: Int): Int {
+ val remainderSpaceValue =
+ if (ofRemainderSpace > 0) {
+ (ofRemainderSpace * remainderSpace).roundToInt()
+ } else {
+ defaultValue
+ }
+
+ return remainderSpaceValue.coerceAtMost(maxSize)
+ }
+
+ fun isValid(): Boolean {
+ // All attributes are empty
+ if (fixedSize < 0f && ofAvailableSpace <= 0f && ofRemainderSpace <= 0f && !matchWorkspace) {
+ Log.e(TAG, "SizeSpec#isValid - all attributes are empty")
+ return false
+ }
+
+ // More than one attribute is filled
+ val attrCount =
+ (if (fixedSize > 0) 1 else 0) +
+ (if (ofAvailableSpace > 0) 1 else 0) +
+ (if (ofRemainderSpace > 0) 1 else 0) +
+ (if (matchWorkspace) 1 else 0)
+ if (attrCount > 1) {
+ Log.e(TAG, "SizeSpec#isValid - more than one attribute is filled")
+ return false
+ }
+
+ // Values should be between 0 and 1
+ if (ofAvailableSpace !in 0f..1f || ofRemainderSpace !in 0f..1f) {
+ Log.e(TAG, "SizeSpec#isValid - values should be between 0 and 1")
+ return false
+ }
+
+ // Invalid fixed or max size
+ if (fixedSize < 0f || maxSize < 0f) {
+ Log.e(TAG, "SizeSpec#isValid - values should be bigger or equal to zero.")
+ return false
+ }
+
+ if (fixedSize > 0f && fixedSize > maxSize) {
+ Log.e(TAG, "SizeSpec#isValid - fixed size should be smaller than the max size.")
+ return false
+ }
+
+ return true
+ }
+
+ companion object {
+ private const val TAG = "SizeSpec"
+
+ private fun getValue(a: TypedArray, index: Int): Float {
+ return when (a.getType(index)) {
+ TypedValue.TYPE_DIMENSION -> a.getDimensionPixelSize(index, 0).toFloat()
+ TypedValue.TYPE_FLOAT -> a.getFloat(index, 0f)
+ else -> 0f
+ }
+ }
+
+ fun create(resourceHelper: ResourceHelper, attrs: AttributeSet): SizeSpec {
+ val styledAttrs = resourceHelper.obtainStyledAttributes(attrs, R.styleable.SizeSpec)
+
+ val fixedSize = getValue(styledAttrs, R.styleable.SizeSpec_fixedSize)
+ val ofAvailableSpace = getValue(styledAttrs, R.styleable.SizeSpec_ofAvailableSpace)
+ val ofRemainderSpace = getValue(styledAttrs, R.styleable.SizeSpec_ofRemainderSpace)
+ val matchWorkspace = styledAttrs.getBoolean(R.styleable.SizeSpec_matchWorkspace, false)
+ val maxSize =
+ styledAttrs.getDimensionPixelSize(R.styleable.SizeSpec_maxSize, Int.MAX_VALUE)
+
+ styledAttrs.recycle()
+
+ return SizeSpec(fixedSize, ofAvailableSpace, ofRemainderSpace, matchWorkspace, maxSize)
+ }
+ }
+}
diff --git a/src/com/android/launcher3/statemanager/StateManager.java b/src/com/android/launcher3/statemanager/StateManager.java
index 198dad3..b1586dc 100644
--- a/src/com/android/launcher3/statemanager/StateManager.java
+++ b/src/com/android/launcher3/statemanager/StateManager.java
@@ -76,6 +76,10 @@
return mState;
}
+ public STATE_TYPE getTargetState() {
+ return (STATE_TYPE) mConfig.targetState;
+ }
+
public STATE_TYPE getCurrentStableState() {
return mCurrentStableState;
}
diff --git a/src/com/android/launcher3/statemanager/StatefulActivity.java b/src/com/android/launcher3/statemanager/StatefulActivity.java
index 520f33c..de5887f 100644
--- a/src/com/android/launcher3/statemanager/StatefulActivity.java
+++ b/src/com/android/launcher3/statemanager/StatefulActivity.java
@@ -237,4 +237,10 @@
* @param leftOrTop if the staged split will be positioned left or top.
*/
public void enterStageSplitFromRunningApp(boolean leftOrTop) { }
+
+
+ /** Returns whether the overview command helper queue is empty. */
+ public boolean isCommandQueueEmpty() {
+ return true;
+ }
}
diff --git a/src/com/android/launcher3/states/StateAnimationConfig.java b/src/com/android/launcher3/states/StateAnimationConfig.java
index d1e816b..0d9e010 100644
--- a/src/com/android/launcher3/states/StateAnimationConfig.java
+++ b/src/com/android/launcher3/states/StateAnimationConfig.java
@@ -66,7 +66,8 @@
ANIM_WORKSPACE_PAGE_TRANSLATE_X,
ANIM_OVERVIEW_SPLIT_SELECT_FLOATING_TASK_TRANSLATE_OFFSCREEN,
ANIM_OVERVIEW_SPLIT_SELECT_INSTRUCTIONS_FADE,
- ANIM_ALL_APPS_BOTTOM_SHEET_FADE
+ ANIM_ALL_APPS_BOTTOM_SHEET_FADE,
+ ANIM_ALL_APPS_KEYBOARD_FADE
})
@Retention(RetentionPolicy.SOURCE)
public @interface AnimType {}
@@ -90,8 +91,9 @@
public static final int ANIM_OVERVIEW_SPLIT_SELECT_FLOATING_TASK_TRANSLATE_OFFSCREEN = 17;
public static final int ANIM_OVERVIEW_SPLIT_SELECT_INSTRUCTIONS_FADE = 18;
public static final int ANIM_ALL_APPS_BOTTOM_SHEET_FADE = 19;
+ public static final int ANIM_ALL_APPS_KEYBOARD_FADE = 20;
- private static final int ANIM_TYPES_COUNT = 20;
+ private static final int ANIM_TYPES_COUNT = 21;
protected final Interpolator[] mInterpolators = new Interpolator[ANIM_TYPES_COUNT];
diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java
index e62ccbc..b054f51 100644
--- a/src/com/android/launcher3/testing/TestInformationHandler.java
+++ b/src/com/android/launcher3/testing/TestInformationHandler.java
@@ -16,7 +16,7 @@
package com.android.launcher3.testing;
import static com.android.launcher3.allapps.AllAppsStore.DEFER_UPDATES_TEST;
-import static com.android.launcher3.config.FeatureFlags.FOLDABLE_SINGLE_PAGE;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_TRACKPAD_GESTURE;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import android.annotation.TargetApi;
@@ -156,8 +156,7 @@
return response;
case TestProtocol.REQUEST_IS_TWO_PANELS:
- response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD,
- FOLDABLE_SINGLE_PAGE.get() ? false : mDeviceProfile.isTwoPanels);
+ response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD, false);
return response;
case TestProtocol.REQUEST_GET_HAD_NONTEST_EVENTS:
@@ -229,7 +228,13 @@
}
case TestProtocol.REQUEST_HAS_TIS: {
- response.putBoolean(TestProtocol.REQUEST_HAS_TIS, false);
+ response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD, false);
+ return response;
+ }
+
+ case TestProtocol.REQUEST_IS_TRACKPAD_GESTURE_ENABLED: {
+ response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD,
+ ENABLE_TRACKPAD_GESTURE.get());
return response;
}
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index c499e35..cec4574 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -15,6 +15,7 @@
*/
package com.android.launcher3.touch;
+import static com.android.app.animation.Interpolators.scrollInterpolatorForVelocity;
import static com.android.launcher3.LauncherAnimUtils.SUCCESS_TRANSITION_PROGRESS;
import static com.android.launcher3.LauncherAnimUtils.TABLET_BOTTOM_SHEET_SUCCESS_TRANSITION_PROGRESS;
import static com.android.launcher3.LauncherAnimUtils.newCancelListener;
@@ -22,7 +23,6 @@
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
-import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_ALLAPPS;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
@@ -384,8 +384,8 @@
} else {
logReachedState(targetState);
}
- mLauncher.getRootView().getSysUiScrim().createSysuiMultiplierAnim(
- 1f).setDuration(0).start();
+ mLauncher.getRootView().getSysUiScrim().getSysUIMultiplier().animateToValue(1f)
+ .setDuration(0).start();
}
private void logReachedState(LauncherState targetState) {
diff --git a/src/com/android/launcher3/touch/AllAppsSwipeController.java b/src/com/android/launcher3/touch/AllAppsSwipeController.java
index d028f24..ad812f0 100644
--- a/src/com/android/launcher3/touch/AllAppsSwipeController.java
+++ b/src/com/android/launcher3/touch/AllAppsSwipeController.java
@@ -15,17 +15,19 @@
*/
package com.android.launcher3.touch;
+import static com.android.app.animation.Interpolators.EMPHASIZED;
+import static com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE;
+import static com.android.app.animation.Interpolators.EMPHASIZED_DECELERATE;
+import static com.android.app.animation.Interpolators.FINAL_FRAME;
+import static com.android.app.animation.Interpolators.INSTANT;
+import static com.android.app.animation.Interpolators.LINEAR;
+import static com.android.app.animation.Interpolators.clampToProgress;
+import static com.android.app.animation.Interpolators.mapToProgress;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.anim.Interpolators.EMPHASIZED;
-import static com.android.launcher3.anim.Interpolators.EMPHASIZED_ACCELERATE;
-import static com.android.launcher3.anim.Interpolators.EMPHASIZED_DECELERATE;
-import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
-import static com.android.launcher3.anim.Interpolators.INSTANT;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.anim.Interpolators.clampToProgress;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_BOTTOM_SHEET_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_KEYBOARD_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_DEPTH;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_SCALE;
@@ -40,11 +42,11 @@
import android.view.MotionEvent;
import android.view.animation.Interpolator;
+import com.android.app.animation.Interpolators;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.states.StateAnimationConfig;
/**
@@ -293,20 +295,15 @@
config.setInterpolator(ANIM_WORKSPACE_SCALE, INSTANT);
config.setInterpolator(ANIM_WORKSPACE_TRANSLATE, INSTANT);
} else {
- // Remove scrim for this transition.
- config.setInterpolator(ANIM_SCRIM_FADE, progress -> 0);
-
- // For now, pop the background panel in at full opacity at the threshold.
+ // Pop the background panel, keyboard, and content in at full opacity at the threshold.
config.setInterpolator(ANIM_ALL_APPS_BOTTOM_SHEET_FADE,
thresholdInterpolator(threshold, INSTANT));
-
- // Fade the apps in when the scrim normally does, so it's apparent sooner what is
- // happening (in this case we are fading them on top of the background panel).
- config.setInterpolator(ANIM_ALL_APPS_FADE,
- thresholdInterpolator(threshold, SCRIM_FADE_MANUAL));
+ config.setInterpolator(ANIM_ALL_APPS_KEYBOARD_FADE,
+ thresholdInterpolator(threshold, INSTANT));
+ config.setInterpolator(ANIM_ALL_APPS_FADE, thresholdInterpolator(threshold, INSTANT));
config.setInterpolator(ANIM_VERTICAL_PROGRESS,
- thresholdInterpolator(threshold, ALL_APPS_VERTICAL_PROGRESS_MANUAL));
+ thresholdInterpolator(threshold, mapToProgress(LINEAR, threshold, 1f)));
}
}
diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java
index 6647d0d..a7c94bb 100644
--- a/src/com/android/launcher3/util/DisplayController.java
+++ b/src/com/android/launcher3/util/DisplayController.java
@@ -54,8 +54,8 @@
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
@@ -106,7 +106,8 @@
private final LauncherPrefs mPrefs;
- private DisplayController(Context context) {
+ @VisibleForTesting
+ protected DisplayController(Context context) {
mContext = context;
mDM = context.getSystemService(DisplayManager.class);
mPrefs = LauncherPrefs.get(context);
@@ -127,8 +128,7 @@
Context displayInfoContext = getDisplayInfoContext(display);
mInfo = new Info(displayInfoContext, wmProxy,
wmProxy.estimateInternalDisplayBounds(displayInfoContext));
- mInfo.mPerDisplayBounds.forEach((key, value) -> FileLog.i(TAG,
- "(CTOR) perDisplayBounds - " + key + ": " + Arrays.deepToString(value)));
+ FileLog.i(TAG, "(CTOR) perDisplayBounds: " + mInfo.mPerDisplayBounds);
}
/**
@@ -286,9 +286,8 @@
if (!newInfo.supportedBounds.equals(oldInfo.supportedBounds)
|| !newInfo.mPerDisplayBounds.equals(oldInfo.mPerDisplayBounds)) {
change |= CHANGE_SUPPORTED_BOUNDS;
- newInfo.mPerDisplayBounds.forEach((key, value) -> FileLog.w(TAG,
- "(CHANGE_SUPPORTED_BOUNDS) perDisplayBounds - " + key + ": "
- + Arrays.deepToString(value)));
+ FileLog.w(TAG,
+ "(CHANGE_SUPPORTED_BOUNDS) perDisplayBounds: " + newInfo.mPerDisplayBounds);
}
if (DEBUG) {
Log.d(TAG, "handleInfoChange - change: " + getChangeFlagsString(change));
@@ -329,7 +328,7 @@
// WindowBounds
public final WindowBounds realBounds;
public final Set<WindowBounds> supportedBounds = new ArraySet<>();
- private final ArrayMap<CachedDisplayInfo, WindowBounds[]> mPerDisplayBounds =
+ private final ArrayMap<CachedDisplayInfo, List<WindowBounds>> mPerDisplayBounds =
new ArrayMap<>();
public Info(Context displayInfoContext) {
@@ -340,7 +339,7 @@
// Used for testing
public Info(Context displayInfoContext,
WindowManagerProxy wmProxy,
- Map<CachedDisplayInfo, WindowBounds[]> perDisplayBoundsCache) {
+ Map<CachedDisplayInfo, List<WindowBounds>> perDisplayBoundsCache) {
CachedDisplayInfo displayInfo = wmProxy.getDisplayInfo(displayInfoContext);
normalizedDisplayInfo = displayInfo.normalize();
rotation = displayInfo.rotation;
@@ -354,16 +353,14 @@
navigationMode = wmProxy.getNavigationMode(displayInfoContext);
mPerDisplayBounds.putAll(perDisplayBoundsCache);
- WindowBounds[] cachedValue = mPerDisplayBounds.get(normalizedDisplayInfo);
+ List<WindowBounds> cachedValue = mPerDisplayBounds.get(normalizedDisplayInfo);
realBounds = wmProxy.getRealBounds(displayInfoContext, displayInfo);
if (cachedValue == null) {
// Unexpected normalizedDisplayInfo is found, recreate the cache
FileLog.e(TAG, "Unexpected normalizedDisplayInfo found, invalidating cache: "
+ normalizedDisplayInfo);
- mPerDisplayBounds.forEach((key, value) -> FileLog.e(TAG,
- "(Invalid Cache) perDisplayBounds - " + key + ": " + Arrays.deepToString(
- value)));
+ FileLog.e(TAG, "(Invalid Cache) perDisplayBounds : " + mPerDisplayBounds);
mPerDisplayBounds.clear();
mPerDisplayBounds.putAll(wmProxy.estimateInternalDisplayBounds(displayInfoContext));
cachedValue = mPerDisplayBounds.get(normalizedDisplayInfo);
@@ -376,22 +373,19 @@
if (cachedValue != null) {
// Verify that the real bounds are a match
- WindowBounds expectedBounds = cachedValue[displayInfo.rotation];
+ WindowBounds expectedBounds = cachedValue.get(displayInfo.rotation);
if (!realBounds.equals(expectedBounds)) {
- WindowBounds[] clone = new WindowBounds[4];
- System.arraycopy(cachedValue, 0, clone, 0, 4);
- clone[displayInfo.rotation] = realBounds;
+ List<WindowBounds> clone = new ArrayList<>(cachedValue);
+ clone.set(displayInfo.rotation, realBounds);
mPerDisplayBounds.put(normalizedDisplayInfo, clone);
}
}
- mPerDisplayBounds.values().forEach(
- windowBounds -> Collections.addAll(supportedBounds, windowBounds));
+ mPerDisplayBounds.values().forEach(supportedBounds::addAll);
if (DEBUG) {
Log.d(TAG, "displayInfo: " + displayInfo);
Log.d(TAG, "realBounds: " + realBounds);
Log.d(TAG, "normalizedDisplayInfo: " + normalizedDisplayInfo);
- mPerDisplayBounds.forEach((key, value) -> Log.d(TAG,
- "perDisplayBounds - " + key + ": " + Arrays.deepToString(value)));
+ Log.d(TAG, "perDisplayBounds: " + mPerDisplayBounds);
}
}
@@ -448,7 +442,7 @@
pw.println(" navigationMode=" + info.navigationMode.name());
pw.println(" currentSize=" + info.currentSize);
info.mPerDisplayBounds.forEach((key, value) -> pw.println(
- " perDisplayBounds - " + key + ": " + Arrays.deepToString(value)));
+ " perDisplayBounds - " + key + ": " + value));
}
/**
diff --git a/src/com/android/launcher3/util/IconSizeSteps.kt b/src/com/android/launcher3/util/IconSizeSteps.kt
new file mode 100644
index 0000000..2a5afe0
--- /dev/null
+++ b/src/com/android/launcher3/util/IconSizeSteps.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.util
+
+import android.content.res.Resources
+import androidx.core.content.res.getDimensionOrThrow
+import androidx.core.content.res.use
+import com.android.launcher3.R
+import kotlin.math.max
+
+class IconSizeSteps(res: Resources) {
+ private val steps: List<Int>
+
+ init {
+ steps =
+ res.obtainTypedArray(R.array.icon_size_steps).use {
+ (0 until it.length()).map { step -> it.getDimensionOrThrow(step).toInt() }.sorted()
+ }
+ }
+
+ fun minimumIconSize(): Int = steps[0]
+
+ fun getNextLowerIconSize(iconSizePx: Int): Int {
+ return steps[max(0, getIndexForIconSize(iconSizePx) - 1)]
+ }
+
+ fun getIconSmallerThan(cellWidth: Int): Int {
+ return steps.lastOrNull { it <= cellWidth } ?: steps[0]
+ }
+
+ private fun getIndexForIconSize(iconSizePx: Int): Int {
+ return max(0, steps.indexOfFirst { iconSizePx <= it })
+ }
+}
diff --git a/src/com/android/launcher3/util/InstantAppResolver.java b/src/com/android/launcher3/util/InstantAppResolver.java
index 6f706d2..bdb5e77 100644
--- a/src/com/android/launcher3/util/InstantAppResolver.java
+++ b/src/com/android/launcher3/util/InstantAppResolver.java
@@ -42,14 +42,7 @@
return false;
}
- public boolean isInstantApp(Context context, String packageName) {
- PackageManager packageManager = context.getPackageManager();
- try {
- return isInstantApp(packageManager.getPackageInfo(packageName, 0).applicationInfo);
- } catch (PackageManager.NameNotFoundException e) {
- Log.e("InstantAppResolver", "Failed to determine whether package is instant app "
- + packageName, e);
- }
+ public boolean isInstantApp(String packageName, int userId) {
return false;
}
}
diff --git a/src/com/android/launcher3/util/IntArray.java b/src/com/android/launcher3/util/IntArray.java
index 1c78795..2498242 100644
--- a/src/com/android/launcher3/util/IntArray.java
+++ b/src/com/android/launcher3/util/IntArray.java
@@ -250,6 +250,11 @@
return b.toString();
}
+ @Override
+ public String toString() {
+ return "IntArray [" + toConcatString() + "]";
+ }
+
public static IntArray fromConcatString(String concatString) {
StringTokenizer tokenizer = new StringTokenizer(concatString, ",");
int[] array = new int[tokenizer.countTokens()];
diff --git a/src/com/android/launcher3/util/LockedUserState.kt b/src/com/android/launcher3/util/LockedUserState.kt
index 1231604..0a87594 100644
--- a/src/com/android/launcher3/util/LockedUserState.kt
+++ b/src/com/android/launcher3/util/LockedUserState.kt
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
package com.android.launcher3.util
import android.content.Context
diff --git a/src/com/android/launcher3/util/MainThreadInitializedObject.java b/src/com/android/launcher3/util/MainThreadInitializedObject.java
index 6a4e528..1cb9994 100644
--- a/src/com/android/launcher3/util/MainThreadInitializedObject.java
+++ b/src/com/android/launcher3/util/MainThreadInitializedObject.java
@@ -48,8 +48,8 @@
}
public T get(Context context) {
- if (context instanceof SandboxContext) {
- return ((SandboxContext) context).getObject(this, mProvider);
+ if (context instanceof SandboxContext sc) {
+ return sc.getObject(this);
}
if (mValue == null) {
@@ -131,23 +131,22 @@
* Find a cached object from mObjectMap if we have already created one. If not, generate
* an object using the provider.
*/
- private <T> T getObject(MainThreadInitializedObject<T> object, ObjectProvider<T> provider) {
+ protected <T> T getObject(MainThreadInitializedObject<T> object) {
synchronized (mDestroyLock) {
if (mDestroyed) {
Log.e(TAG, "Static object access with a destroyed context");
}
-
T t = (T) mObjectMap.get(object);
if (t != null) {
return t;
}
if (Looper.myLooper() == Looper.getMainLooper()) {
- t = createObject(provider);
+ t = createObject(object);
// Check if we've explicitly allowed the object or if it's a SafeCloseable,
// it will get destroyed in onDestroy()
if (!mAllowedObjects.contains(object) && !(t instanceof SafeCloseable)) {
- throw new IllegalStateException(
- "Leaking unknown objects " + object + " " + provider + " " + t);
+ throw new IllegalStateException("Leaking unknown objects "
+ + object + " " + object.mProvider + " " + t);
}
mObjectMap.put(object, t);
mOrderedObjects.add(t);
@@ -156,15 +155,15 @@
}
try {
- return MAIN_EXECUTOR.submit(() -> getObject(object, provider)).get();
+ return MAIN_EXECUTOR.submit(() -> getObject(object)).get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
}
@UiThread
- protected <T> T createObject(ObjectProvider<T> provider) {
- return provider.get(this);
+ protected <T> T createObject(MainThreadInitializedObject<T> object) {
+ return object.mProvider.get(this);
}
}
}
diff --git a/src/com/android/launcher3/util/PackageManagerHelper.java b/src/com/android/launcher3/util/PackageManagerHelper.java
index 1d6bc25..91203a7 100644
--- a/src/com/android/launcher3/util/PackageManagerHelper.java
+++ b/src/com/android/launcher3/util/PackageManagerHelper.java
@@ -164,13 +164,6 @@
}
}
- public static Intent getStyleWallpapersIntent(Context context) {
- return new Intent(Intent.ACTION_SET_WALLPAPER).setComponent(
- new ComponentName(context.getString(R.string.wallpaper_picker_package),
- context.getString(R.string.custom_activity_picker)
- ));
- }
-
/**
* Starts the details activity for {@code info}
*/
diff --git a/src/com/android/launcher3/util/TraceHelper.java b/src/com/android/launcher3/util/TraceHelper.java
index c23df77..138cc4a 100644
--- a/src/com/android/launcher3/util/TraceHelper.java
+++ b/src/com/android/launcher3/util/TraceHelper.java
@@ -15,12 +15,17 @@
*/
package com.android.launcher3.util;
+import android.annotation.SuppressLint;
import android.os.Trace;
import androidx.annotation.MainThread;
+import com.android.launcher3.Utilities;
+
import java.util.function.Supplier;
+import kotlin.random.Random;
+
/**
* A wrapper around {@link Trace} to allow better testing.
*
@@ -36,54 +41,63 @@
// Temporarily ignore blocking binder calls for this trace.
public static final int FLAG_IGNORE_BINDERS = 1 << 1;
- public static final int FLAG_CHECK_FOR_RACE_CONDITIONS = 1 << 2;
-
- public static final int FLAG_UI_EVENT =
- FLAG_ALLOW_BINDER_TRACKING | FLAG_CHECK_FOR_RACE_CONDITIONS;
-
/**
* Static instance of Trace helper, overridden in tests.
*/
public static TraceHelper INSTANCE = new TraceHelper();
/**
- * @return a token to pass into {@link #endSection(Object)}.
+ * @see Trace#beginSection(String)
*/
- public Object beginSection(String sectionName) {
- return beginSection(sectionName, 0);
- }
-
- public Object beginSection(String sectionName, int flags) {
+ public void beginSection(String sectionName) {
Trace.beginSection(sectionName);
- return null;
}
/**
- * @param token the token returned from {@link #beginSection(String, int)}
+ * @see Trace#endSection()
*/
- public void endSection(Object token) {
+ public void endSection() {
Trace.endSection();
}
/**
- * Similar to {@link #beginSection} but doesn't add a trace section.
+ * @see Trace#beginAsyncSection(String, int)
+ * @return a SafeCloseable that can be used to end the session
*/
- public Object beginFlagsOverride(int flags) {
- return null;
+ @SuppressWarnings("NewApi")
+ @SuppressLint("NewApi")
+ public SafeCloseable beginAsyncSection(String sectionName) {
+ if (!Utilities.ATLEAST_Q) {
+ return () -> { };
+ }
+ int cookie = Random.Default.nextInt();
+ Trace.beginAsyncSection(sectionName, cookie);
+ return () -> Trace.endAsyncSection(sectionName, cookie);
}
- public void endFlagsOverride(Object token) { }
+ /**
+ * Returns a SafeCloseable to temporarily ignore blocking binder calls.
+ */
+ @SuppressWarnings("NewApi")
+ @SuppressLint("NewApi")
+ public SafeCloseable allowIpcs(String rpcName) {
+ if (!Utilities.ATLEAST_Q) {
+ return () -> { };
+ }
+ int cookie = Random.Default.nextInt();
+ Trace.beginAsyncSection(rpcName, cookie);
+ return () -> Trace.endAsyncSection(rpcName, cookie);
+ }
/**
* Temporarily ignore blocking binder calls for the duration of this {@link Supplier}.
+ *
+ * Note, new features should be designed to not rely on mainThread RPCs.
*/
@MainThread
public static <T> T allowIpcs(String rpcName, Supplier<T> supplier) {
- Object traceToken = INSTANCE.beginSection(rpcName, FLAG_IGNORE_BINDERS);
- try {
+ try (SafeCloseable c = INSTANCE.allowIpcs(rpcName)) {
return supplier.get();
- } finally {
- INSTANCE.endSection(traceToken);
}
}
}
diff --git a/src/com/android/launcher3/util/VibratorWrapper.java b/src/com/android/launcher3/util/VibratorWrapper.java
index ceba0db..91945ca 100644
--- a/src/com/android/launcher3/util/VibratorWrapper.java
+++ b/src/com/android/launcher3/util/VibratorWrapper.java
@@ -17,6 +17,7 @@
import static android.os.VibrationEffect.createPredefined;
import static android.provider.Settings.System.HAPTIC_FEEDBACK_ENABLED;
+
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
@@ -68,6 +69,9 @@
@Nullable
private final VibrationEffect mBumpEffect;
+ @Nullable
+ private final VibrationEffect mAssistEffect;
+
private long mLastDragTime;
private final int mThresholdUntilNextDragCallMillis;
@@ -125,12 +129,25 @@
mBumpEffect = null;
mThresholdUntilNextDragCallMillis = 0;
}
+
+ if (Utilities.ATLEAST_R && mVibrator.areAllPrimitivesSupported(
+ VibrationEffect.Composition.PRIMITIVE_QUICK_RISE,
+ VibrationEffect.Composition.PRIMITIVE_TICK)) {
+ // quiet ramp, short pause, then sharp tick
+ mAssistEffect = VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_RISE, 0.25f)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1f, 50)
+ .compose();
+ } else {
+ // fallback for devices without composition support
+ mAssistEffect = VibrationEffect.createPredefined(VibrationEffect.EFFECT_HEAVY_CLICK);
+ }
}
/**
- * This is called when the user swipes to/from all apps. This is meant to be used in between
- * long animation progresses so that it gives a dragging texture effect. For a better
- * experience, this should be used in combination with vibrateForDragCommit().
+ * This is called when the user swipes to/from all apps. This is meant to be used in between
+ * long animation progresses so that it gives a dragging texture effect. For a better
+ * experience, this should be used in combination with vibrateForDragCommit().
*/
public void vibrateForDragTexture() {
if (mDragEffect == null) {
@@ -145,7 +162,7 @@
}
/**
- * This is used when user reaches the commit threshold when swiping to/from from all apps.
+ * This is used when user reaches the commit threshold when swiping to/from from all apps.
*/
public void vibrateForDragCommit() {
if (mCommitEffect != null) {
@@ -156,9 +173,9 @@
}
/**
- * The bump haptic is used to be called at the end of a swipe and only if it the gesture is a
- * FLING going to/from all apps. Client can just call this method elsewhere just for the
- * effect.
+ * The bump haptic is used to be called at the end of a swipe and only if it the gesture is a
+ * FLING going to/from all apps. Client can just call this method elsewhere just for the
+ * effect.
*/
public void vibrateForDragBump() {
if (mBumpEffect != null) {
@@ -167,6 +184,15 @@
}
/**
+ * The assist haptic is used to be called when an assistant is invoked
+ */
+ public void vibrateForAssist() {
+ if (mAssistEffect != null) {
+ vibrate(mAssistEffect);
+ }
+ }
+
+ /**
* This should be used to cancel a haptic in case where the haptic shouldn't be vibrating. For
* example, when no animation is happening but a vibrator happens to be vibrating still. Need
* boolean parameter for {@link PendingAnimation#addEndListener(Consumer)}.
@@ -176,6 +202,7 @@
// reset dragTexture timestamp to be able to play dragTexture again whenever cancelled
mLastDragTime = 0;
}
+
private boolean isHapticFeedbackEnabled(ContentResolver resolver) {
return Settings.System.getInt(resolver, HAPTIC_FEEDBACK_ENABLED, 0) == 1;
}
diff --git a/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java b/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java
index 4ac6bc4..b97b889 100644
--- a/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java
+++ b/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java
@@ -15,9 +15,9 @@
import androidx.annotation.AnyThread;
+import com.android.app.animation.Interpolators;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
-import com.android.launcher3.anim.Interpolators;
/**
* Utility class to handle wallpaper scrolling along with workspace.
@@ -237,7 +237,7 @@
public OffsetHandler(Context context) {
super(UI_HELPER_EXECUTOR.getLooper());
- mInterpolator = Interpolators.DEACCEL_1_5;
+ mInterpolator = Interpolators.DECELERATE_1_5;
mWM = WallpaperManager.getInstance(context);
}
diff --git a/src/com/android/launcher3/util/window/WindowManagerProxy.java b/src/com/android/launcher3/util/window/WindowManagerProxy.java
index 4093bc9..278a37e 100644
--- a/src/com/android/launcher3/util/window/WindowManagerProxy.java
+++ b/src/com/android/launcher3/util/window/WindowManagerProxy.java
@@ -57,6 +57,9 @@
import com.android.launcher3.util.ResourceBasedOverride;
import com.android.launcher3.util.WindowBounds;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Utility class for mocking some window manager behaviours
*/
@@ -90,11 +93,11 @@
* Returns a map of normalized info of internal displays to estimated window bounds
* for that display
*/
- public ArrayMap<CachedDisplayInfo, WindowBounds[]> estimateInternalDisplayBounds(
+ public ArrayMap<CachedDisplayInfo, List<WindowBounds>> estimateInternalDisplayBounds(
Context displayInfoContext) {
CachedDisplayInfo info = getDisplayInfo(displayInfoContext).normalize();
- WindowBounds[] bounds = estimateWindowBounds(displayInfoContext, info);
- ArrayMap<CachedDisplayInfo, WindowBounds[]> result = new ArrayMap<>();
+ List<WindowBounds> bounds = estimateWindowBounds(displayInfoContext, info);
+ ArrayMap<CachedDisplayInfo, List<WindowBounds>> result = new ArrayMap<>();
result.put(info, bounds);
return result;
}
@@ -200,7 +203,8 @@
/**
* Returns a list of possible WindowBounds for the display keyed on the 4 surface rotations
*/
- protected WindowBounds[] estimateWindowBounds(Context context, CachedDisplayInfo displayInfo) {
+ protected List<WindowBounds> estimateWindowBounds(Context context,
+ CachedDisplayInfo displayInfo) {
int densityDpi = context.getResources().getConfiguration().densityDpi;
int rotation = displayInfo.rotation;
Rect safeCutout = displayInfo.cutout;
@@ -243,7 +247,7 @@
? 0
: getDimenByName(systemRes, NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE);
- WindowBounds[] result = new WindowBounds[4];
+ List<WindowBounds> result = new ArrayList<>(4);
Point tempSize = new Point();
for (int i = 0; i < 4; i++) {
int rotationChange = deltaRotation(rotation, i);
@@ -274,7 +278,7 @@
} else {
insets.right = Math.max(insets.right, navbarWidth);
}
- result[i] = new WindowBounds(bounds, insets, i);
+ result.add(new WindowBounds(bounds, insets, i));
}
return result;
}
diff --git a/src/com/android/launcher3/views/AbstractSlideInView.java b/src/com/android/launcher3/views/AbstractSlideInView.java
index ec7ec0b..91eb109 100644
--- a/src/com/android/launcher3/views/AbstractSlideInView.java
+++ b/src/com/android/launcher3/views/AbstractSlideInView.java
@@ -17,11 +17,11 @@
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static com.android.app.animation.Interpolators.scrollInterpolatorForVelocity;
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.LauncherAnimUtils.SUCCESS_TRANSITION_PROGRESS;
import static com.android.launcher3.LauncherAnimUtils.TABLET_BOTTOM_SHEET_SUCCESS_TRANSITION_PROGRESS;
import static com.android.launcher3.allapps.AllAppsTransitionController.REVERT_SWIPE_ALL_APPS_TO_HOME_ANIMATION_DURATION_MS;
-import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
import static com.android.launcher3.util.ScrollableLayoutManager.PREDICTIVE_BACK_MIN_SCALE;
import android.animation.Animator;
@@ -47,10 +47,10 @@
import androidx.annotation.Px;
import androidx.annotation.RequiresApi;
+import com.android.app.animation.Interpolators;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatedFloat;
-import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.touch.BaseSwipeDetector;
import com.android.launcher3.touch.SingleAxisSwipeDetector;
@@ -310,7 +310,7 @@
TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
mOpenCloseAnimator.setDuration(
BaseSwipeDetector.calculateDuration(velocity, mTranslationShift))
- .setInterpolator(Interpolators.DEACCEL);
+ .setInterpolator(Interpolators.DECELERATE);
mOpenCloseAnimator.start();
}
}
@@ -357,7 +357,7 @@
}
protected Interpolator getIdleInterpolator() {
- return Interpolators.ACCEL;
+ return Interpolators.ACCELERATE;
}
protected void onCloseComplete() {
diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java
index 4b319e5..67f24aa 100644
--- a/src/com/android/launcher3/views/ActivityContext.java
+++ b/src/com/android/launcher3/views/ActivityContext.java
@@ -338,6 +338,12 @@
return null;
}
+ boolean isShortcut = (item instanceof WorkspaceItemInfo)
+ && item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
+ && !((WorkspaceItemInfo) item).isPromise();
+ if (isShortcut && GO_DISABLE_WIDGETS) {
+ return null;
+ }
ActivityOptionsWrapper options = v != null ? getActivityLaunchOptions(v, item)
: makeDefaultActivityOptions(item != null && item.animationType == DEFAULT_NO_ICON
? SPLASH_SCREEN_STYLE_SOLID_COLOR : -1 /* SPLASH_SCREEN_STYLE_UNDEFINED */);
@@ -349,13 +355,11 @@
intent.setSourceBounds(Utilities.getViewBounds(v));
}
try {
- boolean isShortcut = (item instanceof WorkspaceItemInfo)
- && (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT
- || item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT)
- && !((WorkspaceItemInfo) item).isPromise();
if (isShortcut) {
- // Shortcuts need some special checks due to legacy reasons.
- startShortcutIntentSafely(intent, optsBundle, item);
+ String id = ((WorkspaceItemInfo) item).getDeepShortcutId();
+ String packageName = intent.getPackage();
+ ((Context) this).getSystemService(LauncherApps.class).startShortcut(
+ packageName, id, intent.getSourceBounds(), optsBundle, user);
} else if (user == null || user.equals(Process.myUserHandle())) {
// Could be launching some bookkeeping activity
context.startActivity(intent, optsBundle);
@@ -430,55 +434,6 @@
return new ActivityOptionsWrapper(options, new RunnableList());
}
- /**
- * Safely launches an intent for a shortcut.
- *
- * @param intent Intent to start.
- * @param optsBundle Optional launch arguments.
- * @param info Shortcut information.
- */
- default void startShortcutIntentSafely(Intent intent, Bundle optsBundle, ItemInfo info) {
- try {
- StrictMode.VmPolicy oldPolicy = StrictMode.getVmPolicy();
- try {
- // Temporarily disable deathPenalty on all default checks. For eg, shortcuts
- // containing file Uri's would cause a crash as penaltyDeathOnFileUriExposure
- // is enabled by default on NYC.
- StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll()
- .penaltyLog().build());
-
- if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
- String id = ((WorkspaceItemInfo) info).getDeepShortcutId();
- String packageName = intent.getPackage();
- startShortcut(packageName, id, intent.getSourceBounds(), optsBundle, info.user);
- } else {
- // Could be launching some bookkeeping activity
- ((Context) this).startActivity(intent, optsBundle);
- }
- } finally {
- StrictMode.setVmPolicy(oldPolicy);
- }
- } catch (SecurityException e) {
- throw e;
- }
- }
-
- /**
- * A wrapper around the platform method with Launcher specific checks.
- */
- default void startShortcut(String packageName, String id, Rect sourceBounds,
- Bundle startActivityOptions, UserHandle user) {
- if (GO_DISABLE_WIDGETS) {
- return;
- }
- try {
- ((Context) this).getSystemService(LauncherApps.class).startShortcut(packageName, id,
- sourceBounds, startActivityOptions, user);
- } catch (SecurityException | IllegalStateException e) {
- Log.e(TAG, "Failed to start shortcut", e);
- }
- }
-
default CellPosMapper getCellPosMapper() {
return CellPosMapper.DEFAULT;
}
diff --git a/src/com/android/launcher3/views/ArrowTipView.java b/src/com/android/launcher3/views/ArrowTipView.java
index 73c5ad4..8e05650 100644
--- a/src/com/android/launcher3/views/ArrowTipView.java
+++ b/src/com/android/launcher3/views/ArrowTipView.java
@@ -16,8 +16,13 @@
package com.android.launcher3.views;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.Configuration;
+import android.content.res.TypedArray;
import android.graphics.CornerPathEffect;
import android.graphics.Paint;
import android.graphics.Rect;
@@ -33,18 +38,16 @@
import androidx.annotation.Nullable;
import androidx.annotation.Px;
-import androidx.core.content.ContextCompat;
+import com.android.app.animation.Interpolators;
import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
-import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.graphics.TriangleShape;
/**
- * A base class for arrow tip view in launcher
+ * A base class for arrow tip view in launcher.
*/
public class ArrowTipView extends AbstractFloatingView {
@@ -54,33 +57,46 @@
private static final long SHOW_DURATION_MS = 300;
private static final long HIDE_DURATION_MS = 100;
- protected final BaseDraggingActivity mActivity;
+ private final ActivityContext mActivityContext;
private final Handler mHandler = new Handler();
- private final int mArrowWidth;
- private final int mArrowMinOffset;
private boolean mIsPointingUp;
private Runnable mOnClosed;
private View mArrowView;
+ private final int mArrowWidth;
+ private final int mArrowMinOffset;
+ private final int mArrowViewPaintColor;
+
+ private AnimatorSet mOpenAnimator = new AnimatorSet();
+ private AnimatorSet mCloseAnimator = new AnimatorSet();
public ArrowTipView(Context context) {
this(context, false);
}
public ArrowTipView(Context context, boolean isPointingUp) {
+ this(context, isPointingUp, R.layout.arrow_toast);
+ }
+
+ public ArrowTipView(Context context, boolean isPointingUp, int layoutId) {
super(context, null, 0);
- mActivity = BaseDraggingActivity.fromContext(context);
+ mActivityContext = ActivityContext.lookupContext(context);
mIsPointingUp = isPointingUp;
- mArrowWidth = context.getResources().getDimensionPixelSize(R.dimen.arrow_toast_arrow_width);
+ mArrowWidth = context.getResources().getDimensionPixelSize(
+ R.dimen.arrow_toast_arrow_width);
mArrowMinOffset = context.getResources().getDimensionPixelSize(
R.dimen.dynamic_grid_cell_border_spacing);
- init(context);
+ TypedArray ta = context.obtainStyledAttributes(R.styleable.ArrowTipView);
+ mArrowViewPaintColor = ta.getColor(R.styleable.ArrowTipView_arrowTipBackground,
+ context.getColor(R.color.arrow_tip_view_bg));
+ ta.recycle();
+ init(context, layoutId);
}
@Override
public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
close(true);
- if (mActivity.getDragLayer().isEventOverView(this, ev)) {
+ if (mActivityContext.getDragLayer().isEventOverView(this, ev)) {
return true;
}
}
@@ -89,18 +105,15 @@
@Override
protected void handleClose(boolean animate) {
+ if (mOpenAnimator.isStarted()) {
+ mOpenAnimator.cancel();
+ }
if (mIsOpen) {
if (animate) {
- animate().alpha(0f)
- .withLayer()
- .setStartDelay(0)
- .setDuration(HIDE_DURATION_MS)
- .setInterpolator(Interpolators.ACCEL)
- .withEndAction(() -> mActivity.getDragLayer().removeView(this))
- .start();
+ mCloseAnimator.start();
} else {
- animate().cancel();
- mActivity.getDragLayer().removeView(this);
+ mCloseAnimator.cancel();
+ mActivityContext.getDragLayer().removeView(this);
}
if (mOnClosed != null) mOnClosed.run();
mIsOpen = false;
@@ -112,12 +125,31 @@
return (type & TYPE_ON_BOARD_POPUP) != 0;
}
- private void init(Context context) {
- inflate(context, R.layout.arrow_toast, this);
+ private void init(Context context, int layoutId) {
+ inflate(context, layoutId, this);
setOrientation(LinearLayout.VERTICAL);
mArrowView = findViewById(R.id.arrow);
updateArrowTipInView();
+ setAlpha(0);
+
+ // Create default open animator.
+ mOpenAnimator.play(ObjectAnimator.ofFloat(this, ALPHA, 1f));
+ mOpenAnimator.setStartDelay(SHOW_DELAY_MS);
+ mOpenAnimator.setDuration(SHOW_DURATION_MS);
+ mOpenAnimator.setInterpolator(Interpolators.DECELERATE);
+
+ // Create default close animator.
+ mCloseAnimator.play(ObjectAnimator.ofFloat(this, ALPHA, 0));
+ mCloseAnimator.setStartDelay(0);
+ mCloseAnimator.setDuration(HIDE_DURATION_MS);
+ mCloseAnimator.setInterpolator(Interpolators.ACCELERATE);
+ mCloseAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mActivityContext.getDragLayer().removeView(ArrowTipView.this);
+ }
+ });
}
/**
@@ -153,10 +185,10 @@
public ArrowTipView show(
String text, int gravity, int arrowMarginStart, int top, boolean shouldAutoClose) {
((TextView) findViewById(R.id.text)).setText(text);
- ViewGroup parent = mActivity.getDragLayer();
+ ViewGroup parent = mActivityContext.getDragLayer();
parent.addView(this);
- DeviceProfile grid = mActivity.getDeviceProfile();
+ DeviceProfile grid = mActivityContext.getDeviceProfile();
DragLayer.LayoutParams params = (DragLayer.LayoutParams) getLayoutParams();
params.gravity = gravity;
@@ -185,14 +217,8 @@
if (shouldAutoClose) {
mHandler.postDelayed(() -> handleClose(true), AUTO_CLOSE_TIMEOUT_MILLIS);
}
- setAlpha(0);
- animate()
- .alpha(1f)
- .withLayer()
- .setStartDelay(SHOW_DELAY_MS)
- .setDuration(SHOW_DURATION_MS)
- .setInterpolator(Interpolators.DEACCEL)
- .start();
+
+ mOpenAnimator.start();
return this;
}
@@ -273,7 +299,7 @@
*/
@Nullable private ArrowTipView showAtLocation(String text, @Px int arrowXCoord,
@Px int yCoordDownPointingTip, @Px int yCoordUpPointingTip, boolean shouldAutoClose) {
- ViewGroup parent = mActivity.getDragLayer();
+ ViewGroup parent = mActivityContext.getDragLayer();
@Px int parentViewWidth = parent.getWidth();
@Px int parentViewHeight = parent.getHeight();
@Px int maxTextViewWidth = getContext().getResources()
@@ -288,8 +314,10 @@
TextView textView = findViewById(R.id.text);
textView.setText(text);
textView.setMaxWidth(maxTextViewWidth);
- parent.addView(this);
- requestLayout();
+ if (parent.indexOfChild(this) < 0) {
+ parent.addView(this);
+ requestLayout();
+ }
post(() -> {
// Adjust the tooltip horizontally.
@@ -333,14 +361,8 @@
if (shouldAutoClose) {
mHandler.postDelayed(() -> handleClose(true), AUTO_CLOSE_TIMEOUT_MILLIS);
}
- setAlpha(0);
- animate()
- .alpha(1f)
- .withLayer()
- .setStartDelay(SHOW_DELAY_MS)
- .setDuration(SHOW_DURATION_MS)
- .setInterpolator(Interpolators.DEACCEL)
- .start();
+
+ mOpenAnimator.start();
return this;
}
@@ -351,7 +373,7 @@
Paint arrowPaint = arrowDrawable.getPaint();
@Px int arrowTipRadius = getContext().getResources()
.getDimensionPixelSize(R.dimen.arrow_toast_corner_radius);
- arrowPaint.setColor(ContextCompat.getColor(getContext(), R.color.arrow_tip_view_bg));
+ arrowPaint.setColor(mArrowViewPaintColor);
arrowPaint.setPathEffect(new CornerPathEffect(arrowTipRadius));
mArrowView.setBackground(arrowDrawable);
// Add negative margin so that the rounded corners on base of arrow are not visible.
@@ -378,4 +400,18 @@
super.onConfigurationChanged(newConfig);
close(/* animate= */ false);
}
+
+ /**
+ * Sets a custom animation to run on open of the ArrowTipView.
+ */
+ public void setCustomOpenAnimation(AnimatorSet animator) {
+ mOpenAnimator = animator;
+ }
+
+ /**
+ * Sets a custom animation to run on close of the ArrowTipView.
+ */
+ public void setCustomCloseAnimation(AnimatorSet animator) {
+ mCloseAnimator = animator;
+ }
}
diff --git a/src/com/android/launcher3/views/ClipIconView.java b/src/com/android/launcher3/views/ClipIconView.java
index 694dead..87e496e 100644
--- a/src/com/android/launcher3/views/ClipIconView.java
+++ b/src/com/android/launcher3/views/ClipIconView.java
@@ -15,9 +15,9 @@
*/
package com.android.launcher3.views;
+import static com.android.app.animation.Interpolators.LINEAR;
import static com.android.launcher3.Utilities.boundToRange;
import static com.android.launcher3.Utilities.mapToRange;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
import static java.lang.Math.max;
diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
index 6b5c8df..f425821 100644
--- a/src/com/android/launcher3/views/FloatingIconView.java
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -17,10 +17,10 @@
import static android.view.Gravity.LEFT;
+import static com.android.app.animation.Interpolators.LINEAR;
import static com.android.launcher3.Utilities.getBadge;
import static com.android.launcher3.Utilities.getFullDrawable;
import static com.android.launcher3.Utilities.mapToRange;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import static com.android.launcher3.views.IconLabelDotView.setIconAndDotVisible;
diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java
index 4641e31..55febc7 100644
--- a/src/com/android/launcher3/views/OptionsPopupView.java
+++ b/src/com/android/launcher3/views/OptionsPopupView.java
@@ -55,7 +55,6 @@
import com.android.launcher3.shortcuts.DeepShortcutView;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
-import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.widget.picker.WidgetsFullSheet;
import java.util.ArrayList;
@@ -190,14 +189,9 @@
*/
public static ArrayList<OptionItem> getOptions(Launcher launcher) {
ArrayList<OptionItem> options = new ArrayList<>();
- boolean styleWallpaperExists = styleWallpapersExists(launcher);
- int resString = styleWallpaperExists
- ? R.string.styles_wallpaper_button_text : R.string.wallpaper_button_text;
- int resDrawable = styleWallpaperExists
- ? R.drawable.ic_palette : R.drawable.ic_wallpaper;
options.add(new OptionItem(launcher,
- resString,
- resDrawable,
+ R.string.styles_wallpaper_button_text,
+ R.drawable.ic_palette,
IGNORE,
OptionsPopupView::startWallpaperPicker));
if (!WidgetsModel.GO_DISABLE_WIDGETS) {
@@ -274,12 +268,8 @@
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
.putExtra(EXTRA_WALLPAPER_OFFSET,
launcher.getWorkspace().getWallpaperOffsetForCenterPage())
- .putExtra(EXTRA_WALLPAPER_LAUNCH_SOURCE, "app_launched_launcher");
- if (!styleWallpapersExists(launcher)) {
- intent.putExtra(EXTRA_WALLPAPER_FLAVOR, "wallpaper_only");
- } else {
- intent.putExtra(EXTRA_WALLPAPER_FLAVOR, "focus_wallpaper");
- }
+ .putExtra(EXTRA_WALLPAPER_LAUNCH_SOURCE, "app_launched_launcher")
+ .putExtra(EXTRA_WALLPAPER_FLAVOR, "focus_wallpaper");
String pickerPackage = launcher.getString(R.string.wallpaper_picker_package);
if (!TextUtils.isEmpty(pickerPackage)) {
intent.setPackage(pickerPackage);
@@ -290,7 +280,6 @@
static WorkspaceItemInfo placeholderInfo(Intent intent) {
WorkspaceItemInfo placeholderInfo = new WorkspaceItemInfo();
placeholderInfo.intent = intent;
- placeholderInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
placeholderInfo.container = LauncherSettings.Favorites.CONTAINER_SETTINGS;
return placeholderInfo;
}
@@ -323,9 +312,4 @@
this.clickListener = clickListener;
}
}
-
- private static boolean styleWallpapersExists(Context context) {
- return context.getPackageManager().resolveActivity(
- PackageManagerHelper.getStyleWallpapersIntent(context), 0) != null;
- }
}
diff --git a/src/com/android/launcher3/views/Snackbar.java b/src/com/android/launcher3/views/Snackbar.java
index 2460be1..99040ff 100644
--- a/src/com/android/launcher3/views/Snackbar.java
+++ b/src/com/android/launcher3/views/Snackbar.java
@@ -30,10 +30,10 @@
import androidx.annotation.Nullable;
+import com.android.app.animation.Interpolators;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
-import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.dragndrop.DragLayer;
@@ -175,7 +175,7 @@
.scaleX(1)
.scaleY(1)
.setDuration(SHOW_DURATION_MS)
- .setInterpolator(Interpolators.ACCEL_DEACCEL)
+ .setInterpolator(Interpolators.ACCELERATE_DECELERATE)
.start();
int timeout = AccessibilityManagerCompat.getRecommendedTimeoutMillis(activity,
TIMEOUT_DURATION_MS, FLAG_CONTENT_TEXT | FLAG_CONTENT_CONTROLS);
@@ -190,7 +190,7 @@
.withLayer()
.setStartDelay(0)
.setDuration(HIDE_DURATION_MS)
- .setInterpolator(Interpolators.ACCEL)
+ .setInterpolator(Interpolators.ACCELERATE)
.withEndAction(this::onClosed)
.start();
} else {
diff --git a/src/com/android/launcher3/views/WidgetsEduView.java b/src/com/android/launcher3/views/WidgetsEduView.java
index c2947c7..9180781 100644
--- a/src/com/android/launcher3/views/WidgetsEduView.java
+++ b/src/com/android/launcher3/views/WidgetsEduView.java
@@ -15,7 +15,7 @@
*/
package com.android.launcher3.views;
-import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
+import static com.android.app.animation.Interpolators.FAST_OUT_SLOW_IN;
import android.animation.PropertyValuesHolder;
import android.content.Context;
diff --git a/src/com/android/launcher3/widget/AddItemWidgetsBottomSheet.java b/src/com/android/launcher3/widget/AddItemWidgetsBottomSheet.java
index 9442734..473abf1 100644
--- a/src/com/android/launcher3/widget/AddItemWidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/AddItemWidgetsBottomSheet.java
@@ -16,8 +16,8 @@
package com.android.launcher3.widget;
+import static com.android.app.animation.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.Utilities.ATLEAST_R;
-import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import android.animation.PropertyValuesHolder;
import android.annotation.SuppressLint;
diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java
index 049131e..dcc86a1 100644
--- a/src/com/android/launcher3/widget/BaseWidgetSheet.java
+++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java
@@ -15,7 +15,7 @@
*/
package com.android.launcher3.widget;
-import static com.android.launcher3.anim.Interpolators.EMPHASIZED;
+import static com.android.app.animation.Interpolators.EMPHASIZED;
import static com.android.launcher3.config.FeatureFlags.LARGE_SCREEN_WIDGET_PICKER;
import android.content.Context;
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
index bc3889f..98d854e 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
@@ -95,6 +95,8 @@
private boolean mTrackingWidgetUpdate = false;
+ private boolean mIsWidgetCachingDisabled = false;
+
public LauncherAppWidgetHostView(Context context) {
super(context);
mLauncher = Launcher.getLauncher(context);
@@ -138,6 +140,10 @@
}
}
+ public void setIsWidgetCachingDisabled(boolean isWidgetCachingDisabled) {
+ mIsWidgetCachingDisabled = isWidgetCachingDisabled;
+ }
+
@Override
@TargetApi(Build.VERSION_CODES.Q)
public void updateAppWidget(RemoteViews remoteViews) {
@@ -147,7 +153,8 @@
TRACE_METHOD_NAME + getAppWidgetInfo().provider, getAppWidgetId());
mTrackingWidgetUpdate = false;
}
- if (FeatureFlags.ENABLE_CACHED_WIDGET.get()) {
+ if (FeatureFlags.ENABLE_CACHED_WIDGET.get()
+ && !mIsWidgetCachingDisabled) {
mLastRemoteViews = remoteViews;
if (isDeferringUpdates()) {
return;
diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
index 846dafd..93f7cb3 100644
--- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
@@ -16,8 +16,8 @@
package com.android.launcher3.widget;
+import static com.android.app.animation.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_BOTTOM_WIDGETS_TRAY;
-import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import android.animation.PropertyValuesHolder;
import android.content.Context;
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index 33c4f8d..a3ef6e1 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -774,7 +774,7 @@
super.onCloseComplete();
removeCallbacks(mShowEducationTipTask);
if (mLatestEducationalTip != null) {
- mLatestEducationalTip.close(false);
+ mLatestEducationalTip.close(true);
}
AccessibilityManagerCompat.sendStateEventToTest(getContext(), NORMAL_STATE_ORDINAL);
}
diff --git a/src/com/android/launcher3/workspace/WorkspaceSpecs.kt b/src/com/android/launcher3/workspace/WorkspaceSpecs.kt
index ac0a166..8cc0c59 100644
--- a/src/com/android/launcher3/workspace/WorkspaceSpecs.kt
+++ b/src/com/android/launcher3/workspace/WorkspaceSpecs.kt
@@ -16,13 +16,12 @@
package com.android.launcher3.workspace
-import android.content.res.TypedArray
import android.content.res.XmlResourceParser
import android.util.AttributeSet
import android.util.Log
-import android.util.TypedValue
import android.util.Xml
import com.android.launcher3.R
+import com.android.launcher3.responsive.SizeSpec
import com.android.launcher3.util.ResourceHelper
import java.io.IOException
import kotlin.math.roundToInt
@@ -45,6 +44,7 @@
val workspaceHeightSpecList = mutableListOf<WorkspaceSpec>()
val workspaceWidthSpecList = mutableListOf<WorkspaceSpec>()
+ // TODO(b/286538013) Remove this init after a more generic or reusable parser is created
init {
try {
val parser: XmlResourceParser = resourceHelper.getXml()
@@ -95,16 +95,16 @@
if (type == XmlPullParser.START_TAG) {
when (parser.name) {
XmlTags.START_PADDING -> {
- startPadding = SizeSpec(resourceHelper, attr)
+ startPadding = SizeSpec.create(resourceHelper, attr)
}
XmlTags.END_PADDING -> {
- endPadding = SizeSpec(resourceHelper, attr)
+ endPadding = SizeSpec.create(resourceHelper, attr)
}
XmlTags.GUTTER -> {
- gutter = SizeSpec(resourceHelper, attr)
+ gutter = SizeSpec.create(resourceHelper, attr)
}
XmlTags.CELL_SIZE -> {
- cellSize = SizeSpec(resourceHelper, attr)
+ cellSize = SizeSpec.create(resourceHelper, attr)
}
}
}
@@ -231,6 +231,13 @@
if (workspaceSpec.cellSize.ofRemainderSpace > 0)
cellSizePx = (workspaceSpec.cellSize.ofRemainderSpace * remainderSpace).roundToInt()
}
+
+ override fun toString(): String {
+ return "CalculatedWorkspaceSpec(availableSpace=$availableSpace, " +
+ "cells=$cells, startPaddingPx=$startPaddingPx, endPaddingPx=$endPaddingPx, " +
+ "gutterPx=$gutterPx, cellSizePx=$cellSizePx, " +
+ "workspaceSpec.maxAvailableSize=${workspaceSpec.maxAvailableSize})"
+ }
}
data class WorkspaceSpec(
@@ -263,61 +270,12 @@
}
private fun allSpecsAreValid(): Boolean =
- startPadding.isValid() && endPadding.isValid() && gutter.isValid() && cellSize.isValid()
-}
-
-class SizeSpec(resourceHelper: ResourceHelper, attrs: AttributeSet) {
- val fixedSize: Float
- val ofAvailableSpace: Float
- val ofRemainderSpace: Float
-
- init {
- val styledAttrs = resourceHelper.obtainStyledAttributes(attrs, R.styleable.SpecSize)
-
- fixedSize = getValue(styledAttrs, R.styleable.SpecSize_fixedSize)
- ofAvailableSpace = getValue(styledAttrs, R.styleable.SpecSize_ofAvailableSpace)
- ofRemainderSpace = getValue(styledAttrs, R.styleable.SpecSize_ofRemainderSpace)
-
- styledAttrs.recycle()
- }
-
- private fun getValue(a: TypedArray, index: Int): Float {
- if (a.getType(index) == TypedValue.TYPE_DIMENSION) {
- return a.getDimensionPixelSize(index, 0).toFloat()
- } else if (a.getType(index) == TypedValue.TYPE_FLOAT) {
- return a.getFloat(index, 0f)
- }
- return 0f
- }
-
- fun isValid(): Boolean {
- // All attributes are empty
- if (fixedSize < 0f && ofAvailableSpace <= 0f && ofRemainderSpace <= 0f) {
- Log.e(TAG, "SizeSpec#isValid - all attributes are empty")
- return false
- }
-
- // More than one attribute is filled
- val attrCount =
- (if (fixedSize > 0) 1 else 0) +
- (if (ofAvailableSpace > 0) 1 else 0) +
- (if (ofRemainderSpace > 0) 1 else 0)
- if (attrCount > 1) {
- Log.e(TAG, "SizeSpec#isValid - more than one attribute is filled")
- return false
- }
-
- // Values should be between 0 and 1
- if (ofAvailableSpace !in 0f..1f || ofRemainderSpace !in 0f..1f) {
- Log.e(TAG, "SizeSpec#isValid - values should be between 0 and 1")
- return false
- }
-
- return true
- }
-
- override fun toString(): String {
- return "SizeSpec(fixedSize=$fixedSize, ofAvailableSpace=$ofAvailableSpace, " +
- "ofRemainderSpace=$ofRemainderSpace)"
- }
+ startPadding.isValid() &&
+ endPadding.isValid() &&
+ gutter.isValid() &&
+ cellSize.isValid() &&
+ !startPadding.matchWorkspace &&
+ !endPadding.matchWorkspace &&
+ !gutter.matchWorkspace &&
+ !cellSize.matchWorkspace
}
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java b/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java
index 772a995..b62dbd1 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -15,7 +15,7 @@
*/
package com.android.launcher3.uioverrides.states;
-import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
+import static com.android.app.animation.Interpolators.DECELERATE;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_ALLAPPS;
import android.content.Context;
@@ -80,7 +80,7 @@
@Override
public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) {
PageAlphaProvider superPageAlphaProvider = super.getWorkspacePageAlphaProvider(launcher);
- return new PageAlphaProvider(DEACCEL_2) {
+ return new PageAlphaProvider(DECELERATE) {
@Override
public float getPageAlpha(int pageIndex) {
return launcher.getDeviceProfile().isTablet
diff --git a/tests/Android.bp b/tests/Android.bp
index e7f4084..d518a0e 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -58,6 +58,7 @@
"src/com/android/launcher3/util/rule/SimpleActivityRule.java",
"src/com/android/launcher3/util/rule/TestStabilityRule.java",
"src/com/android/launcher3/util/rule/TISBindRule.java",
+ "src/com/android/launcher3/util/rule/ViewCaptureAnalysisRule.java",
"src/com/android/launcher3/testcomponent/BaseTestingActivity.java",
"src/com/android/launcher3/testcomponent/OtherBaseTestingActivity.java",
"src/com/android/launcher3/testcomponent/CustomShortcutConfigActivity.java",
diff --git a/tests/res/raw/cache_data_updated_task_data.txt b/tests/res/raw/cache_data_updated_task_data.txt
deleted file mode 100644
index 603dbe3..0000000
--- a/tests/res/raw/cache_data_updated_task_data.txt
+++ /dev/null
@@ -1,28 +0,0 @@
-# Model data used by CacheDataUpdatedTaskTest
-
-classMap s com.android.launcher3.model.data.WorkspaceItemInfo
-
-# Items for the BgDataModel
-
-# App shortcuts
-bgItem s itemType=0 title=app1-class1 intent=component=app1/class1 id=1
-bgItem s itemType=0 title=app1-class2 intent=component=app1/class2 id=2
-bgItem s itemType=0 title=app2-class1 intent=component=app2/class1 id=3
-bgItem s itemType=0 title=app2-class2 intent=component=app2/class2 id=4
-
-# Auto install app shortcut
-bgItem s itemType=0 status=2 title=app3-class1 intent=component=app3/class1 id=5
-bgItem s itemType=0 status=2 title=app3-class2 intent=component=app3/class2 id=6
-
-# Custom shortcuts
-bgItem s itemType=1 title=app1-shrt intent=component=app1/class3 id=7
-bgItem s itemType=1 title=app4-shrt intent=component=app4/class1 id=8
-
-# Restored custom shortcut
-bgItem s itemType=1 status=1 title=app3-shrt intent=component=app3/class3 id=9
-bgItem s itemType=1 status=1 title=app5-shrt intent=component=app5/class1 id=10
-
-allApps componentName=app1/class1 intent=component=app1/class1
-allApps componentName=app1/class2 intent=component=app1/class2
-allApps componentName=app2/class1 intent=component=app2/class1
-allApps componentName=app2/class2 intent=component=app2/class2
\ No newline at end of file
diff --git a/tests/res/raw/package_install_state_change_task_data.txt b/tests/res/raw/package_install_state_change_task_data.txt
deleted file mode 100644
index e82ea9d..0000000
--- a/tests/res/raw/package_install_state_change_task_data.txt
+++ /dev/null
@@ -1,24 +0,0 @@
-# Model data used by PackageInstallStateChangeTaskTest
-
-classMap s com.android.launcher3.model.data.WorkspaceItemInfo
-classMap w com.android.launcher3.model.data.LauncherAppWidgetInfo
-
-# Items for the BgDataModel
-
-# App shortcuts
-bgItem s itemType=0 title=app1-class1 intent=component=app1/class1 id=1
-bgItem s itemType=0 title=app1-class2 intent=component=app1/class2 id=2
-bgItem s itemType=0 title=app2-class1 intent=component=app2/class1 id=3
-bgItem s itemType=0 title=app2-class2 intent=component=app2/class2 id=4
-
-# Promise icons for app3
-bgItem s itemType=0 status=2 title=app3-class1 intent=component=app3/class1 id=5
-bgItem s itemType=0 status=2 title=app3-class2 intent=component=app3/class2 id=6
-bgItem s itemType=1 status=1 title=app3-shrt intent=component=app3/class3 id=7
-
-# Promise icon for app4
-bgItem s itemType=1 status=1 title=app4-shrt intent=component=app4/class1 id=8
-
-# Widget
-bgItem w providerName=app4/provider1 id=9
-bgItem w providerName=app5/provider1 id=10
\ No newline at end of file
diff --git a/tests/res/raw/widgets_predication_update_task_data.txt b/tests/res/raw/widgets_predication_update_task_data.txt
deleted file mode 100644
index 941d195..0000000
--- a/tests/res/raw/widgets_predication_update_task_data.txt
+++ /dev/null
@@ -1,24 +0,0 @@
-# Model data used by WidgetsPredictionUpdateTasksTest
-
-classMap s com.android.launcher3.model.data.WorkspaceItemInfo
-classMap w com.android.launcher3.model.data.LauncherAppWidgetInfo
-
-# Items for the BgDataModel
-
-# App shortcuts
-bgItem s itemType=0 title=app1-class1 intent=component=app1/class1 id=1
-bgItem s itemType=0 title=app1-class2 intent=component=app1/class2 id=2
-bgItem s itemType=0 title=app2-class1 intent=component=app2/class1 id=3
-bgItem s itemType=0 title=app2-class2 intent=component=app2/class2 id=4
-
-# Promise icons for app3
-bgItem s itemType=0 status=2 title=app3-class1 intent=component=app3/class1 id=5
-bgItem s itemType=0 status=2 title=app3-class2 intent=component=app3/class2 id=6
-bgItem s itemType=1 status=1 title=app3-shrt intent=component=app3/class3 id=7
-
-# Promise icon for app4
-bgItem s itemType=1 status=1 title=app4-shrt intent=component=app4/class1 id=8
-
-# Widget
-bgItem w providerName=app4/provider1 id=9
-bgItem w providerName=app5/provider1 id=10
\ No newline at end of file
diff --git a/tests/res/values/attrs.xml b/tests/res/values/attrs.xml
index 2310d9e..0d586c2 100644
--- a/tests/res/values/attrs.xml
+++ b/tests/res/values/attrs.xml
@@ -26,10 +26,21 @@
<attr name="maxAvailableSize" format="dimension" />
</declare-styleable>
- <declare-styleable name="SpecSize">
+ <declare-styleable name="SizeSpec">
<attr name="fixedSize" format="dimension" />
<attr name="ofAvailableSpace" format="float" />
<attr name="ofRemainderSpace" format="float" />
+ <attr name="matchWorkspace" format="boolean" />
+ <attr name="maxSize" format="dimension" />
</declare-styleable>
+ <declare-styleable name="FolderSpec">
+ <attr name="specType" />
+ <attr name="maxAvailableSize" />
+ </declare-styleable>
+
+ <declare-styleable name="AllAppsSpec">
+ <attr name="specType" />
+ <attr name="maxAvailableSize" />
+ </declare-styleable>
</resources>
diff --git a/tests/res/xml/invalid_all_apps_file_case_1.xml b/tests/res/xml/invalid_all_apps_file_case_1.xml
new file mode 100644
index 0000000..6fd35b1
--- /dev/null
+++ b/tests/res/xml/invalid_all_apps_file_case_1.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<allAppsSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto">
+ <allAppsSpec
+ launcher:specType="height"
+ launcher:maxAvailableSize="9999dp">
+ <!-- missing startPadding -->
+ <endPadding launcher:fixedSize="0dp" />
+ <gutter launcher:matchWorkspace="true" />
+ <cellSize launcher:matchWorkspace="true" />
+ </allAppsSpec>
+
+ <allAppsSpec
+ launcher:specType="width"
+ launcher:maxAvailableSize="9999dp">
+ <startPadding launcher:matchWorkspace="true" />
+ <endPadding launcher:matchWorkspace="true" />
+ <gutter launcher:matchWorkspace="true" />
+ <cellSize launcher:matchWorkspace="true" />
+ </allAppsSpec>
+
+</allAppsSpecs>
+
diff --git a/tests/res/xml/invalid_all_apps_file_case_2.xml b/tests/res/xml/invalid_all_apps_file_case_2.xml
new file mode 100644
index 0000000..de9c1ac
--- /dev/null
+++ b/tests/res/xml/invalid_all_apps_file_case_2.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<allAppsSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto">
+ <allAppsSpec
+ launcher:specType="height"
+ launcher:maxAvailableSize="9999dp">
+ <startPadding launcher:fixedSize="0dp" />
+ <endPadding launcher:fixedSize="0dp" />
+ <!-- more than 1 value in one tag -->
+ <gutter
+ launcher:matchWorkspace="true"
+ launcher:fixedSize="16dp" />
+ <cellSize launcher:matchWorkspace="true" />
+ </allAppsSpec>
+
+ <allAppsSpec
+ launcher:specType="width"
+ launcher:maxAvailableSize="9999dp">
+ <startPadding launcher:matchWorkspace="true" />
+ <endPadding launcher:matchWorkspace="true" />
+ <gutter launcher:matchWorkspace="true" />
+ <cellSize launcher:matchWorkspace="true" />
+ </allAppsSpec>
+
+</allAppsSpecs>
\ No newline at end of file
diff --git a/tests/res/xml/invalid_all_apps_file_case_3.xml b/tests/res/xml/invalid_all_apps_file_case_3.xml
new file mode 100644
index 0000000..7af0af4
--- /dev/null
+++ b/tests/res/xml/invalid_all_apps_file_case_3.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<allAppsSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto">
+ <allAppsSpec
+ launcher:specType="height"
+ launcher:maxAvailableSize="9999dp">
+ <startPadding launcher:fixedSize="0dp" />
+ <endPadding launcher:fixedSize="0dp" />
+ <gutter launcher:matchWorkspace="true" />
+ <!-- value bigger than 1 -->
+ <cellSize launcher:ofRemainderSpace="1.001" />
+ </allAppsSpec>
+
+ <allAppsSpec
+ launcher:specType="width"
+ launcher:maxAvailableSize="9999dp">
+ <startPadding launcher:matchWorkspace="true" />
+ <endPadding launcher:matchWorkspace="true" />
+ <gutter launcher:matchWorkspace="true" />
+ <cellSize launcher:matchWorkspace="true" />
+ </allAppsSpec>
+
+</allAppsSpecs>
+
diff --git a/tests/res/xml/invalid_folders_specs_1.xml b/tests/res/xml/invalid_folders_specs_1.xml
new file mode 100644
index 0000000..0864249
--- /dev/null
+++ b/tests/res/xml/invalid_folders_specs_1.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<!-- Tablet - 6x5 portrait -->
+<folderSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto">
+ <folderSpec launcher:specType="width" launcher:maxAvailableSize="800dp">
+ <!-- missing startPadding -->
+ <endPadding launcher:fixedSize="16dp" />
+ <gutter launcher:fixedSize="16dp" />
+ <cellSize launcher:matchWorkspace="true" />
+ </folderSpec>
+ <folderSpec launcher:specType="width" launcher:maxAvailableSize="9999dp">
+ <startPadding launcher:fixedSize="16dp" />
+ <endPadding launcher:fixedSize="16dp" />
+ <gutter launcher:fixedSize="16dp" />
+ <cellSize launcher:fixedSize="102dp" />
+ </folderSpec>
+
+ <!-- Height spec is fixed -->
+ <folderSpec launcher:specType="height" launcher:maxAvailableSize="9999dp">
+ <startPadding launcher:fixedSize="24dp" />
+ <!-- mapped to footer height size -->
+ <endPadding launcher:fixedSize="64dp" />
+ <gutter launcher:fixedSize="16dp" />
+ <cellSize launcher:fixedSize="104dp" />
+ </folderSpec>
+</folderSpecs>
diff --git a/tests/res/xml/invalid_folders_specs_2.xml b/tests/res/xml/invalid_folders_specs_2.xml
new file mode 100644
index 0000000..0b7dd62
--- /dev/null
+++ b/tests/res/xml/invalid_folders_specs_2.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<!-- Tablet - 6x5 portrait -->
+<folderSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto">
+ <folderSpec launcher:specType="width" launcher:maxAvailableSize="800dp">
+ <startPadding launcher:fixedSize="16dp" />
+ <endPadding launcher:fixedSize="16dp" />
+ <!-- more than 1 value in one tag -->
+ <gutter
+ launcher:ofAvailableSpace="0.0125"
+ launcher:fixedSize="16dp" />
+ <cellSize launcher:matchWorkspace="true" />
+ </folderSpec>
+ <folderSpec launcher:specType="width" launcher:maxAvailableSize="9999dp">
+ <startPadding launcher:fixedSize="16dp" />
+ <endPadding launcher:fixedSize="16dp" />
+ <gutter launcher:fixedSize="16dp" />
+ <cellSize launcher:fixedSize="102dp" />
+ </folderSpec>
+
+ <!-- Height spec is fixed -->
+ <folderSpec launcher:specType="height" launcher:maxAvailableSize="9999dp">
+ <startPadding launcher:fixedSize="24dp" />
+ <!-- mapped to footer height size -->
+ <endPadding launcher:fixedSize="64dp" />
+ <gutter launcher:fixedSize="16dp" />
+ <cellSize launcher:fixedSize="104dp" />
+ </folderSpec>
+</folderSpecs>
diff --git a/tests/res/xml/invalid_folders_specs_3.xml b/tests/res/xml/invalid_folders_specs_3.xml
new file mode 100644
index 0000000..83fd3e1
--- /dev/null
+++ b/tests/res/xml/invalid_folders_specs_3.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<!-- Tablet - 6x5 portrait - More the one value first gutter -->
+<folderSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto">
+ <folderSpec launcher:specType="width" launcher:maxAvailableSize="800dp">
+ <startPadding launcher:fixedSize="16dp" />
+ <endPadding launcher:fixedSize="16dp" />
+ <gutter launcher:fixedSize="16dp" />
+ <!-- value bigger than 1 -->
+ <cellSize launcher:ofRemainderSpace="1.001" />
+ </folderSpec>
+ <folderSpec launcher:specType="width" launcher:maxAvailableSize="9999dp">
+ <startPadding launcher:fixedSize="16dp" />
+ <endPadding launcher:fixedSize="16dp" />
+ <gutter launcher:fixedSize="16dp" />
+ <cellSize launcher:fixedSize="102dp" />
+ </folderSpec>
+
+ <!-- Height spec is fixed -->
+ <folderSpec launcher:specType="height" launcher:maxAvailableSize="9999dp">
+ <startPadding launcher:fixedSize="24dp" />
+ <!-- mapped to footer height size -->
+ <endPadding launcher:fixedSize="64dp" />
+ <gutter launcher:fixedSize="16dp" />
+ <cellSize launcher:fixedSize="104dp" />
+ </folderSpec>
+</folderSpecs>
diff --git a/tests/res/xml/invalid_folders_specs_4.xml b/tests/res/xml/invalid_folders_specs_4.xml
new file mode 100644
index 0000000..2d8c730
--- /dev/null
+++ b/tests/res/xml/invalid_folders_specs_4.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<!-- missing height spec -->
+<folderSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto">
+ <folderSpec launcher:specType="width" launcher:maxAvailableSize="800dp">
+ <startPadding launcher:fixedSize="16dp" />
+ <endPadding launcher:fixedSize="16dp" />
+ <gutter launcher:fixedSize="16dp" />
+ <cellSize launcher:matchWorkspace="true" />
+ </folderSpec>
+</folderSpecs>
diff --git a/tests/res/xml/invalid_folders_specs_5.xml b/tests/res/xml/invalid_folders_specs_5.xml
new file mode 100644
index 0000000..b4f1f4d
--- /dev/null
+++ b/tests/res/xml/invalid_folders_specs_5.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<!-- missing breakpoints > 800dp -->
+<folderSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto">
+ <folderSpec launcher:specType="width" launcher:maxAvailableSize="800dp">
+ <startPadding launcher:fixedSize="16dp" />
+ <endPadding launcher:fixedSize="16dp" />
+ <gutter launcher:fixedSize="16dp" />
+ <cellSize launcher:matchWorkspace="true" />
+ </folderSpec>
+
+ <!-- Height spec is fixed -->
+ <folderSpec launcher:specType="height" launcher:maxAvailableSize="800dp">
+ <startPadding launcher:fixedSize="24dp" />
+ <!-- mapped to footer height size -->
+ <endPadding launcher:fixedSize="64dp" />
+ <gutter launcher:fixedSize="16dp" />
+ <cellSize launcher:fixedSize="104dp" />
+ </folderSpec>
+</folderSpecs>
diff --git a/tests/res/xml/invalid_workspace_file_case_4.xml b/tests/res/xml/invalid_workspace_file_case_4.xml
new file mode 100644
index 0000000..9e74c85
--- /dev/null
+++ b/tests/res/xml/invalid_workspace_file_case_4.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<workspaceSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto">
+ <workspaceSpec
+ launcher:specType="height"
+ launcher:maxAvailableSize="648dp">
+ <startPadding
+ launcher:ofAvailableSpace="0.0125" />
+ <endPadding
+ launcher:ofAvailableSpace="0.05" />
+ <!-- value in workspace spec using matchWorkspace -->
+ <gutter
+ launcher:matchWorkspace="true" />
+ <cellSize
+ launcher:ofRemainderSpace="0.2" />
+ </workspaceSpec>
+
+ <workspaceSpec
+ launcher:specType="height"
+ launcher:maxAvailableSize="9999dp">
+ <startPadding
+ launcher:ofAvailableSpace="0.0306" />
+ <endPadding
+ launcher:ofAvailableSpace="0.068" />
+ <gutter
+ launcher:fixedSize="16dp" />
+ <cellSize
+ launcher:ofRemainderSpace="0.2" />
+ </workspaceSpec>
+
+ <!-- Width spec is always the same -->
+ <workspaceSpec
+ launcher:specType="width"
+ launcher:maxAvailableSize="9999dp">
+ <startPadding
+ launcher:ofRemainderSpace="0.21436227" />
+ <endPadding
+ launcher:ofRemainderSpace="0.21436227" />
+ <gutter
+ launcher:ofRemainderSpace="0.11425509" />
+ <cellSize
+ launcher:fixedSize="120dp" />
+ </workspaceSpec>
+</workspaceSpecs>
diff --git a/tests/res/xml/valid_all_apps_file.xml b/tests/res/xml/valid_all_apps_file.xml
new file mode 100644
index 0000000..0be55d1
--- /dev/null
+++ b/tests/res/xml/valid_all_apps_file.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<allAppsSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto">
+ <allAppsSpec
+ launcher:specType="height"
+ launcher:maxAvailableSize="9999dp">
+ <startPadding launcher:fixedSize="0dp" />
+ <endPadding launcher:fixedSize="0dp" />
+ <gutter launcher:matchWorkspace="true" />
+ <cellSize launcher:matchWorkspace="true" />
+ </allAppsSpec>
+
+ <allAppsSpec
+ launcher:specType="width"
+ launcher:maxAvailableSize="9999dp">
+ <startPadding launcher:matchWorkspace="true" />
+ <endPadding launcher:matchWorkspace="true" />
+ <gutter launcher:matchWorkspace="true" />
+ <cellSize launcher:matchWorkspace="true" />
+ </allAppsSpec>
+
+</allAppsSpecs>
diff --git a/tests/res/xml/valid_folders_specs.xml b/tests/res/xml/valid_folders_specs.xml
new file mode 100644
index 0000000..0c45544
--- /dev/null
+++ b/tests/res/xml/valid_folders_specs.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<folderSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto">
+ <folderSpec launcher:specType="width" launcher:maxAvailableSize="800dp">
+ <startPadding launcher:fixedSize="16dp" />
+ <endPadding launcher:fixedSize="16dp" />
+ <gutter launcher:fixedSize="16dp" />
+ <cellSize launcher:matchWorkspace="true" />
+ </folderSpec>
+ <folderSpec launcher:specType="width" launcher:maxAvailableSize="9999dp">
+ <startPadding launcher:fixedSize="16dp" />
+ <endPadding launcher:fixedSize="16dp" />
+ <gutter launcher:fixedSize="16dp" />
+ <cellSize launcher:fixedSize="102dp" />
+ </folderSpec>
+
+ <!-- Height spec is fixed -->
+ <folderSpec launcher:specType="height" launcher:maxAvailableSize="9999dp">
+ <startPadding launcher:fixedSize="24dp" />
+ <!-- mapped to footer height size -->
+ <endPadding launcher:fixedSize="64dp" />
+ <gutter launcher:fixedSize="16dp" />
+ <cellSize launcher:matchWorkspace="true" />
+ </folderSpec>
+</folderSpecs>
diff --git a/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java b/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java
index 2ac0dd7..8cc01ff 100644
--- a/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java
+++ b/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java
@@ -120,6 +120,7 @@
"get-activities-created-count";
public static final String REQUEST_GET_ACTIVITIES = "get-activities";
public static final String REQUEST_HAS_TIS = "has-touch-interaction-service";
+ public static final String REQUEST_IS_TRACKPAD_GESTURE_ENABLED = "is-trackpad-gesture-enabled";
public static final String REQUEST_TASKBAR_ALL_APPS_TOP_PADDING =
"taskbar-all-apps-top-padding";
public static final String REQUEST_ALL_APPS_TOP_PADDING = "all-apps-top-padding";
diff --git a/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt b/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
index 3de4d55..f0cedd3 100644
--- a/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
+++ b/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
@@ -27,6 +27,10 @@
import com.android.launcher3.util.WindowBounds
import com.android.launcher3.util.window.CachedDisplayInfo
import com.android.launcher3.util.window.WindowManagerProxy
+import java.io.BufferedReader
+import java.io.File
+import java.io.PrintWriter
+import java.io.StringWriter
import kotlin.math.max
import kotlin.math.min
import org.junit.After
@@ -196,7 +200,7 @@
isGestureMode: Boolean,
naturalX: Int,
naturalY: Int
- ): Array<WindowBounds> {
+ ): List<WindowBounds> {
val buttonsNavHeight = Utilities.dpToPx(48f, deviceSpec.densityDpi)
val rotation0Insets =
@@ -231,7 +235,7 @@
if (isGestureMode) deviceSpec.gesturePx else 0
)
- return arrayOf(
+ return listOf(
WindowBounds(Rect(0, 0, naturalX, naturalY), rotation0Insets, Surface.ROTATION_0),
WindowBounds(Rect(0, 0, naturalY, naturalX), rotation90Insets, Surface.ROTATION_90),
WindowBounds(Rect(0, 0, naturalX, naturalY), rotation180Insets, Surface.ROTATION_180),
@@ -243,11 +247,11 @@
deviceSpec: DeviceSpec,
naturalX: Int,
naturalY: Int
- ): Array<WindowBounds> {
+ ): List<WindowBounds> {
val naturalInsets = Rect(0, deviceSpec.statusBarNaturalPx, 0, 0)
val rotatedInsets = Rect(0, deviceSpec.statusBarRotatedPx, 0, 0)
- return arrayOf(
+ return listOf(
WindowBounds(Rect(0, 0, naturalX, naturalY), naturalInsets, Surface.ROTATION_0),
WindowBounds(Rect(0, 0, naturalY, naturalX), rotatedInsets, Surface.ROTATION_90),
WindowBounds(Rect(0, 0, naturalX, naturalY), naturalInsets, Surface.ROTATION_180),
@@ -256,7 +260,7 @@
}
private fun initializeCommonVars(
- perDisplayBoundsCache: Map<CachedDisplayInfo, Array<WindowBounds>>,
+ perDisplayBoundsCache: Map<CachedDisplayInfo, List<WindowBounds>>,
displayInfo: CachedDisplayInfo,
rotation: Int,
isGestureMode: Boolean = true,
@@ -287,4 +291,19 @@
whenever(displayController.info).thenReturn(info)
whenever(displayController.isTransientTaskbar).thenReturn(isGestureMode)
}
+
+ /** Create a new dump of DeviceProfile, saves to a file in the device and returns it */
+ protected fun dump(context: Context, dp: DeviceProfile, fileName: String): String {
+ val stringWriter = StringWriter()
+ PrintWriter(stringWriter).use { dp.dump(context, "", it) }
+ return stringWriter.toString().also { content -> writeToDevice(context, fileName, content) }
+ }
+
+ /** Read a file from assets/ and return it as a string */
+ protected fun readDumpFromAssets(context: Context, fileName: String): String =
+ context.assets.open("dumpTests/$fileName").bufferedReader().use(BufferedReader::readText)
+
+ private fun writeToDevice(context: Context, fileName: String, content: String) {
+ File(context.getDir("dumpTests", Context.MODE_PRIVATE), fileName).writeText(content)
+ }
}
diff --git a/tests/src/com/android/launcher3/celllayout/CellLayoutBoard.java b/tests/src/com/android/launcher3/celllayout/CellLayoutBoard.java
index 3c2b49a..28899d9 100644
--- a/tests/src/com/android/launcher3/celllayout/CellLayoutBoard.java
+++ b/tests/src/com/android/launcher3/celllayout/CellLayoutBoard.java
@@ -103,6 +103,8 @@
public static final char IGNORE = 'x';
// The cells marked by this will be filled by app icons
public static final char ICON = 'i';
+ // The cells marked by FOLDER will be filled by folders with 27 app icons inside
+ public static final char FOLDER = 'Z';
// Empty space
public static final char EMPTY = '-';
// Widget that will be saved as "main widget" for easier retrieval
@@ -171,6 +173,25 @@
}
}
+ public static class FolderPoint {
+ public Point coord;
+ public char mType;
+
+ public FolderPoint(Point coord, char type) {
+ this.coord = coord;
+ mType = type;
+ }
+
+ /**
+ * [A-Z]: Represents a folder and number of icons in the folder is represented by
+ * the order of letter in the alphabet, A=2, B=3, C=4 ... etc.
+ */
+ public int getNumberIconsInside() {
+ return (mType - 'A') + 2;
+ }
+ }
+
+
private HashSet<Character> mUsedWidgetTypes = new HashSet<>();
static final int INFINITE = 99999;
@@ -181,6 +202,7 @@
Map<Character, WidgetRect> mWidgetsMap = new HashMap<>();
List<IconPoint> mIconPoints = new ArrayList<>();
+ List<FolderPoint> mFolderPoints = new ArrayList<>();
WidgetRect mMain = null;
@@ -213,6 +235,10 @@
return mIconPoints;
}
+ public List<FolderPoint> getFolders() {
+ return mFolderPoints;
+ }
+
public WidgetRect getMain() {
return mMain;
}
@@ -248,6 +274,17 @@
}
return true;
}).collect(Collectors.toList());
+
+ // Remove overlapping folders and remove them from the board
+ mFolderPoints = mFolderPoints.stream().filter(folderPoint -> {
+ int x = folderPoint.coord.x;
+ int y = folderPoint.coord.y;
+ if (rect.contains(x, y)) {
+ mWidget[x][y] = '-';
+ return false;
+ }
+ return true;
+ }).collect(Collectors.toList());
}
private void removeOverlappingItems(Point p) {
@@ -269,6 +306,17 @@
}
return true;
}).collect(Collectors.toList());
+
+ // Remove overlapping folders and remove them from the board
+ mFolderPoints = mFolderPoints.stream().filter(folderPoint -> {
+ int x = folderPoint.coord.x;
+ int y = folderPoint.coord.y;
+ if (p.x == x && p.y == y) {
+ mWidget[x][y] = '-';
+ return false;
+ }
+ return true;
+ }).collect(Collectors.toList());
}
private char getNextWidgetType() {
@@ -373,6 +421,18 @@
return iconPoints;
}
+ private static List<FolderPoint> getFolderPoints(char[][] board) {
+ List<FolderPoint> folderPoints = new ArrayList<>();
+ for (int x = 0; x < board.length; x++) {
+ for (int y = 0; y < board[0].length; y++) {
+ if (isFolder(board[x][y])) {
+ folderPoints.add(new FolderPoint(new Point(x, y), board[x][y]));
+ }
+ }
+ }
+ return folderPoints;
+ }
+
public static WidgetRect getMainFromList(List<CellLayoutBoard> boards) {
for (CellLayoutBoard board : boards) {
WidgetRect main = board.getMain();
@@ -406,6 +466,7 @@
board.mWidgetsMap.put(widgetRect.mType, widgetRect);
});
board.mIconPoints = getIconPoints(board.mWidget);
+ board.mFolderPoints = getFolderPoints(board.mWidget);
return board;
}
diff --git a/tests/src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java b/tests/src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java
index 8ce932d..bf7a21c 100644
--- a/tests/src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java
+++ b/tests/src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java
@@ -15,62 +15,116 @@
*/
package com.android.launcher3.celllayout;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+import static com.android.launcher3.util.TestUtil.runOnExecutorSync;
-import android.content.ContentResolver;
-import android.content.ContentValues;
import android.content.Context;
+import androidx.test.uiautomator.UiDevice;
+
import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.model.BgDataModel.Callbacks;
+import com.android.launcher3.model.ModelDbController;
+import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.ui.AbstractLauncherUiTest;
+import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
+import com.android.launcher3.tapl.LauncherInstrumentation;
import com.android.launcher3.util.ContentWriter;
import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.ExecutionException;
+import java.util.function.Supplier;
public class FavoriteItemsTransaction {
- private ArrayList<ItemInfo> mItemsToSubmit;
+ private ArrayList<Supplier<ItemInfo>> mItemsToSubmit;
private Context mContext;
- private ContentResolver mResolver;
- public AbstractLauncherUiTest mTest;
- public FavoriteItemsTransaction(Context context, AbstractLauncherUiTest test) {
+ public FavoriteItemsTransaction(Context context) {
mItemsToSubmit = new ArrayList<>();
mContext = context;
- mResolver = mContext.getContentResolver();
- mTest = test;
}
- public FavoriteItemsTransaction addItem(ItemInfo itemInfo) {
+ public FavoriteItemsTransaction addItem(Supplier<ItemInfo> itemInfo) {
this.mItemsToSubmit.add(itemInfo);
return this;
}
- public FavoriteItemsTransaction removeLast() {
- this.mItemsToSubmit.remove(this.mItemsToSubmit.size() - 1);
- return this;
- }
-
/**
* Commits all the ItemInfo into the database of Favorites
**/
- public void commit() throws ExecutionException, InterruptedException {
- List<ContentValues> values = new ArrayList<>();
- for (ItemInfo item : this.mItemsToSubmit) {
- ContentWriter writer = new ContentWriter(mContext);
- item.onAddToDatabase(writer);
- writer.put(LauncherSettings.Favorites._ID, item.id);
- values.add(writer.getValues(mContext));
- }
- // Submit the icons to the database in the model thread to prevent race conditions
- MODEL_EXECUTOR.submit(() -> mResolver.bulkInsert(LauncherSettings.Favorites.CONTENT_URI,
- values.toArray(new ContentValues[0]))).get();
- // Reload the state of the Launcher
- MAIN_EXECUTOR.submit(() -> LauncherAppState.getInstance(
- mContext).getModel().forceReload()).get();
+ public void commit() {
+ LauncherModel model = LauncherAppState.getInstance(mContext).getModel();
+ // Load the model once so that there is no pending migration:
+ loadModelSync(model);
+
+ runOnExecutorSync(MODEL_EXECUTOR, () -> {
+ ModelDbController controller = model.getModelDbController();
+ // Migrate any previous data so that the DB state is correct
+ controller.tryMigrateDB();
+
+ // Create DB again to load fresh data
+ controller.createEmptyDB();
+ controller.clearEmptyDbFlag();
+
+ // Add new data
+ try (SQLiteTransaction transaction = controller.newTransaction()) {
+ int count = mItemsToSubmit.size();
+ ArrayList<ItemInfo> containerItems = new ArrayList<>();
+ for (int i = 0; i < count; i++) {
+ ContentWriter writer = new ContentWriter(mContext);
+ ItemInfo item = mItemsToSubmit.get(i).get();
+
+ if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
+ FolderInfo folderInfo = (FolderInfo) item;
+ for (ItemInfo itemInfo : folderInfo.contents) {
+ itemInfo.container = i;
+ containerItems.add(itemInfo);
+ }
+ }
+
+ item.onAddToDatabase(writer);
+ writer.put(LauncherSettings.Favorites._ID, i);
+ controller.insert(TABLE_NAME, writer.getValues(mContext));
+ }
+
+ for (int i = 0; i < containerItems.size(); i++) {
+ ContentWriter writer = new ContentWriter(mContext);
+ ItemInfo item = containerItems.get(i);
+ item.onAddToDatabase(writer);
+ writer.put(LauncherSettings.Favorites._ID, count + i);
+ controller.insert(TABLE_NAME, writer.getValues(mContext));
+ }
+ transaction.commit();
+ }
+ });
+
+ // Reload model
+ runOnExecutorSync(MAIN_EXECUTOR, model::forceReload);
+ loadModelSync(model);
+ }
+
+ private void loadModelSync(LauncherModel model) {
+ Callbacks mockCb = new Callbacks() { };
+ runOnExecutorSync(MAIN_EXECUTOR, () -> model.addCallbacksAndLoad(mockCb));
+ runOnExecutorSync(MODEL_EXECUTOR, () -> { });
+
+ runOnExecutorSync(MAIN_EXECUTOR, () -> { });
+ runOnExecutorSync(MAIN_EXECUTOR, () -> model.removeCallbacks(mockCb));
+ }
+
+ /**
+ * Commits the transaction and waits for home load
+ */
+ public void commitAndLoadHome(LauncherInstrumentation inst) {
+ commit();
+
+ // Launch the home activity
+ UiDevice.getInstance(getInstrumentation()).pressHome();
+ inst.waitForLauncherInitialized();
}
}
diff --git a/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java b/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java
index 243edc3..7ec78bb 100644
--- a/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java
+++ b/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java
@@ -59,9 +59,8 @@
@Before
public void setup() throws Throwable {
- mWorkspaceBuilder = new TestWorkspaceBuilder(this, mTargetContext);
+ mWorkspaceBuilder = new TestWorkspaceBuilder(mTargetContext);
TaplTestsLauncher3.initialize(this);
- clearHomescreen();
}
/**
@@ -108,13 +107,13 @@
testCase.mStart);
FavoriteItemsTransaction transaction =
- new FavoriteItemsTransaction(mTargetContext, this);
+ new FavoriteItemsTransaction(mTargetContext);
transaction = buildWorkspaceFromBoards(testCase.mStart, transaction);
transaction.commit();
+ mLauncher.waitForLauncherInitialized();
// resetLoaderState triggers the launcher to start loading the workspace which allows
// waitForLauncherCondition to wait for that condition, otherwise the condition would
// always be true and it wouldn't wait for the changes to be applied.
- resetLoaderState();
waitForLauncherCondition("Workspace didn't finish loading", l -> !l.isWorkspaceLoading());
Widget widget = mLauncher.getWorkspace().getWidgetAtCell(mainWidgetCellPos.getCellX(),
mainWidgetCellPos.getCellY());
diff --git a/tests/src/com/android/launcher3/celllayout/TestWorkspaceBuilder.java b/tests/src/com/android/launcher3/celllayout/TestWorkspaceBuilder.java
index 5945605..398bd82 100644
--- a/tests/src/com/android/launcher3/celllayout/TestWorkspaceBuilder.java
+++ b/tests/src/com/android/launcher3/celllayout/TestWorkspaceBuilder.java
@@ -15,6 +15,9 @@
*/
package com.android.launcher3.celllayout;
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+
+import static com.android.launcher3.ui.TestViewHelpers.findWidgetProvider;
import static com.android.launcher3.util.WidgetUtils.createWidgetInfo;
import android.content.ComponentName;
@@ -25,33 +28,30 @@
import android.os.UserHandle;
import android.util.Log;
-import androidx.test.core.app.ApplicationProvider;
-
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
-import com.android.launcher3.ui.AbstractLauncherUiTest;
-import com.android.launcher3.ui.TestViewHelpers;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
+import java.util.function.Supplier;
+import java.util.stream.IntStream;
+
public class TestWorkspaceBuilder {
private static final String TAG = "CellLayoutBoardBuilder";
private static final ComponentName APP_COMPONENT_NAME = new ComponentName(
"com.google.android.calculator", "com.android.calculator2.Calculator");
- public AbstractLauncherUiTest mTest;
-
private UserHandle mMyUser;
private Context mContext;
private ContentResolver mResolver;
- public TestWorkspaceBuilder(AbstractLauncherUiTest test, Context context) {
- mTest = test;
+ public TestWorkspaceBuilder(Context context) {
mMyUser = Process.myUserHandle();
mContext = context;
mResolver = mContext.getContentResolver();
@@ -68,10 +68,9 @@
for (int y = initY; y < initY + widgetRect.getSpanY(); y++) {
try {
// this widgets are filling, we don't care if we can't place them
- ItemInfo item = createWidgetInCell(
+ transaction.addItem(createWidgetInCell(
new CellLayoutBoard.WidgetRect(CellLayoutBoard.CellType.IGNORE,
- new Rect(x, y, x, y)), screenId);
- transaction.addItem(item);
+ new Rect(x, y, x, y)), screenId));
} catch (Exception e) {
Log.d(TAG, "Unable to place filling widget at " + x + "," + y);
}
@@ -80,12 +79,6 @@
return transaction;
}
- private int getID() {
- return LauncherSettings.Settings.call(
- mResolver, LauncherSettings.Settings.METHOD_NEW_ITEM_ID)
- .getInt(LauncherSettings.Settings.EXTRA_VALUE);
- }
-
private AppInfo getApp() {
return new AppInfo(APP_COMPONENT_NAME, "test icon", mMyUser,
AppInfo.makeLaunchIntent(APP_COMPONENT_NAME));
@@ -108,7 +101,10 @@
board.getWidgets().forEach(
(widgetRect) -> addCorrespondingWidgetRect(widgetRect, transaction, screenId));
board.getIcons().forEach((iconPoint) ->
- transaction.addItem(createIconInCell(iconPoint, screenId))
+ transaction.addItem(() -> createIconInCell(iconPoint, screenId))
+ );
+ board.getFolders().forEach((folderPoint) ->
+ transaction.addItem(() -> createFolderInCell(folderPoint, screenId))
);
return transaction;
}
@@ -118,29 +114,52 @@
* be clean otherwise this doesn't overrides the existing icons.
*/
public FavoriteItemsTransaction fillHotseatIcons(FavoriteItemsTransaction transaction) {
- int hotseatCount = InvariantDeviceProfile.INSTANCE.get(mContext).numDatabaseHotseatIcons;
- for (int i = 0; i < hotseatCount; i++) {
- transaction.addItem(getHotseatValues(i));
- }
+ IntStream.range(0, InvariantDeviceProfile.INSTANCE.get(mContext).numDatabaseHotseatIcons)
+ .forEach(i -> transaction.addItem(() -> getHotseatValues(i)));
return transaction;
}
- private ItemInfo createWidgetInCell(CellLayoutBoard.WidgetRect widgetRect, int screenId) {
- LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(mTest, false);
- LauncherAppWidgetInfo item = createWidgetInfo(info,
- ApplicationProvider.getApplicationContext(), true);
- item.id = getID();
- item.cellX = widgetRect.getCellX();
- item.cellY = widgetRect.getCellY();
- item.spanX = widgetRect.getSpanX();
- item.spanY = widgetRect.getSpanY();
+ private Supplier<ItemInfo> createWidgetInCell(
+ CellLayoutBoard.WidgetRect widgetRect, int screenId) {
+ // Create the widget lazily since the appWidgetId can get lost during setup
+ return () -> {
+ LauncherAppWidgetProviderInfo info = findWidgetProvider(false);
+ LauncherAppWidgetInfo item = createWidgetInfo(info, getApplicationContext(), true);
+ item.cellX = widgetRect.getCellX();
+ item.cellY = widgetRect.getCellY();
+ item.spanX = widgetRect.getSpanX();
+ item.spanY = widgetRect.getSpanY();
+ item.screenId = screenId;
+ return item;
+ };
+ }
+
+ public FolderInfo createFolderInCell(CellLayoutBoard.FolderPoint folderPoint, int screenId) {
+ FolderInfo folderInfo = new FolderInfo();
+ folderInfo.screenId = screenId;
+ folderInfo.container = LauncherSettings.Favorites.CONTAINER_DESKTOP;
+ folderInfo.cellX = folderPoint.coord.x;
+ folderInfo.cellY = folderPoint.coord.y;
+ folderInfo.minSpanY = folderInfo.minSpanX = folderInfo.spanX = folderInfo.spanY = 1;
+ folderInfo.setOption(FolderInfo.FLAG_MULTI_PAGE_ANIMATION, true, null);
+
+ for (int i = 0; i < folderPoint.getNumberIconsInside(); i++) {
+ folderInfo.add(getDefaultWorkspaceItem(screenId), false);
+ }
+
+ return folderInfo;
+ }
+
+ private WorkspaceItemInfo getDefaultWorkspaceItem(int screenId) {
+ WorkspaceItemInfo item = new WorkspaceItemInfo(getApp());
item.screenId = screenId;
+ item.minSpanY = item.minSpanX = item.spanX = item.spanY = 1;
+ item.container = LauncherSettings.Favorites.CONTAINER_DESKTOP;
return item;
}
private ItemInfo createIconInCell(CellLayoutBoard.IconPoint iconPoint, int screenId) {
WorkspaceItemInfo item = new WorkspaceItemInfo(getApp());
- item.id = getID();
item.screenId = screenId;
item.cellX = iconPoint.getCoord().x;
item.cellY = iconPoint.getCoord().y;
@@ -151,7 +170,6 @@
private ItemInfo getHotseatValues(int x) {
WorkspaceItemInfo item = new WorkspaceItemInfo(getApp());
- item.id = getID();
item.cellX = x;
item.cellY = 0;
item.minSpanY = item.minSpanX = item.spanX = item.spanY = 1;
diff --git a/tests/src/com/android/launcher3/model/AbstractWorkspaceModelTest.kt b/tests/src/com/android/launcher3/model/AbstractWorkspaceModelTest.kt
index 03352fe..98191fe 100644
--- a/tests/src/com/android/launcher3/model/AbstractWorkspaceModelTest.kt
+++ b/tests/src/com/android/launcher3/model/AbstractWorkspaceModelTest.kt
@@ -17,17 +17,18 @@
import android.content.ComponentName
import android.content.Context
-import android.content.Intent
import android.graphics.Rect
import com.android.launcher3.InvariantDeviceProfile
import com.android.launcher3.LauncherAppState
-import com.android.launcher3.LauncherSettings
+import com.android.launcher3.model.data.AppInfo
import com.android.launcher3.model.data.WorkspaceItemInfo
-import com.android.launcher3.util.ContentWriter
import com.android.launcher3.util.GridOccupancy
import com.android.launcher3.util.IntArray
import com.android.launcher3.util.IntSparseArrayMap
+import com.android.launcher3.util.LauncherLayoutBuilder
import com.android.launcher3.util.LauncherModelHelper
+import com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY
+import com.android.launcher3.util.LauncherModelHelper.TEST_PACKAGE
import java.util.UUID
/** Base class for workspace related tests. */
@@ -38,6 +39,7 @@
val nonEmptyScreenSpaces = listOf(Rect(1, 2, 3, 4))
}
+ protected lateinit var mLayoutBuilder: LauncherLayoutBuilder
protected lateinit var mTargetContext: Context
protected lateinit var mIdp: InvariantDeviceProfile
protected lateinit var mAppState: LauncherAppState
@@ -47,6 +49,7 @@
protected lateinit var mScreenOccupancy: IntSparseArrayMap<GridOccupancy>
open fun setup() {
+ mLayoutBuilder = LauncherLayoutBuilder()
mModelHelper = LauncherModelHelper()
mTargetContext = mModelHelper.sandboxContext
mIdp = InvariantDeviceProfile.INSTANCE[mTargetContext]
@@ -64,10 +67,11 @@
/** Sets up workspaces with the given screen IDs with some items and a 2x2 space. */
fun setupWorkspaces(screenIdsWithItems: List<Int>) {
- var nextItemId = 1
- screenIdsWithItems.forEach { screenId ->
- nextItemId = setupWorkspace(nextItemId, screenId, nonEmptyScreenSpaces)
- }
+ screenIdsWithItems.forEach { screenId -> setupWorkspace(screenId, nonEmptyScreenSpaces) }
+ mModelHelper.setupDefaultLayoutProvider(mLayoutBuilder)
+ mIdp.numRows = 5
+ mIdp.numColumns = mIdp.numRows
+ mModelHelper.loadModelSync()
}
/**
@@ -78,30 +82,23 @@
screen1: List<Rect>? = null,
screen2: List<Rect>? = null,
screen3: List<Rect>? = null,
- ) = listOf(screen0, screen1, screen2, screen3).let(this::setupWithSpaces)
+ ) {
+ listOf(screen0, screen1, screen2, screen3).let(this::setupWithSpaces)
+ mModelHelper.setupDefaultLayoutProvider(mLayoutBuilder)
+ mIdp.numRows = 5
+ mIdp.numColumns = mIdp.numRows
+ mModelHelper.loadModelSync()
+ }
private fun setupWithSpaces(workspaceSpaces: List<List<Rect>?>) {
- var nextItemId = 1
workspaceSpaces.forEachIndexed { screenId, spaces ->
if (spaces != null) {
- nextItemId = setupWorkspace(nextItemId, screenId, spaces)
+ setupWorkspace(screenId, spaces)
}
}
}
- private fun setupWorkspace(startId: Int, screenId: Int, spaces: List<Rect>): Int {
- return mModelHelper.executeSimpleTask { dataModel ->
- writeWorkspaceWithSpaces(dataModel, startId, screenId, spaces)
- }
- }
-
- private fun writeWorkspaceWithSpaces(
- bgDataModel: BgDataModel,
- itemStartId: Int,
- screenId: Int,
- spaces: List<Rect>,
- ): Int {
- var itemId = itemStartId
+ private fun setupWorkspace(screenId: Int, spaces: List<Rect>) {
val occupancy = GridOccupancy(mIdp.numColumns, mIdp.numRows)
occupancy.markCells(0, 0, mIdp.numColumns, mIdp.numRows, true)
spaces.forEach { spaceRect -> occupancy.markCells(spaceRect, false) }
@@ -109,35 +106,22 @@
mScreenOccupancy.append(screenId, occupancy)
for (x in 0 until mIdp.numColumns) {
for (y in 0 until mIdp.numRows) {
- if (!occupancy.cells[x][y]) {
- continue
+ if (occupancy.cells[x][y]) {
+ mLayoutBuilder.atWorkspace(x, y, screenId).putApp(TEST_PACKAGE, TEST_ACTIVITY)
}
- val info = getExistingItem()
- info.id = itemId++
- info.screenId = screenId
- info.cellX = x
- info.cellY = y
- info.container = LauncherSettings.Favorites.CONTAINER_DESKTOP
- bgDataModel.addItem(mTargetContext, info, false)
- val writer = ContentWriter(mTargetContext)
- info.writeToValues(writer)
- writer.put(LauncherSettings.Favorites._ID, info.id)
- mTargetContext.contentResolver.insert(
- LauncherSettings.Favorites.CONTENT_URI,
- writer.getValues(mTargetContext)
- )
}
}
- return itemId
}
fun getExistingItem() =
- WorkspaceItemInfo().apply { intent = Intent().setComponent(ComponentName("a", "b")) }
+ WorkspaceItemInfo().apply {
+ intent = AppInfo.makeLaunchIntent(ComponentName(TEST_PACKAGE, TEST_ACTIVITY))
+ }
fun getNewItem(): WorkspaceItemInfo {
val itemPackage = UUID.randomUUID().toString()
return WorkspaceItemInfo().apply {
- intent = Intent().setComponent(ComponentName(itemPackage, itemPackage))
+ intent = AppInfo.makeLaunchIntent(ComponentName(itemPackage, itemPackage))
}
}
}
diff --git a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt
index 6636b8a..1155227 100644
--- a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt
+++ b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt
@@ -23,7 +23,7 @@
import com.android.launcher3.model.data.WorkspaceItemInfo
import com.android.launcher3.util.Executors
import com.android.launcher3.util.IntArray
-import com.android.launcher3.util.IntSet
+import com.android.launcher3.util.TestUtil.runOnExecutorSync
import com.android.launcher3.util.any
import com.android.launcher3.util.eq
import com.android.launcher3.util.same
@@ -32,8 +32,6 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.ArgumentCaptor
-import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
@@ -46,11 +44,7 @@
@RunWith(AndroidJUnit4::class)
class AddWorkspaceItemsTaskTest : AbstractWorkspaceModelTest() {
- @Captor private lateinit var mAnimatedItemArgumentCaptor: ArgumentCaptor<ArrayList<ItemInfo>>
-
- @Captor private lateinit var mNotAnimatedItemArgumentCaptor: ArgumentCaptor<ArrayList<ItemInfo>>
-
- @Mock private lateinit var mDataModelCallbacks: BgDataModel.Callbacks
+ private lateinit var mDataModelCallbacks: MyCallbacks
@Mock private lateinit var mWorkspaceItemSpaceFinder: WorkspaceItemSpaceFinder
@@ -58,7 +52,7 @@
override fun setup() {
super.setup()
MockitoAnnotations.initMocks(this)
- whenever(mDataModelCallbacks.getPagesToBindSynchronously(any())).thenReturn(IntSet())
+ mDataModelCallbacks = MyCallbacks()
Executors.MAIN_EXECUTOR.submit { mModelHelper.model.addCallbacks(mDataModelCallbacks) }
.get()
}
@@ -105,7 +99,7 @@
val addedItems = testAddItems(nonEmptyScreenIds, itemToAdd)
assertThat(addedItems.size).isEqualTo(0)
- verifyZeroInteractions(mWorkspaceItemSpaceFinder, mDataModelCallbacks)
+ verifyZeroInteractions(mWorkspaceItemSpaceFinder)
}
@Test
@@ -191,22 +185,14 @@
): List<AddedItem> {
setupWorkspaces(nonEmptyScreenIds)
val task = newTask(*itemsToAdd)
- var updateCount = 0
- mModelHelper.executeTaskForTest(task).forEach {
- updateCount++
- it.run()
- }
val addedItems = mutableListOf<AddedItem>()
- if (updateCount > 0) {
- verify(mDataModelCallbacks)
- .bindAppsAdded(
- any(),
- mNotAnimatedItemArgumentCaptor.capture(),
- mAnimatedItemArgumentCaptor.capture()
- )
- addedItems.addAll(mAnimatedItemArgumentCaptor.value.map { AddedItem(it, true) })
- addedItems.addAll(mNotAnimatedItemArgumentCaptor.value.map { AddedItem(it, false) })
+
+ runOnExecutorSync(Executors.MODEL_EXECUTOR) {
+ mDataModelCallbacks.addedItems.clear()
+ mModelHelper.model.enqueueModelUpdateTask(task)
+ runOnExecutorSync(Executors.MAIN_EXECUTOR) {}
+ addedItems.addAll(mDataModelCallbacks.addedItems)
}
return addedItems
@@ -224,3 +210,17 @@
}
private data class AddedItem(val itemInfo: ItemInfo, val isAnimated: Boolean)
+
+private class MyCallbacks : BgDataModel.Callbacks {
+
+ val addedItems = mutableListOf<AddedItem>()
+
+ override fun bindAppsAdded(
+ newScreens: IntArray?,
+ addNotAnimated: ArrayList<ItemInfo>,
+ addAnimated: ArrayList<ItemInfo>
+ ) {
+ addedItems.addAll(addAnimated.map { AddedItem(it, true) })
+ addedItems.addAll(addNotAnimated.map { AddedItem(it, false) })
+ }
+}
diff --git a/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java b/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
index f55b244..f771052 100644
--- a/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
+++ b/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
@@ -1,32 +1,31 @@
package com.android.launcher3.model;
+import static android.os.Process.myUserHandle;
+
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+import static com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY;
+import static com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY2;
+import static com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY3;
+import static com.android.launcher3.util.LauncherModelHelper.TEST_PACKAGE;
+import static com.android.launcher3.util.TestUtil.runOnExecutorSync;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertTrue;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.pm.PackageInfo;
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.Config;
-import android.graphics.Color;
-import android.os.Process;
-import android.os.UserHandle;
-import android.os.UserManager;
-import androidx.annotation.NonNull;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.icons.BitmapInfo;
-import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.icons.cache.CachingLogic;
-import com.android.launcher3.model.data.AppInfo;
-import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.util.IntSet;
+import com.android.launcher3.util.LauncherLayoutBuilder;
import com.android.launcher3.util.LauncherModelHelper;
+import com.android.launcher3.util.PackageUserKey;
import org.junit.After;
import org.junit.Before;
@@ -35,6 +34,7 @@
import java.util.Arrays;
import java.util.HashSet;
+import java.util.List;
/**
* Tests for {@link CacheDataUpdatedTask}
@@ -43,49 +43,40 @@
@RunWith(AndroidJUnit4.class)
public class CacheDataUpdatedTaskTest {
- private static final String NEW_LABEL_PREFIX = "new-label-";
+ private static final String PENDING_APP_1 = TEST_PACKAGE + ".pending1";
+ private static final String PENDING_APP_2 = TEST_PACKAGE + ".pending2";
private LauncherModelHelper mModelHelper;
+ private Context mContext;
+
+ private int mSession1;
@Before
public void setup() throws Exception {
mModelHelper = new LauncherModelHelper();
- mModelHelper.initializeData("cache_data_updated_task_data");
+ mContext = mModelHelper.sandboxContext;
+ mSession1 = mModelHelper.createInstallerSession(PENDING_APP_1);
+ mModelHelper.createInstallerSession(PENDING_APP_2);
- // Add placeholder entries in the cache to simulate update
- Context context = mModelHelper.sandboxContext;
- IconCache iconCache = LauncherAppState.getInstance(context).getIconCache();
- CachingLogic<ItemInfo> placeholderLogic = new CachingLogic<ItemInfo>() {
- @Override
- @NonNull
- public ComponentName getComponent(@NonNull ItemInfo info) {
- return info.getTargetComponent();
- }
+ LauncherLayoutBuilder builder = new LauncherLayoutBuilder()
+ .atHotseat(1).putFolder("MyFolder")
+ .addApp(TEST_PACKAGE, TEST_ACTIVITY) // 2
+ .addApp(TEST_PACKAGE, TEST_ACTIVITY2) // 3
+ .addApp(TEST_PACKAGE, TEST_ACTIVITY3) // 4
- @NonNull
- @Override
- public UserHandle getUser(@NonNull ItemInfo info) {
- return info.user;
- }
+ // Pending App 1
+ .addApp(PENDING_APP_1, TEST_ACTIVITY) // 5
+ .addApp(PENDING_APP_1, TEST_ACTIVITY2) // 6
+ .addApp(PENDING_APP_1, TEST_ACTIVITY3) // 7
- @NonNull
- @Override
- public CharSequence getLabel(@NonNull ItemInfo info) {
- return NEW_LABEL_PREFIX + info.id;
- }
-
- @NonNull
- @Override
- public BitmapInfo loadIcon(@NonNull Context context, @NonNull ItemInfo info) {
- return BitmapInfo.of(Bitmap.createBitmap(1, 1, Config.ARGB_8888), Color.RED);
- }
- };
-
- UserManager um = context.getSystemService(UserManager.class);
- for (ItemInfo info : mModelHelper.getBgDataModel().itemsIdMap) {
- iconCache.addIconToDBAndMemCache(info, placeholderLogic, new PackageInfo(),
- um.getSerialNumberForUser(info.user), true);
- }
+ // Pending App 2
+ .addApp(PENDING_APP_2, TEST_ACTIVITY) // 8
+ .addApp(PENDING_APP_2, TEST_ACTIVITY2) // 9
+ .addApp(PENDING_APP_2, TEST_ACTIVITY3) // 10
+ .build();
+ mModelHelper.setupDefaultLayoutProvider(builder);
+ mModelHelper.loadModelSync();
+ assertEquals(10, mModelHelper.getBgDataModel().itemsIdMap.size());
}
@After
@@ -94,27 +85,63 @@
}
private CacheDataUpdatedTask newTask(int op, String... pkg) {
- return new CacheDataUpdatedTask(op, Process.myUserHandle(),
+ return new CacheDataUpdatedTask(op, myUserHandle(),
new HashSet<>(Arrays.asList(pkg)));
}
@Test
- public void testCacheUpdate_update_apps() throws Exception {
- // Clear all icons from apps list so that its easy to check what was updated
- for (AppInfo info : mModelHelper.getAllAppsList().data) {
- info.bitmap = BitmapInfo.LOW_RES_INFO;
- }
+ public void testCacheUpdate_update_apps() {
+ // Run on model executor so that no other task runs in the middle.
+ runOnExecutorSync(MODEL_EXECUTOR, () -> {
+ // Clear all icons from apps list so that its easy to check what was updated
+ allItems().forEach(wi -> wi.bitmap = BitmapInfo.LOW_RES_INFO);
- mModelHelper.executeTaskForTest(newTask(CacheDataUpdatedTask.OP_CACHE_UPDATE, "app1"));
+ mModelHelper.getModel().enqueueModelUpdateTask(
+ newTask(CacheDataUpdatedTask.OP_CACHE_UPDATE, TEST_PACKAGE));
- // Verify that only the app icons of app1 (id 1 & 2) are updated. Custom shortcut (id 7)
- // is not updated
- verifyUpdate(1, 2);
+ // Verify that only the app icons of TEST_PACKAGE (id 2, 3, 4) are updated.
+ verifyUpdate(2, 3, 4);
+ });
+ }
- // Verify that only app1 var updated in allAppsList
- assertFalse(mModelHelper.getAllAppsList().data.isEmpty());
- for (AppInfo info : mModelHelper.getAllAppsList().data) {
- if (info.componentName.getPackageName().equals("app1")) {
+ @Test
+ public void testSessionUpdate_ignores_normal_apps() {
+ // Run on model executor so that no other task runs in the middle.
+ runOnExecutorSync(MODEL_EXECUTOR, () -> {
+ // Clear all icons from apps list so that its easy to check what was updated
+ allItems().forEach(wi -> wi.bitmap = BitmapInfo.LOW_RES_INFO);
+
+ mModelHelper.getModel().enqueueModelUpdateTask(
+ newTask(CacheDataUpdatedTask.OP_SESSION_UPDATE, TEST_PACKAGE));
+
+ // TEST_PACKAGE has no restored shortcuts. Verify that nothing was updated.
+ verifyUpdate();
+ });
+ }
+
+ @Test
+ public void testSessionUpdate_updates_pending_apps() {
+ // Run on model executor so that no other task runs in the middle.
+ runOnExecutorSync(MODEL_EXECUTOR, () -> {
+ LauncherAppState.getInstance(mContext).getIconCache().updateSessionCache(
+ new PackageUserKey(PENDING_APP_1, myUserHandle()),
+ mContext.getPackageManager().getPackageInstaller().getSessionInfo(mSession1));
+
+ // Clear all icons from apps list so that its easy to check what was updated
+ allItems().forEach(wi -> wi.bitmap = BitmapInfo.LOW_RES_INFO);
+
+ mModelHelper.getModel().enqueueModelUpdateTask(
+ newTask(CacheDataUpdatedTask.OP_SESSION_UPDATE, PENDING_APP_1));
+
+ // Only restored apps from PENDING_APP_1 (id 5, 6, 7) are updated
+ verifyUpdate(5, 6, 7);
+ });
+ }
+
+ private void verifyUpdate(int... idsUpdated) {
+ IntSet updates = IntSet.wrap(idsUpdated);
+ for (WorkspaceItemInfo info : allItems()) {
+ if (updates.contains(info.id)) {
assertFalse(info.bitmap.isNullOrLowRes());
} else {
assertTrue(info.bitmap.isNullOrLowRes());
@@ -122,33 +149,7 @@
}
}
- @Test
- public void testSessionUpdate_ignores_normal_apps() throws Exception {
- mModelHelper.executeTaskForTest(newTask(CacheDataUpdatedTask.OP_SESSION_UPDATE, "app1"));
-
- // app1 has no restored shortcuts. Verify that nothing was updated.
- verifyUpdate();
- }
-
- @Test
- public void testSessionUpdate_updates_pending_apps() throws Exception {
- mModelHelper.executeTaskForTest(newTask(CacheDataUpdatedTask.OP_SESSION_UPDATE, "app3"));
-
- // app3 has only restored apps (id 5, 6) and shortcuts (id 9). Verify that only apps were
- // were updated
- verifyUpdate(5, 6);
- }
-
- private void verifyUpdate(Integer... idsUpdated) {
- HashSet<Integer> updates = new HashSet<>(Arrays.asList(idsUpdated));
- for (ItemInfo info : mModelHelper.getBgDataModel().itemsIdMap) {
- if (updates.contains(info.id)) {
- assertEquals(NEW_LABEL_PREFIX + info.id, info.title);
- assertFalse(((WorkspaceItemInfo) info).bitmap.isNullOrLowRes());
- } else {
- assertNotSame(NEW_LABEL_PREFIX + info.id, info.title);
- assertTrue(((WorkspaceItemInfo) info).bitmap.isNullOrLowRes());
- }
- }
+ private List<WorkspaceItemInfo> allItems() {
+ return ((FolderInfo) mModelHelper.getBgDataModel().itemsIdMap.get(1)).contents;
}
}
diff --git a/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt b/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt
index 3b480ca..4fa5352 100644
--- a/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt
+++ b/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt
@@ -24,7 +24,6 @@
import android.os.Process
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import androidx.test.platform.app.InstrumentationRegistry
import com.android.launcher3.InvariantDeviceProfile
import com.android.launcher3.LauncherPrefs
import com.android.launcher3.LauncherPrefs.Companion.WORKSPACE_SIZE
@@ -108,8 +107,8 @@
fun testMigration() {
// Src Hotseat icons
addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_HOTSEAT, 0, 0, testPackage1, 1, TMP_TABLE)
- addItem(ITEM_TYPE_SHORTCUT, 1, CONTAINER_HOTSEAT, 0, 0, testPackage2, 2, TMP_TABLE)
- addItem(ITEM_TYPE_SHORTCUT, 3, CONTAINER_HOTSEAT, 0, 0, testPackage3, 3, TMP_TABLE)
+ addItem(ITEM_TYPE_DEEP_SHORTCUT, 1, CONTAINER_HOTSEAT, 0, 0, testPackage2, 2, TMP_TABLE)
+ addItem(ITEM_TYPE_DEEP_SHORTCUT, 3, CONTAINER_HOTSEAT, 0, 0, testPackage3, 3, TMP_TABLE)
addItem(ITEM_TYPE_APPLICATION, 4, CONTAINER_HOTSEAT, 0, 0, testPackage4, 4, TMP_TABLE)
// Src grid icons
// _ _ _ _ _
@@ -124,7 +123,7 @@
addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 4, 3, testPackage9, 9, TMP_TABLE)
// Dest hotseat icons
- addItem(ITEM_TYPE_SHORTCUT, 1, CONTAINER_HOTSEAT, 0, 0, testPackage2)
+ addItem(ITEM_TYPE_DEEP_SHORTCUT, 1, CONTAINER_HOTSEAT, 0, 0, testPackage2)
// Dest grid icons
addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 2, 2, testPackage10)
@@ -219,8 +218,8 @@
// Hotseat items in grid A
// 1 2 _ 3 4
addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_HOTSEAT, 0, 0, testPackage1, 1, TMP_TABLE)
- addItem(ITEM_TYPE_SHORTCUT, 1, CONTAINER_HOTSEAT, 0, 0, testPackage2, 2, TMP_TABLE)
- addItem(ITEM_TYPE_SHORTCUT, 3, CONTAINER_HOTSEAT, 0, 0, testPackage3, 3, TMP_TABLE)
+ addItem(ITEM_TYPE_DEEP_SHORTCUT, 1, CONTAINER_HOTSEAT, 0, 0, testPackage2, 2, TMP_TABLE)
+ addItem(ITEM_TYPE_DEEP_SHORTCUT, 3, CONTAINER_HOTSEAT, 0, 0, testPackage3, 3, TMP_TABLE)
addItem(ITEM_TYPE_APPLICATION, 4, CONTAINER_HOTSEAT, 0, 0, testPackage4, 4, TMP_TABLE)
// Workspace items in grid A
// _ _ _ _ _
@@ -235,7 +234,7 @@
// Hotseat items in grid B
// 2 _ _ _
- addItem(ITEM_TYPE_SHORTCUT, 0, CONTAINER_HOTSEAT, 0, 0, testPackage2)
+ addItem(ITEM_TYPE_DEEP_SHORTCUT, 0, CONTAINER_HOTSEAT, 0, 0, testPackage2)
// Workspace items in grid B
// _ _ _ _
// _ _ _ 10
@@ -291,7 +290,7 @@
null
)
?: throw IllegalStateException()
- var locMap = parseLocMap(context, c)
+ var locMap = parseLocMap(c)
// Expected items in grid B
// _ _ _ _
// 5 6 7 8
@@ -348,7 +347,7 @@
null
)
?: throw IllegalStateException()
- locMap = parseLocMap(context, c)
+ locMap = parseLocMap(c)
// Expected workspace items in grid A
// _ _ _ _ _
// _ _ _ _ 5
@@ -410,7 +409,7 @@
null
)
?: throw IllegalStateException()
- locMap = parseLocMap(context, c)
+ locMap = parseLocMap(c)
// Expected workspace items in grid B
// _ _ _ _
// 5 6 _ 8
@@ -436,7 +435,7 @@
c.close()
}
- private fun parseLocMap(context: Context, c: Cursor): Map<String, Triple<Int, Int, Int>> {
+ private fun parseLocMap(c: Cursor): Map<String, Triple<Int, Int, Int>> {
// Check workspace items
val intentIndex = c.getColumnIndex(INTENT)
val screenIndex = c.getColumnIndex(SCREEN)
@@ -465,7 +464,16 @@
1,
TMP_TABLE
),
- addItem(ITEM_TYPE_SHORTCUT, 1, CONTAINER_HOTSEAT, 0, 0, testPackage2, 2, TMP_TABLE),
+ addItem(
+ ITEM_TYPE_DEEP_SHORTCUT,
+ 1,
+ CONTAINER_HOTSEAT,
+ 0,
+ 0,
+ testPackage2,
+ 2,
+ TMP_TABLE
+ ),
addItem(
ITEM_TYPE_APPLICATION,
2,
@@ -476,7 +484,16 @@
3,
TMP_TABLE
),
- addItem(ITEM_TYPE_SHORTCUT, 3, CONTAINER_HOTSEAT, 0, 0, testPackage4, 4, TMP_TABLE)
+ addItem(
+ ITEM_TYPE_DEEP_SHORTCUT,
+ 3,
+ CONTAINER_HOTSEAT,
+ 0,
+ 0,
+ testPackage4,
+ 4,
+ TMP_TABLE
+ )
)
val numSrcDatabaseHotseatIcons = srcHotseatItems.size
idp.numDatabaseHotseatIcons = 6
@@ -532,9 +549,9 @@
@Test
fun migrateFromLargerHotseat() {
addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_HOTSEAT, 0, 0, testPackage1, 1, TMP_TABLE)
- addItem(ITEM_TYPE_SHORTCUT, 2, CONTAINER_HOTSEAT, 0, 0, testPackage2, 2, TMP_TABLE)
+ addItem(ITEM_TYPE_DEEP_SHORTCUT, 2, CONTAINER_HOTSEAT, 0, 0, testPackage2, 2, TMP_TABLE)
addItem(ITEM_TYPE_APPLICATION, 3, CONTAINER_HOTSEAT, 0, 0, testPackage3, 3, TMP_TABLE)
- addItem(ITEM_TYPE_SHORTCUT, 4, CONTAINER_HOTSEAT, 0, 0, testPackage4, 4, TMP_TABLE)
+ addItem(ITEM_TYPE_DEEP_SHORTCUT, 4, CONTAINER_HOTSEAT, 0, 0, testPackage4, 4, TMP_TABLE)
addItem(ITEM_TYPE_APPLICATION, 5, CONTAINER_HOTSEAT, 0, 0, testPackage5, 5, TMP_TABLE)
idp.numDatabaseHotseatIcons = 4
diff --git a/tests/src/com/android/launcher3/model/LoaderCursorTest.java b/tests/src/com/android/launcher3/model/LoaderCursorTest.java
index 78812c0..544ed6b 100644
--- a/tests/src/com/android/launcher3/model/LoaderCursorTest.java
+++ b/tests/src/com/android/launcher3/model/LoaderCursorTest.java
@@ -30,7 +30,7 @@
import static com.android.launcher3.LauncherSettings.Favorites.INTENT;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
-import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
import static com.android.launcher3.LauncherSettings.Favorites.OPTIONS;
import static com.android.launcher3.LauncherSettings.Favorites.PROFILE_ID;
import static com.android.launcher3.LauncherSettings.Favorites.RANK;
@@ -158,13 +158,13 @@
@Test
public void loadSimpleShortcut() {
- initCursor(ITEM_TYPE_SHORTCUT, "my-shortcut");
+ initCursor(ITEM_TYPE_DEEP_SHORTCUT, "my-shortcut");
assertTrue(mLoaderCursor.moveToNext());
WorkspaceItemInfo info = mLoaderCursor.loadSimpleWorkspaceItem();
assertTrue(mApp.getIconCache().isDefaultIcon(info.bitmap, info.user));
assertEquals("my-shortcut", info.title);
- assertEquals(ITEM_TYPE_SHORTCUT, info.itemType);
+ assertEquals(ITEM_TYPE_DEEP_SHORTCUT, info.itemType);
}
@Test
diff --git a/tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java b/tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
index 519191e..4ba61ac 100644
--- a/tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
+++ b/tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
@@ -1,5 +1,12 @@
package com.android.launcher3.model;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+import static com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY;
+import static com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY2;
+import static com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY3;
+import static com.android.launcher3.util.LauncherModelHelper.TEST_PACKAGE;
+import static com.android.launcher3.util.TestUtil.runOnExecutorSync;
+
import static org.junit.Assert.assertEquals;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -9,6 +16,8 @@
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.pm.PackageInstallInfo;
+import com.android.launcher3.util.IntSet;
+import com.android.launcher3.util.LauncherLayoutBuilder;
import com.android.launcher3.util.LauncherModelHelper;
import org.junit.After;
@@ -16,9 +25,6 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.util.Arrays;
-import java.util.HashSet;
-
/**
* Tests for {@link PackageInstallStateChangedTask}
*/
@@ -26,12 +32,36 @@
@RunWith(AndroidJUnit4.class)
public class PackageInstallStateChangedTaskTest {
+ private static final String PENDING_APP_1 = TEST_PACKAGE + ".pending1";
+ private static final String PENDING_APP_2 = TEST_PACKAGE + ".pending2";
+
private LauncherModelHelper mModelHelper;
+ private IntSet mDownloadingApps;
@Before
public void setup() throws Exception {
mModelHelper = new LauncherModelHelper();
- mModelHelper.initializeData("package_install_state_change_task_data");
+ mModelHelper.createInstallerSession(PENDING_APP_1);
+ mModelHelper.createInstallerSession(PENDING_APP_2);
+
+ LauncherLayoutBuilder builder = new LauncherLayoutBuilder()
+ .atWorkspace(0, 0, 1).putApp(TEST_PACKAGE, TEST_ACTIVITY) // 1
+ .atWorkspace(0, 0, 2).putApp(TEST_PACKAGE, TEST_ACTIVITY2) // 2
+ .atWorkspace(0, 0, 3).putApp(TEST_PACKAGE, TEST_ACTIVITY3) // 3
+
+ .atWorkspace(0, 0, 4).putApp(PENDING_APP_1, TEST_ACTIVITY) // 4
+ .atWorkspace(0, 0, 5).putApp(PENDING_APP_1, TEST_ACTIVITY2) // 5
+ .atWorkspace(0, 0, 6).putApp(PENDING_APP_1, TEST_ACTIVITY3) // 6
+ .atWorkspace(0, 0, 7).putWidget(PENDING_APP_1, "pending.widget", 1, 1) // 7
+
+ .atWorkspace(0, 0, 8).putApp(PENDING_APP_2, TEST_ACTIVITY) // 8
+ .atWorkspace(0, 0, 9).putApp(PENDING_APP_2, TEST_ACTIVITY2) // 9
+ .atWorkspace(0, 0, 10).putApp(PENDING_APP_2, TEST_ACTIVITY3); // 10
+
+ mDownloadingApps = IntSet.wrap(4, 5, 6, 7, 8, 9, 10);
+ mModelHelper.setupDefaultLayoutProvider(builder);
+ mModelHelper.loadModelSync();
+ assertEquals(10, mModelHelper.getBgDataModel().itemsIdMap.size());
}
@After
@@ -47,36 +77,45 @@
}
@Test
- public void testSessionUpdate_ignore_installed() throws Exception {
- mModelHelper.executeTaskForTest(newTask("app1", 30));
+ public void testSessionUpdate_ignore_installed() {
+ // Run on model executor so that no other task runs in the middle.
+ runOnExecutorSync(MODEL_EXECUTOR, () -> {
+ mModelHelper.getModel().enqueueModelUpdateTask(newTask(TEST_PACKAGE, 30));
- // No shortcuts were updated
- verifyProgressUpdate(0);
+ // No shortcuts were updated
+ verifyProgressUpdate(0);
+ });
}
@Test
- public void testSessionUpdate_shortcuts_updated() throws Exception {
- mModelHelper.executeTaskForTest(newTask("app3", 30));
+ public void testSessionUpdate_shortcuts_updated() {
+ // Run on model executor so that no other task runs in the middle.
+ runOnExecutorSync(MODEL_EXECUTOR, () -> {
+ mModelHelper.getModel().enqueueModelUpdateTask(newTask(PENDING_APP_1, 30));
- verifyProgressUpdate(30, 5, 6, 7);
+ verifyProgressUpdate(30, 4, 5, 6, 7);
+ });
}
@Test
- public void testSessionUpdate_widgets_updated() throws Exception {
- mModelHelper.executeTaskForTest(newTask("app4", 30));
+ public void testSessionUpdate_widgets_updated() {
+ // Run on model executor so that no other task runs in the middle.
+ runOnExecutorSync(MODEL_EXECUTOR, () -> {
+ mModelHelper.getModel().enqueueModelUpdateTask(newTask(PENDING_APP_2, 30));
- verifyProgressUpdate(30, 8, 9);
+ verifyProgressUpdate(30, 8, 9, 10);
+ });
}
- private void verifyProgressUpdate(int progress, Integer... idsUpdated) {
- HashSet<Integer> updates = new HashSet<>(Arrays.asList(idsUpdated));
+ private void verifyProgressUpdate(int progress, int... idsUpdated) {
+ IntSet updates = IntSet.wrap(idsUpdated);
for (ItemInfo info : mModelHelper.getBgDataModel().itemsIdMap) {
- if (info instanceof WorkspaceItemInfo) {
- assertEquals(updates.contains(info.id) ? progress: 100,
- ((WorkspaceItemInfo) info).getProgressLevel());
+ int expectedProgress = updates.contains(info.id) ? progress
+ : (mDownloadingApps.contains(info.id) ? 0 : 100);
+ if (info instanceof WorkspaceItemInfo wi) {
+ assertEquals(expectedProgress, wi.getProgressLevel());
} else {
- assertEquals(updates.contains(info.id) ? progress: -1,
- ((LauncherAppWidgetInfo) info).installProgress);
+ assertEquals(expectedProgress, ((LauncherAppWidgetInfo) info).installProgress);
}
}
}
diff --git a/tests/src/com/android/launcher3/nonquickstep/DeviceProfileDumpTest.kt b/tests/src/com/android/launcher3/nonquickstep/DeviceProfileDumpTest.kt
index a81413e..0a95771 100644
--- a/tests/src/com/android/launcher3/nonquickstep/DeviceProfileDumpTest.kt
+++ b/tests/src/com/android/launcher3/nonquickstep/DeviceProfileDumpTest.kt
@@ -84,7 +84,8 @@
"\tfolderChildIconSizePx: 147.0px (56.0dp)\n" +
"\tfolderChildTextSizePx: 38.0px (14.476191dp)\n" +
"\tfolderChildDrawablePaddingPx: 10.0px (3.8095238dp)\n" +
- "\tfolderCellLayoutBorderSpacePx: 0.0px (0.0dp)\n" +
+ "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" +
+ "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" +
"\tfolderContentPaddingLeftRight: 21.0px (8.0dp)\n" +
"\tfolderTopPadding: 63.0px (24.0dp)\n" +
"\tfolderFooterHeight: 147.0px (56.0dp)\n" +
@@ -105,7 +106,7 @@
"\tallAppsBorderSpacePxX: 42.0px (16.0dp)\n" +
"\tallAppsBorderSpacePxY: 42.0px (16.0dp)\n" +
"\tnumShownAllAppsColumns: 5\n" +
- "\tallAppsLeftRightPadding: 21.0px (8.0dp)\n" +
+ "\tallAppsLeftRightPadding: 0.0px (0.0dp)\n" +
"\tallAppsLeftRightMargin: 0.0px (0.0dp)\n" +
"\thotseatBarSizePx: 294.0px (112.0dp)\n" +
"\tinv.hotseatColumnSpan: 5\n" +
@@ -220,7 +221,8 @@
"\tfolderChildIconSizePx: 147.0px (56.0dp)\n" +
"\tfolderChildTextSizePx: 38.0px (14.476191dp)\n" +
"\tfolderChildDrawablePaddingPx: 10.0px (3.8095238dp)\n" +
- "\tfolderCellLayoutBorderSpacePx: 0.0px (0.0dp)\n" +
+ "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" +
+ "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" +
"\tfolderContentPaddingLeftRight: 21.0px (8.0dp)\n" +
"\tfolderTopPadding: 63.0px (24.0dp)\n" +
"\tfolderFooterHeight: 147.0px (56.0dp)\n" +
@@ -241,7 +243,7 @@
"\tallAppsBorderSpacePxX: 42.0px (16.0dp)\n" +
"\tallAppsBorderSpacePxY: 42.0px (16.0dp)\n" +
"\tnumShownAllAppsColumns: 5\n" +
- "\tallAppsLeftRightPadding: 21.0px (8.0dp)\n" +
+ "\tallAppsLeftRightPadding: 0.0px (0.0dp)\n" +
"\tallAppsLeftRightMargin: 0.0px (0.0dp)\n" +
"\thotseatBarSizePx: 273.0px (104.0dp)\n" +
"\tinv.hotseatColumnSpan: 5\n" +
@@ -356,7 +358,8 @@
"\tfolderChildIconSizePx: 131.0px (49.904762dp)\n" +
"\tfolderChildTextSizePx: 34.0px (12.952381dp)\n" +
"\tfolderChildDrawablePaddingPx: 9.0px (3.4285715dp)\n" +
- "\tfolderCellLayoutBorderSpacePx: 0.0px (0.0dp)\n" +
+ "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" +
+ "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" +
"\tfolderContentPaddingLeftRight: 21.0px (8.0dp)\n" +
"\tfolderTopPadding: 56.0px (21.333334dp)\n" +
"\tfolderFooterHeight: 131.0px (49.904762dp)\n" +
@@ -492,7 +495,8 @@
"\tfolderChildIconSizePx: 123.0px (46.857143dp)\n" +
"\tfolderChildTextSizePx: 32.0px (12.190476dp)\n" +
"\tfolderChildDrawablePaddingPx: 8.0px (3.047619dp)\n" +
- "\tfolderCellLayoutBorderSpacePx: 0.0px (0.0dp)\n" +
+ "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" +
+ "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" +
"\tfolderContentPaddingLeftRight: 21.0px (8.0dp)\n" +
"\tfolderTopPadding: 53.0px (20.190475dp)\n" +
"\tfolderFooterHeight: 123.0px (46.857143dp)\n" +
@@ -629,7 +633,8 @@
"\tfolderChildIconSizePx: 120.0px (60.0dp)\n" +
"\tfolderChildTextSizePx: 28.0px (14.0dp)\n" +
"\tfolderChildDrawablePaddingPx: 16.0px (8.0dp)\n" +
- "\tfolderCellLayoutBorderSpacePx: 0.0px (0.0dp)\n" +
+ "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" +
+ "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" +
"\tfolderContentPaddingLeftRight: 0.0px (0.0dp)\n" +
"\tfolderTopPadding: 48.0px (24.0dp)\n" +
"\tfolderFooterHeight: 112.0px (56.0dp)\n" +
@@ -766,7 +771,8 @@
"\tfolderChildIconSizePx: 120.0px (60.0dp)\n" +
"\tfolderChildTextSizePx: 28.0px (14.0dp)\n" +
"\tfolderChildDrawablePaddingPx: 16.0px (8.0dp)\n" +
- "\tfolderCellLayoutBorderSpacePx: 0.0px (0.0dp)\n" +
+ "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" +
+ "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" +
"\tfolderContentPaddingLeftRight: 0.0px (0.0dp)\n" +
"\tfolderTopPadding: 48.0px (24.0dp)\n" +
"\tfolderFooterHeight: 112.0px (56.0dp)\n" +
@@ -903,7 +909,8 @@
"\tfolderChildIconSizePx: 120.0px (60.0dp)\n" +
"\tfolderChildTextSizePx: 28.0px (14.0dp)\n" +
"\tfolderChildDrawablePaddingPx: 27.0px (13.5dp)\n" +
- "\tfolderCellLayoutBorderSpacePx: 0.0px (0.0dp)\n" +
+ "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" +
+ "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" +
"\tfolderContentPaddingLeftRight: 0.0px (0.0dp)\n" +
"\tfolderTopPadding: 48.0px (24.0dp)\n" +
"\tfolderFooterHeight: 112.0px (56.0dp)\n" +
@@ -1040,7 +1047,8 @@
"\tfolderChildIconSizePx: 120.0px (60.0dp)\n" +
"\tfolderChildTextSizePx: 28.0px (14.0dp)\n" +
"\tfolderChildDrawablePaddingPx: 27.0px (13.5dp)\n" +
- "\tfolderCellLayoutBorderSpacePx: 0.0px (0.0dp)\n" +
+ "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" +
+ "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" +
"\tfolderContentPaddingLeftRight: 0.0px (0.0dp)\n" +
"\tfolderTopPadding: 48.0px (24.0dp)\n" +
"\tfolderFooterHeight: 112.0px (56.0dp)\n" +
@@ -1182,7 +1190,8 @@
"\tfolderChildIconSizePx: 141.0px (53.714287dp)\n" +
"\tfolderChildTextSizePx: 34.0px (12.952381dp)\n" +
"\tfolderChildDrawablePaddingPx: 10.0px (3.8095238dp)\n" +
- "\tfolderCellLayoutBorderSpacePx: 0.0px (0.0dp)\n" +
+ "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" +
+ "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" +
"\tfolderContentPaddingLeftRight: 21.0px (8.0dp)\n" +
"\tfolderTopPadding: 63.0px (24.0dp)\n" +
"\tfolderFooterHeight: 147.0px (56.0dp)\n" +
@@ -1323,7 +1332,8 @@
"\tfolderChildIconSizePx: 141.0px (53.714287dp)\n" +
"\tfolderChildTextSizePx: 34.0px (12.952381dp)\n" +
"\tfolderChildDrawablePaddingPx: 10.0px (3.8095238dp)\n" +
- "\tfolderCellLayoutBorderSpacePx: 0.0px (0.0dp)\n" +
+ "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" +
+ "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" +
"\tfolderContentPaddingLeftRight: 21.0px (8.0dp)\n" +
"\tfolderTopPadding: 63.0px (24.0dp)\n" +
"\tfolderFooterHeight: 147.0px (56.0dp)\n" +
@@ -1464,7 +1474,8 @@
"\tfolderChildIconSizePx: 141.0px (53.714287dp)\n" +
"\tfolderChildTextSizePx: 34.0px (12.952381dp)\n" +
"\tfolderChildDrawablePaddingPx: 10.0px (3.8095238dp)\n" +
- "\tfolderCellLayoutBorderSpacePx: 0.0px (0.0dp)\n" +
+ "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" +
+ "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" +
"\tfolderContentPaddingLeftRight: 21.0px (8.0dp)\n" +
"\tfolderTopPadding: 63.0px (24.0dp)\n" +
"\tfolderFooterHeight: 147.0px (56.0dp)\n" +
@@ -1601,7 +1612,8 @@
"\tfolderChildIconSizePx: 141.0px (53.714287dp)\n" +
"\tfolderChildTextSizePx: 34.0px (12.952381dp)\n" +
"\tfolderChildDrawablePaddingPx: 10.0px (3.8095238dp)\n" +
- "\tfolderCellLayoutBorderSpacePx: 0.0px (0.0dp)\n" +
+ "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" +
+ "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" +
"\tfolderContentPaddingLeftRight: 21.0px (8.0dp)\n" +
"\tfolderTopPadding: 63.0px (24.0dp)\n" +
"\tfolderFooterHeight: 147.0px (56.0dp)\n" +
diff --git a/tests/src/com/android/launcher3/responsive/AllAppsSpecsTest.kt b/tests/src/com/android/launcher3/responsive/AllAppsSpecsTest.kt
new file mode 100644
index 0000000..77ea5ba
--- /dev/null
+++ b/tests/src/com/android/launcher3/responsive/AllAppsSpecsTest.kt
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.responsive
+
+import android.content.Context
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.launcher3.AbstractDeviceProfileTest
+import com.android.launcher3.tests.R as TestR
+import com.android.launcher3.util.TestResourceHelper
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class AllAppsSpecsTest : AbstractDeviceProfileTest() {
+ override val runningContext: Context = InstrumentationRegistry.getInstrumentation().context
+
+ @Before
+ fun setup() {
+ initializeVarsForPhone(deviceSpecs["phone"]!!)
+ }
+
+ @Test
+ fun parseValidFile() {
+ val allAppsSpecs =
+ AllAppsSpecs(TestResourceHelper(context!!, TestR.xml.valid_all_apps_file))
+ assertThat(allAppsSpecs.allAppsHeightSpecList.size).isEqualTo(1)
+ assertThat(allAppsSpecs.allAppsHeightSpecList[0].toString())
+ .isEqualTo(
+ "AllAppsSpec(" +
+ "maxAvailableSize=26247, " +
+ "specType=HEIGHT, " +
+ "startPadding=SizeSpec(fixedSize=0.0, " +
+ "ofAvailableSpace=0.0, " +
+ "ofRemainderSpace=0.0, " +
+ "matchWorkspace=false, " +
+ "maxSize=2147483647), " +
+ "endPadding=SizeSpec(fixedSize=0.0, " +
+ "ofAvailableSpace=0.0, " +
+ "ofRemainderSpace=0.0, " +
+ "matchWorkspace=false, " +
+ "maxSize=2147483647), " +
+ "gutter=SizeSpec(fixedSize=0.0, " +
+ "ofAvailableSpace=0.0, " +
+ "ofRemainderSpace=0.0, " +
+ "matchWorkspace=true, " +
+ "maxSize=2147483647), " +
+ "cellSize=SizeSpec(fixedSize=0.0, " +
+ "ofAvailableSpace=0.0, " +
+ "ofRemainderSpace=0.0, " +
+ "matchWorkspace=true, " +
+ "maxSize=2147483647)" +
+ ")"
+ )
+
+ assertThat(allAppsSpecs.allAppsWidthSpecList.size).isEqualTo(1)
+ assertThat(allAppsSpecs.allAppsWidthSpecList[0].toString())
+ .isEqualTo(
+ "AllAppsSpec(" +
+ "maxAvailableSize=26247, " +
+ "specType=WIDTH, " +
+ "startPadding=SizeSpec(fixedSize=0.0, " +
+ "ofAvailableSpace=0.0, " +
+ "ofRemainderSpace=0.0, " +
+ "matchWorkspace=true, " +
+ "maxSize=2147483647), " +
+ "endPadding=SizeSpec(fixedSize=0.0, " +
+ "ofAvailableSpace=0.0, " +
+ "ofRemainderSpace=0.0, " +
+ "matchWorkspace=true, " +
+ "maxSize=2147483647), " +
+ "gutter=SizeSpec(fixedSize=0.0, " +
+ "ofAvailableSpace=0.0, " +
+ "ofRemainderSpace=0.0, " +
+ "matchWorkspace=true, " +
+ "maxSize=2147483647), " +
+ "cellSize=SizeSpec(fixedSize=0.0, " +
+ "ofAvailableSpace=0.0, " +
+ "ofRemainderSpace=0.0, " +
+ "matchWorkspace=true, " +
+ "maxSize=2147483647)" +
+ ")"
+ )
+ }
+
+ @Test(expected = IllegalStateException::class)
+ fun parseInvalidFile_missingTag_throwsError() {
+ AllAppsSpecs(TestResourceHelper(context!!, TestR.xml.invalid_all_apps_file_case_1))
+ }
+
+ @Test(expected = IllegalStateException::class)
+ fun parseInvalidFile_moreThanOneValuePerTag_throwsError() {
+ AllAppsSpecs(TestResourceHelper(context!!, TestR.xml.invalid_all_apps_file_case_2))
+ }
+
+ @Test(expected = IllegalStateException::class)
+ fun parseInvalidFile_valueBiggerThan1_throwsError() {
+ AllAppsSpecs(TestResourceHelper(context!!, TestR.xml.invalid_all_apps_file_case_3))
+ }
+}
diff --git a/tests/src/com/android/launcher3/responsive/CalculatedAllAppsSpecTest.kt b/tests/src/com/android/launcher3/responsive/CalculatedAllAppsSpecTest.kt
new file mode 100644
index 0000000..9f981fa
--- /dev/null
+++ b/tests/src/com/android/launcher3/responsive/CalculatedAllAppsSpecTest.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.responsive
+
+import android.content.Context
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.launcher3.AbstractDeviceProfileTest
+import com.android.launcher3.tests.R as TestR
+import com.android.launcher3.util.TestResourceHelper
+import com.android.launcher3.workspace.WorkspaceSpecs
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class CalculatedAllAppsSpecTest : AbstractDeviceProfileTest() {
+ override val runningContext: Context = InstrumentationRegistry.getInstrumentation().context
+
+ /**
+ * This test tests:
+ * - (height spec) copy values from workspace
+ * - (width spec) copy values from workspace
+ */
+ @Test
+ fun normalPhone_copiesFromWorkspace() {
+ val deviceSpec = deviceSpecs["phone"]!!
+ initializeVarsForPhone(deviceSpec)
+
+ val availableWidth = deviceSpec.naturalSize.first
+ // Hotseat size is roughly 495px on a real device,
+ // it doesn't need to be precise on unit tests
+ val availableHeight = deviceSpec.naturalSize.second - deviceSpec.statusBarNaturalPx - 495
+
+ val workspaceSpecs =
+ WorkspaceSpecs(TestResourceHelper(context!!, TestR.xml.valid_workspace_file))
+ val widthSpec = workspaceSpecs.getCalculatedWidthSpec(4, availableWidth)
+ val heightSpec = workspaceSpecs.getCalculatedHeightSpec(5, availableHeight)
+
+ val allAppsSpecs =
+ AllAppsSpecs(TestResourceHelper(context!!, TestR.xml.valid_all_apps_file))
+
+ with(allAppsSpecs.getCalculatedWidthSpec(4, availableWidth, widthSpec)) {
+ assertThat(availableSpace).isEqualTo(availableWidth)
+ assertThat(cells).isEqualTo(4)
+ assertThat(startPaddingPx).isEqualTo(widthSpec.startPaddingPx)
+ assertThat(endPaddingPx).isEqualTo(widthSpec.endPaddingPx)
+ assertThat(gutterPx).isEqualTo(widthSpec.gutterPx)
+ assertThat(cellSizePx).isEqualTo(widthSpec.cellSizePx)
+ }
+
+ with(allAppsSpecs.getCalculatedHeightSpec(5, availableHeight, heightSpec)) {
+ assertThat(availableSpace).isEqualTo(availableHeight)
+ assertThat(cells).isEqualTo(5)
+ assertThat(startPaddingPx).isEqualTo(0)
+ assertThat(endPaddingPx).isEqualTo(0)
+ assertThat(gutterPx).isEqualTo(heightSpec.gutterPx)
+ assertThat(cellSizePx).isEqualTo(heightSpec.cellSizePx)
+ }
+ }
+}
diff --git a/tests/src/com/android/launcher3/responsive/CalculatedFolderSpecsTest.kt b/tests/src/com/android/launcher3/responsive/CalculatedFolderSpecsTest.kt
new file mode 100644
index 0000000..c14722c
--- /dev/null
+++ b/tests/src/com/android/launcher3/responsive/CalculatedFolderSpecsTest.kt
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.responsive
+
+import android.content.Context
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.launcher3.AbstractDeviceProfileTest
+import com.android.launcher3.testing.shared.ResourceUtils
+import com.android.launcher3.tests.R
+import com.android.launcher3.util.TestResourceHelper
+import com.android.launcher3.workspace.WorkspaceSpecs
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class CalculatedFolderSpecsTest : AbstractDeviceProfileTest() {
+ override val runningContext: Context = InstrumentationRegistry.getInstrumentation().context
+
+ private val deviceSpec = deviceSpecs["phone"]!!
+
+ @Before
+ fun setup() {
+ initializeVarsForPhone(deviceSpec)
+ }
+
+ @Test
+ fun validate_matchWidthWorkspace() {
+ val columns = 6
+
+ // Loading workspace specs
+ val resourceHelperWorkspace = TestResourceHelper(context!!, R.xml.valid_workspace_file)
+ val workspaceSpecs = WorkspaceSpecs(resourceHelperWorkspace)
+
+ // Loading folders specs
+ val resourceHelperFolder = TestResourceHelper(context!!, R.xml.valid_folders_specs)
+ val folderSpecs = FolderSpecs(resourceHelperFolder)
+
+ assertThat(folderSpecs.widthSpecs.size).isEqualTo(2)
+ assertThat(folderSpecs.widthSpecs[0].cellSize.matchWorkspace).isEqualTo(true)
+ assertThat(folderSpecs.widthSpecs[1].cellSize.matchWorkspace).isEqualTo(false)
+
+ // Validate width spec <= 800
+ var availableWidth = deviceSpec.naturalSize.first
+ var calculatedWorkspace = workspaceSpecs.getCalculatedWidthSpec(columns, availableWidth)
+ var calculatedWidthFolderSpec =
+ folderSpecs.getWidthSpec(columns, availableWidth, calculatedWorkspace)
+ with(calculatedWidthFolderSpec) {
+ assertThat(availableSpace).isEqualTo(availableWidth)
+ assertThat(cells).isEqualTo(columns)
+ assertThat(startPaddingPx).isEqualTo(16.dpToPx())
+ assertThat(endPaddingPx).isEqualTo(16.dpToPx())
+ assertThat(gutterPx).isEqualTo(16.dpToPx())
+ assertThat(cellSizePx).isEqualTo(calculatedWorkspace.cellSizePx)
+ }
+
+ // Validate width spec > 800
+ availableWidth = 2000.dpToPx()
+ calculatedWorkspace = workspaceSpecs.getCalculatedWidthSpec(columns, availableWidth)
+ calculatedWidthFolderSpec =
+ folderSpecs.getWidthSpec(columns, availableWidth, calculatedWorkspace)
+ with(calculatedWidthFolderSpec) {
+ assertThat(availableSpace).isEqualTo(availableWidth)
+ assertThat(cells).isEqualTo(columns)
+ assertThat(startPaddingPx).isEqualTo(16.dpToPx())
+ assertThat(endPaddingPx).isEqualTo(16.dpToPx())
+ assertThat(gutterPx).isEqualTo(16.dpToPx())
+ assertThat(cellSizePx).isEqualTo(102.dpToPx())
+ }
+ }
+
+ @Test
+ fun validate_matchHeightWorkspace() {
+ // Hotseat is roughly 495px on a real device, it doesn't need to be precise on unit tests
+ val hotseatSize = 495
+ val statusBarHeight = deviceSpec.statusBarNaturalPx
+ val availableHeight = deviceSpec.naturalSize.second - statusBarHeight - hotseatSize
+ val rows = 5
+
+ // Loading workspace specs
+ val resourceHelperWorkspace = TestResourceHelper(context!!, R.xml.valid_workspace_file)
+ val workspaceSpecs = WorkspaceSpecs(resourceHelperWorkspace)
+
+ // Loading folders specs
+ val resourceHelperFolder = TestResourceHelper(context!!, R.xml.valid_folders_specs)
+ val folderSpecs = FolderSpecs(resourceHelperFolder)
+
+ assertThat(folderSpecs.heightSpecs.size).isEqualTo(1)
+ assertThat(folderSpecs.heightSpecs[0].cellSize.matchWorkspace).isEqualTo(true)
+
+ // Validate height spec
+ val calculatedWorkspace = workspaceSpecs.getCalculatedHeightSpec(rows, availableHeight)
+ val calculatedFolderSpec =
+ folderSpecs.getHeightSpec(rows, availableHeight, calculatedWorkspace)
+ with(calculatedFolderSpec) {
+ assertThat(availableSpace).isEqualTo(availableHeight)
+ assertThat(cells).isEqualTo(rows)
+ assertThat(startPaddingPx).isEqualTo(24.dpToPx())
+ assertThat(endPaddingPx).isEqualTo(64.dpToPx())
+ assertThat(gutterPx).isEqualTo(16.dpToPx())
+ assertThat(cellSizePx).isEqualTo(calculatedWorkspace.cellSizePx)
+ }
+ }
+
+ private fun Int.dpToPx(): Int {
+ return ResourceUtils.pxFromDp(this.toFloat(), context!!.resources.displayMetrics)
+ }
+}
diff --git a/tests/src/com/android/launcher3/responsive/FolderSpecsTest.kt b/tests/src/com/android/launcher3/responsive/FolderSpecsTest.kt
new file mode 100644
index 0000000..796bf9a
--- /dev/null
+++ b/tests/src/com/android/launcher3/responsive/FolderSpecsTest.kt
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.responsive
+
+import android.content.Context
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.launcher3.AbstractDeviceProfileTest
+import com.android.launcher3.testing.shared.ResourceUtils
+import com.android.launcher3.tests.R
+import com.android.launcher3.util.TestResourceHelper
+import com.android.launcher3.workspace.CalculatedWorkspaceSpec
+import com.android.launcher3.workspace.WorkspaceSpec
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class FolderSpecsTest : AbstractDeviceProfileTest() {
+ override val runningContext: Context = InstrumentationRegistry.getInstrumentation().context
+
+ @Before
+ fun setup() {
+ initializeVarsForPhone(deviceSpecs["tablet"]!!)
+ }
+
+ @Test
+ fun parseValidFile() {
+ val resourceHelper = TestResourceHelper(context!!, R.xml.valid_folders_specs)
+ val folderSpecs = FolderSpecs(resourceHelper)
+
+ val sizeSpec16 = SizeSpec(16f.dpToPx())
+ val widthSpecsExpected =
+ listOf(
+ FolderSpec(
+ maxAvailableSize = 800.dpToPx(),
+ specType = FolderSpec.SpecType.WIDTH,
+ startPadding = sizeSpec16,
+ endPadding = sizeSpec16,
+ gutter = sizeSpec16,
+ cellSize = SizeSpec(matchWorkspace = true)
+ ),
+ FolderSpec(
+ maxAvailableSize = 9999.dpToPx(),
+ specType = FolderSpec.SpecType.WIDTH,
+ startPadding = sizeSpec16,
+ endPadding = sizeSpec16,
+ gutter = sizeSpec16,
+ cellSize = SizeSpec(102f.dpToPx())
+ )
+ )
+
+ val heightSpecsExpected =
+ FolderSpec(
+ maxAvailableSize = 9999.dpToPx(),
+ specType = FolderSpec.SpecType.HEIGHT,
+ startPadding = SizeSpec(24f.dpToPx()),
+ endPadding = SizeSpec(64f.dpToPx()),
+ gutter = sizeSpec16,
+ cellSize = SizeSpec(matchWorkspace = true)
+ )
+
+ assertThat(folderSpecs.widthSpecs.size).isEqualTo(widthSpecsExpected.size)
+ assertThat(folderSpecs.widthSpecs[0]).isEqualTo(widthSpecsExpected[0])
+ assertThat(folderSpecs.widthSpecs[1]).isEqualTo(widthSpecsExpected[1])
+
+ assertThat(folderSpecs.heightSpecs.size).isEqualTo(1)
+ assertThat(folderSpecs.heightSpecs[0]).isEqualTo(heightSpecsExpected)
+ }
+
+ @Test(expected = IllegalStateException::class)
+ fun parseInvalidFile_missingTag_throwsError() {
+ val resourceHelper = TestResourceHelper(context!!, R.xml.invalid_folders_specs_1)
+ FolderSpecs(resourceHelper)
+ }
+
+ @Test(expected = IllegalStateException::class)
+ fun parseInvalidFile_moreThanOneValuePerTag_throwsError() {
+ val resourceHelper = TestResourceHelper(context!!, R.xml.invalid_folders_specs_2)
+ FolderSpecs(resourceHelper)
+ }
+
+ @Test(expected = IllegalStateException::class)
+ fun parseInvalidFile_valueBiggerThan1_throwsError() {
+ val resourceHelper = TestResourceHelper(context!!, R.xml.invalid_folders_specs_3)
+ FolderSpecs(resourceHelper)
+ }
+
+ @Test(expected = IllegalStateException::class)
+ fun parseInvalidFile_missingSpecs_throwsError() {
+ val resourceHelper = TestResourceHelper(context!!, R.xml.invalid_folders_specs_4)
+ FolderSpecs(resourceHelper)
+ }
+
+ @Test(expected = IllegalStateException::class)
+ fun parseInvalidFile_missingWidthBreakpoint_throwsError() {
+ val availableSpace = 900.dpToPx()
+ val cells = 3
+
+ val workspaceSpec =
+ WorkspaceSpec(
+ maxAvailableSize = availableSpace,
+ specType = WorkspaceSpec.SpecType.WIDTH,
+ startPadding = SizeSpec(fixedSize = 10f),
+ endPadding = SizeSpec(fixedSize = 10f),
+ gutter = SizeSpec(fixedSize = 10f),
+ cellSize = SizeSpec(fixedSize = 10f)
+ )
+ val calculatedWorkspaceSpec = CalculatedWorkspaceSpec(availableSpace, cells, workspaceSpec)
+
+ val resourceHelper = TestResourceHelper(context!!, R.xml.invalid_folders_specs_5)
+ val folderSpecs = FolderSpecs(resourceHelper)
+ folderSpecs.getWidthSpec(cells, availableSpace, calculatedWorkspaceSpec)
+ }
+
+ @Test(expected = IllegalStateException::class)
+ fun parseInvalidFile_missingHeightBreakpoint_throwsError() {
+ val availableSpace = 900.dpToPx()
+ val cells = 3
+
+ val workspaceSpec =
+ WorkspaceSpec(
+ maxAvailableSize = availableSpace,
+ specType = WorkspaceSpec.SpecType.HEIGHT,
+ startPadding = SizeSpec(fixedSize = 10f),
+ endPadding = SizeSpec(fixedSize = 10f),
+ gutter = SizeSpec(fixedSize = 10f),
+ cellSize = SizeSpec(fixedSize = 10f)
+ )
+ val calculatedWorkspaceSpec = CalculatedWorkspaceSpec(availableSpace, cells, workspaceSpec)
+
+ val resourceHelper = TestResourceHelper(context!!, R.xml.invalid_folders_specs_5)
+ val folderSpecs = FolderSpecs(resourceHelper)
+ folderSpecs.getHeightSpec(cells, availableSpace, calculatedWorkspaceSpec)
+ }
+
+ @Test
+ fun retrievesCalculatedWidthSpec() {
+ val availableSpace = 800.dpToPx()
+ val cells = 3
+
+ val workspaceSpec =
+ WorkspaceSpec(
+ maxAvailableSize = availableSpace,
+ specType = WorkspaceSpec.SpecType.WIDTH,
+ startPadding = SizeSpec(fixedSize = 10f),
+ endPadding = SizeSpec(fixedSize = 10f),
+ gutter = SizeSpec(fixedSize = 10f),
+ cellSize = SizeSpec(fixedSize = 10f)
+ )
+ val calculatedWorkspaceSpec = CalculatedWorkspaceSpec(availableSpace, cells, workspaceSpec)
+
+ val expectedResult =
+ CalculatedFolderSpec(
+ startPaddingPx = 16.dpToPx(),
+ endPaddingPx = 16.dpToPx(),
+ gutterPx = 16.dpToPx(),
+ cellSizePx = calculatedWorkspaceSpec.cellSizePx,
+ availableSpace = availableSpace,
+ cells = cells
+ )
+
+ val resourceHelper = TestResourceHelper(context!!, R.xml.valid_folders_specs)
+ val folderSpecs = FolderSpecs(resourceHelper)
+ val calculatedWidthSpec =
+ folderSpecs.getWidthSpec(cells, availableSpace, calculatedWorkspaceSpec)
+ assertThat(calculatedWidthSpec).isEqualTo(expectedResult)
+ }
+
+ @Test(expected = IllegalStateException::class)
+ fun retrievesCalculatedWidthSpec_invalidCalculatedWorkspaceSpecType_throwsError() {
+ val availableSpace = 10.dpToPx()
+ val cells = 3
+
+ val workspaceSpec =
+ WorkspaceSpec(
+ maxAvailableSize = availableSpace,
+ specType = WorkspaceSpec.SpecType.HEIGHT,
+ startPadding = SizeSpec(fixedSize = 10f),
+ endPadding = SizeSpec(fixedSize = 10f),
+ gutter = SizeSpec(fixedSize = 10f),
+ cellSize = SizeSpec(fixedSize = 10f)
+ )
+ val calculatedWorkspaceSpec = CalculatedWorkspaceSpec(availableSpace, cells, workspaceSpec)
+
+ val resourceHelper = TestResourceHelper(context!!, R.xml.valid_folders_specs)
+ val folderSpecs = FolderSpecs(resourceHelper)
+ folderSpecs.getWidthSpec(cells, availableSpace, calculatedWorkspaceSpec)
+ }
+
+ @Test
+ fun retrievesCalculatedHeightSpec() {
+ val availableSpace = 700.dpToPx()
+ val cells = 3
+
+ val workspaceSpec =
+ WorkspaceSpec(
+ maxAvailableSize = availableSpace,
+ specType = WorkspaceSpec.SpecType.HEIGHT,
+ startPadding = SizeSpec(fixedSize = 10f),
+ endPadding = SizeSpec(fixedSize = 10f),
+ gutter = SizeSpec(fixedSize = 10f),
+ cellSize = SizeSpec(fixedSize = 10f)
+ )
+ val calculatedWorkspaceSpec = CalculatedWorkspaceSpec(availableSpace, cells, workspaceSpec)
+
+ val expectedResult =
+ CalculatedFolderSpec(
+ startPaddingPx = 24.dpToPx(),
+ endPaddingPx = 64.dpToPx(),
+ gutterPx = 16.dpToPx(),
+ cellSizePx = calculatedWorkspaceSpec.cellSizePx,
+ availableSpace = availableSpace,
+ cells = cells
+ )
+
+ val resourceHelper = TestResourceHelper(context!!, R.xml.valid_folders_specs)
+ val folderSpecs = FolderSpecs(resourceHelper)
+ val calculatedHeightSpec =
+ folderSpecs.getHeightSpec(cells, availableSpace, calculatedWorkspaceSpec)
+ assertThat(calculatedHeightSpec).isEqualTo(expectedResult)
+ }
+
+ @Test(expected = IllegalStateException::class)
+ fun retrievesCalculatedHeightSpec_invalidCalculatedWorkspaceSpecType_throwsError() {
+ val availableSpace = 10.dpToPx()
+ val cells = 3
+
+ val workspaceSpec =
+ WorkspaceSpec(
+ maxAvailableSize = availableSpace,
+ specType = WorkspaceSpec.SpecType.WIDTH,
+ startPadding = SizeSpec(fixedSize = 10f),
+ endPadding = SizeSpec(fixedSize = 10f),
+ gutter = SizeSpec(fixedSize = 10f),
+ cellSize = SizeSpec(fixedSize = 10f)
+ )
+ val calculatedWorkspaceSpec = CalculatedWorkspaceSpec(availableSpace, cells, workspaceSpec)
+
+ val resourceHelper = TestResourceHelper(context!!, R.xml.valid_folders_specs)
+ val folderSpecs = FolderSpecs(resourceHelper)
+ folderSpecs.getHeightSpec(cells, availableSpace, calculatedWorkspaceSpec)
+ }
+
+ private fun Float.dpToPx(): Float {
+ return ResourceUtils.pxFromDp(this, context!!.resources.displayMetrics).toFloat()
+ }
+
+ private fun Int.dpToPx(): Int {
+ return ResourceUtils.pxFromDp(this.toFloat(), context!!.resources.displayMetrics)
+ }
+}
diff --git a/tests/src/com/android/launcher3/responsive/SizeSpecTest.kt b/tests/src/com/android/launcher3/responsive/SizeSpecTest.kt
new file mode 100644
index 0000000..088cae1
--- /dev/null
+++ b/tests/src/com/android/launcher3/responsive/SizeSpecTest.kt
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.responsive
+
+import android.content.Context
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.launcher3.AbstractDeviceProfileTest
+import com.google.common.truth.Truth.assertThat
+import kotlin.math.roundToInt
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class SizeSpecTest : AbstractDeviceProfileTest() {
+ override val runningContext: Context = InstrumentationRegistry.getInstrumentation().context
+
+ @Before
+ fun setup() {
+ initializeVarsForPhone(deviceSpecs["phone"]!!)
+ }
+
+ @Test
+ fun valid_values() {
+ val combinations =
+ listOf(
+ SizeSpec(100f, 0f, 0f, false),
+ SizeSpec(0f, 1f, 0f, false),
+ SizeSpec(0f, 0f, 1f, false),
+ SizeSpec(0f, 0f, 0f, false),
+ SizeSpec(0f, 0f, 0f, true),
+ SizeSpec(100f, 0f, 0f, false, 100),
+ SizeSpec(0f, 1f, 0f, false, 100),
+ SizeSpec(0f, 0f, 1f, false, 100),
+ SizeSpec(0f, 0f, 0f, false, 100),
+ SizeSpec(0f, 0f, 0f, true, 100)
+ )
+
+ for (instance in combinations) {
+ assertThat(instance.isValid()).isEqualTo(true)
+ }
+ }
+
+ @Test
+ fun validate_getCalculatedValue() {
+ val availableSpace = 100
+ val matchWorkspaceValue = 101
+ val combinations =
+ listOf(
+ SizeSpec(100f) to 100,
+ SizeSpec(ofAvailableSpace = .5f) to (availableSpace * .5f).roundToInt(),
+ SizeSpec(ofRemainderSpace = .5f) to 0,
+ SizeSpec(matchWorkspace = true) to matchWorkspaceValue,
+ // Restricts max size up to 10 (calculated value > 10)
+ SizeSpec(100f, maxSize = 10) to 10,
+ SizeSpec(ofAvailableSpace = .5f, maxSize = 10) to 10,
+ SizeSpec(ofRemainderSpace = .5f, maxSize = 10) to 0,
+ SizeSpec(matchWorkspace = true, maxSize = 10) to 10
+ )
+
+ for ((sizeSpec, expectedValue) in combinations) {
+ val value = sizeSpec.getCalculatedValue(availableSpace, matchWorkspaceValue)
+ assertThat(value).isEqualTo(expectedValue)
+ }
+ }
+
+ @Test
+ fun validate_getRemainderSpaceValue() {
+ val remainderSpace = 100
+ val defaultValue = 50
+ val combinations =
+ listOf(
+ SizeSpec(100f) to defaultValue,
+ SizeSpec(ofAvailableSpace = .5f) to defaultValue,
+ SizeSpec(ofRemainderSpace = .5f) to (remainderSpace * .5f).roundToInt(),
+ SizeSpec(matchWorkspace = true) to defaultValue,
+ // Restricts max size up to 10 (defaultValue > 10)
+ SizeSpec(100f, maxSize = 10) to 10,
+ SizeSpec(ofAvailableSpace = .5f, maxSize = 10) to 10,
+ SizeSpec(ofRemainderSpace = .5f, maxSize = 10) to 10,
+ SizeSpec(matchWorkspace = true, maxSize = 10) to 10,
+ )
+
+ for ((sizeSpec, expectedValue) in combinations) {
+ val value = sizeSpec.getRemainderSpaceValue(remainderSpace, defaultValue)
+ assertThat(value).isEqualTo(expectedValue)
+ }
+ }
+
+ @Test
+ fun multiple_values_assigned() {
+ val combinations =
+ listOf(
+ SizeSpec(1f, 1f, 0f, false),
+ SizeSpec(1f, 0f, 1f, false),
+ SizeSpec(1f, 0f, 0f, true),
+ SizeSpec(0f, 1f, 1f, false),
+ SizeSpec(0f, 1f, 0f, true),
+ SizeSpec(0f, 0f, 1f, true),
+ SizeSpec(1f, 1f, 1f, true)
+ )
+
+ for (instance in combinations) {
+ assertThat(instance.isValid()).isEqualTo(false)
+ }
+ }
+
+ @Test
+ fun invalid_values() {
+ val combinations =
+ listOf(
+ SizeSpec(-1f, 0f, 0f, false),
+ SizeSpec(0f, 1.1f, 0f, false),
+ SizeSpec(0f, -0.1f, 0f, false),
+ SizeSpec(0f, 0f, 1.1f, false),
+ SizeSpec(0f, 0f, -0.1f, false),
+ SizeSpec(0f, 0f, 0f, false, -10),
+ SizeSpec(50f, 0f, 0f, false, 10)
+ )
+
+ for (instance in combinations) {
+ assertThat(instance.isValid()).isEqualTo(false)
+ }
+ }
+}
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 604fe42..1262a26 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -49,11 +49,8 @@
import androidx.test.uiautomator.Until;
import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherState;
import com.android.launcher3.Utilities;
-import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.tapl.HomeAllApps;
import com.android.launcher3.tapl.HomeAppIcon;
@@ -64,14 +61,15 @@
import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.util.LooperExecutor;
import com.android.launcher3.util.SimpleBroadcastReceiver;
+import com.android.launcher3.util.TestUtil;
import com.android.launcher3.util.Wait;
-import com.android.launcher3.util.WidgetUtils;
import com.android.launcher3.util.rule.FailureWatcher;
import com.android.launcher3.util.rule.LauncherActivityRule;
import com.android.launcher3.util.rule.SamplerRule;
import com.android.launcher3.util.rule.ScreenRecordRule;
import com.android.launcher3.util.rule.ShellCommandRule;
import com.android.launcher3.util.rule.TestStabilityRule;
+import com.android.launcher3.util.rule.ViewCaptureAnalysisRule;
import com.android.launcher3.util.rule.ViewCaptureRule;
import org.junit.After;
@@ -82,10 +80,6 @@
import org.junit.rules.TestRule;
import java.io.IOException;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -102,7 +96,7 @@
public static final long DEFAULT_ACTIVITY_TIMEOUT = TimeUnit.SECONDS.toMillis(10);
public static final long DEFAULT_BROADCAST_TIMEOUT_SECS = 5;
- public static final long DEFAULT_UI_TIMEOUT = 10000;
+ public static final long DEFAULT_UI_TIMEOUT = TestUtil.DEFAULT_UI_TIMEOUT;
private static final String TAG = "AbstractLauncherUiTest";
private static boolean sDumpWasGenerated = false;
@@ -209,18 +203,13 @@
mTargetContext.unregisterReceiver(broadcastReceiver);
}
- // Annotation for tests that need to be run in portrait and landscape modes.
- @Retention(RetentionPolicy.RUNTIME)
- @Target(ElementType.METHOD)
- protected @interface PortraitLandscape {
- }
-
protected TestRule getRulesInsideActivityMonitor() {
- final ViewCaptureRule viewCaptureRule = new ViewCaptureRule();
+ final ViewCaptureRule viewCaptureRule = new ViewCaptureRule(mActivityMonitor::getActivity);
final RuleChain inner = RuleChain
.outerRule(new PortraitLandscapeRunner(this))
+ .around(new FailureWatcher(mLauncher, viewCaptureRule::getViewCaptureData))
.around(viewCaptureRule)
- .around(new FailureWatcher(mDevice, mLauncher, viewCaptureRule.getViewCapture()));
+ .around(new ViewCaptureAnalysisRule(viewCaptureRule.getViewCapture()));
return TestHelpers.isInLauncherProcess()
? RuleChain.outerRule(ShellCommandRule.setDefaultLauncher()).around(inner)
@@ -309,40 +298,6 @@
}
/**
- * Removes all icons from homescreen and hotseat.
- */
- public void clearHomescreen() {
- LauncherSettings.Settings.call(mTargetContext.getContentResolver(),
- LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
- LauncherSettings.Settings.call(mTargetContext.getContentResolver(),
- LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG);
- resetLoaderState();
- }
-
- protected void resetLoaderState() {
- try {
- mMainThreadExecutor.execute(
- () -> LauncherAppState.getInstance(
- mTargetContext).getModel().forceReload());
- } catch (Throwable t) {
- throw new IllegalArgumentException(t);
- }
- mLauncher.waitForLauncherInitialized();
- }
-
- /**
- * Adds {@param item} on the homescreen on the 0th screen
- */
- public void addItemToScreen(ItemInfo item) {
- WidgetUtils.addItemToScreen(item, mTargetContext);
- resetLoaderState();
-
- // Launch the home activity
- mDevice.pressHome();
- mLauncher.waitForLauncherInitialized();
- }
-
- /**
* Runs the callback on the UI thread and returns the result.
*/
protected <T> T getOnUiThread(final Callable<T> callback) {
diff --git a/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java b/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
index 266f0ae..f0875f8 100644
--- a/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
+++ b/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
@@ -9,10 +9,21 @@
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
-class PortraitLandscapeRunner implements TestRule {
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+public class PortraitLandscapeRunner implements TestRule {
private static final String TAG = "PortraitLandscapeRunner";
private AbstractLauncherUiTest mTest;
+ // Annotation for tests that need to be run in portrait and landscape modes.
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.METHOD)
+ public @interface PortraitLandscape {
+ }
+
public PortraitLandscapeRunner(AbstractLauncherUiTest test) {
mTest = test;
}
@@ -20,7 +31,7 @@
@Override
public Statement apply(Statement base, Description description) {
if (!TestHelpers.isInLauncherProcess() ||
- description.getAnnotation(AbstractLauncherUiTest.PortraitLandscape.class) == null) {
+ description.getAnnotation(PortraitLandscape.class) == null) {
return base;
}
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index 0b10603..095b135 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -23,7 +23,6 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
@@ -54,8 +53,10 @@
import com.android.launcher3.tapl.HomeAppIconMenuItem;
import com.android.launcher3.tapl.Widgets;
import com.android.launcher3.tapl.Workspace;
+import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
import com.android.launcher3.util.LauncherLayoutBuilder;
import com.android.launcher3.util.TestUtil;
+import com.android.launcher3.util.Wait;
import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
import com.android.launcher3.util.rule.TISBindRule;
import com.android.launcher3.widget.picker.WidgetsFullSheet;
@@ -503,16 +504,11 @@
private void verifyAppUninstalledFromAllApps(Workspace workspace, String appName) {
final HomeAllApps allApps = workspace.switchToAllApps();
- allApps.freeze();
- try {
- assertNull(appName + " app was found on all apps after being uninstalled",
- allApps.tryGetAppIcon(appName));
- } finally {
- allApps.unfreeze();
- }
+ Wait.atMost(appName + " app was found on all apps after being uninstalled",
+ () -> allApps.tryGetAppIcon(appName) == null,
+ DEFAULT_UI_TIMEOUT, mLauncher);
}
- @Ignore("b/256615483")
@Test
@PortraitLandscape
@PlatinumTest(focusArea = "launcher")
@@ -536,7 +532,6 @@
Workspace workspace = mLauncher.getWorkspace();
final HomeAllApps allApps = workspace.switchToAllApps();
workspace = allApps.getAppIcon(DUMMY_APP_NAME).uninstall();
- waitForLauncherUIUpdate();
verifyAppUninstalledFromAllApps(workspace, DUMMY_APP_NAME);
} finally {
TestUtil.uninstallDummyApp();
diff --git a/tests/src/com/android/launcher3/ui/TestViewHelpers.java b/tests/src/com/android/launcher3/ui/TestViewHelpers.java
index 083f580..4b2bade 100644
--- a/tests/src/com/android/launcher3/ui/TestViewHelpers.java
+++ b/tests/src/com/android/launcher3/ui/TestViewHelpers.java
@@ -15,11 +15,14 @@
*/
package com.android.launcher3.ui;
-import static androidx.test.InstrumentationRegistry.getInstrumentation;
-import static androidx.test.InstrumentationRegistry.getTargetContext;
+import static android.os.Process.myUserHandle;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.launcher3.util.TestUtil.getOnUiThread;
+
+import android.app.Instrumentation;
import android.content.ComponentName;
-import android.os.Process;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
@@ -29,7 +32,6 @@
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.android.launcher3.widget.WidgetManagerHelper;
-import java.util.concurrent.Callable;
import java.util.function.Function;
public class TestViewHelpers {
@@ -38,23 +40,16 @@
/**
* Finds a widget provider which can fit on the home screen.
*
- * @param test test suite.
* @param hasConfigureScreen if true, a provider with a config screen is returned.
*/
- public static LauncherAppWidgetProviderInfo findWidgetProvider(AbstractLauncherUiTest test,
- final boolean hasConfigureScreen) {
- LauncherAppWidgetProviderInfo info =
- test.getOnUiThread(new Callable<LauncherAppWidgetProviderInfo>() {
- @Override
- public LauncherAppWidgetProviderInfo call() throws Exception {
- ComponentName cn = new ComponentName(getInstrumentation().getContext(),
- hasConfigureScreen ? AppWidgetWithConfig.class
- : AppWidgetNoConfig.class);
- Log.d(TAG, "findWidgetProvider componentName=" + cn.flattenToString());
- return new WidgetManagerHelper(getTargetContext())
- .findProvider(cn, Process.myUserHandle());
- }
- });
+ public static LauncherAppWidgetProviderInfo findWidgetProvider(boolean hasConfigureScreen) {
+ LauncherAppWidgetProviderInfo info = getOnUiThread(() -> {
+ Instrumentation i = getInstrumentation();
+ ComponentName cn = new ComponentName(i.getContext(),
+ hasConfigureScreen ? AppWidgetWithConfig.class : AppWidgetNoConfig.class);
+ Log.d(TAG, "findWidgetProvider componentName=" + cn.flattenToString());
+ return new WidgetManagerHelper(i.getTargetContext()).findProvider(cn, myUserHandle());
+ });
if (info == null) {
throw new IllegalArgumentException("No valid widget provider");
}
diff --git a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
index e9a2b0f..82bf644 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
@@ -30,10 +30,12 @@
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.launcher3.celllayout.FavoriteItemsTransaction;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.testcomponent.WidgetConfigActivity;
import com.android.launcher3.ui.AbstractLauncherUiTest;
+import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
import com.android.launcher3.ui.TestViewHelpers;
import com.android.launcher3.util.Wait;
import com.android.launcher3.util.rule.ShellCommandRule;
@@ -63,7 +65,7 @@
@Before
public void setUp() throws Exception {
super.setUp();
- mWidgetInfo = TestViewHelpers.findWidgetProvider(this, true /* hasConfigureScreen */);
+ mWidgetInfo = TestViewHelpers.findWidgetProvider(true /* hasConfigureScreen */);
mAppWidgetManager = AppWidgetManager.getInstance(mTargetContext);
}
@@ -84,8 +86,7 @@
* @param acceptConfig accept the config activity
*/
private void runTest(boolean acceptConfig) throws Throwable {
- clearHomescreen();
- mDevice.pressHome();
+ new FavoriteItemsTransaction(mTargetContext).commitAndLoadHome(mLauncher);
// Drag widget to homescreen
WidgetConfigStartupMonitor monitor = new WidgetConfigStartupMonitor();
diff --git a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
index 3eb20e3..435649b 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
@@ -22,13 +22,15 @@
import android.platform.test.annotations.PlatinumTest;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
+import com.android.launcher3.celllayout.FavoriteItemsTransaction;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.tapl.Widget;
import com.android.launcher3.tapl.WidgetResizeFrame;
import com.android.launcher3.ui.AbstractLauncherUiTest;
+import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
import com.android.launcher3.ui.TestViewHelpers;
import com.android.launcher3.util.rule.ShellCommandRule;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
@@ -52,13 +54,12 @@
@Test
@PortraitLandscape
public void testDragIcon() throws Throwable {
- clearHomescreen();
- mDevice.pressHome();
+ new FavoriteItemsTransaction(mTargetContext).commitAndLoadHome(mLauncher);
waitForLauncherCondition("Workspace didn't finish loading", l -> !l.isWorkspaceLoading());
final LauncherAppWidgetProviderInfo widgetInfo =
- TestViewHelpers.findWidgetProvider(this, false /* hasConfigureScreen */);
+ TestViewHelpers.findWidgetProvider(false /* hasConfigureScreen */);
WidgetResizeFrame resizeFrame = mLauncher
.getWorkspace()
@@ -90,8 +91,8 @@
@Test
@PortraitLandscape
public void testDragCustomShortcut() throws Throwable {
- clearHomescreen();
- mDevice.pressHome();
+ new FavoriteItemsTransaction(mTargetContext).commitAndLoadHome(mLauncher);
+
mLauncher.getWorkspace().openAllWidgets()
.getWidget("com.android.launcher3.testcomponent.CustomShortcutConfigActivity")
.dragToWorkspace(false, true);
diff --git a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
index 0f861eb..7db3161 100644
--- a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
@@ -15,8 +15,13 @@
*/
package com.android.launcher3.ui.widget;
-import static androidx.test.InstrumentationRegistry.getTargetContext;
-
+import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME;
+import static com.android.launcher3.WorkspaceLayoutManager.FIRST_SCREEN_ID;
+import static com.android.launcher3.model.data.LauncherAppWidgetInfo.FLAG_ID_NOT_VALID;
+import static com.android.launcher3.model.data.LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY;
+import static com.android.launcher3.model.data.LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
+import static com.android.launcher3.provider.LauncherDbUtils.itemIdMatch;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import static com.android.launcher3.util.WidgetUtils.createWidgetInfo;
import static org.junit.Assert.assertEquals;
@@ -26,7 +31,6 @@
import android.appwidget.AppWidgetManager;
import android.content.ComponentName;
-import android.content.ContentResolver;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionParams;
import android.content.pm.PackageManager;
@@ -34,11 +38,14 @@
import android.os.Bundle;
import android.widget.RemoteViews;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
+import com.android.launcher3.celllayout.FavoriteItemsTransaction;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.pm.InstallSessionHelper;
import com.android.launcher3.tapl.Widget;
@@ -57,6 +64,7 @@
import java.util.HashSet;
import java.util.Set;
+import java.util.function.Consumer;
/**
* Tests for bind widget flow.
@@ -70,24 +78,18 @@
@Rule
public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind();
- private ContentResolver mResolver;
-
// Objects created during test, which should be cleaned up in the end.
private Cursor mCursor;
// App install session id.
private int mSessionId = -1;
+ private LauncherModel mModel;
+
@Override
@Before
public void setUp() throws Exception {
super.setUp();
-
- mResolver = mTargetContext.getContentResolver();
-
- // Clear all existing data
- LauncherSettings.Settings.call(mResolver, LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
- LauncherSettings.Settings.call(mResolver,
- LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG);
+ mModel = LauncherAppState.getInstance(mTargetContext).getModel();
}
@After
@@ -103,34 +105,24 @@
@Test
public void testBindNormalWidget_withConfig() {
- LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, true);
- LauncherAppWidgetInfo item = createWidgetInfo(info, getTargetContext(), true);
-
- addItemToScreen(item);
+ LauncherAppWidgetProviderInfo info = addWidgetToScreen(true, true, i -> { });
verifyWidgetPresent(info);
}
@Test
public void testBindNormalWidget_withoutConfig() {
- LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false);
- LauncherAppWidgetInfo item = createWidgetInfo(info, getTargetContext(), true);
-
- addItemToScreen(item);
+ LauncherAppWidgetProviderInfo info = addWidgetToScreen(false, true, i -> { });
verifyWidgetPresent(info);
}
@Test
public void testUnboundWidget_removed() {
- LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false);
- LauncherAppWidgetInfo item = createWidgetInfo(info, getTargetContext(), false);
- item.appWidgetId = -33;
-
- addItemToScreen(item);
+ LauncherAppWidgetProviderInfo info = addWidgetToScreen(false, false,
+ item -> item.appWidgetId = -33);
final Workspace workspace = mLauncher.getWorkspace();
// Item deleted from db
- mCursor = mResolver.query(LauncherSettings.Favorites.getContentUri(item.id),
- null, null, null, null, null);
+ mCursor = queryItem();
assertEquals(0, mCursor.getCount());
// The view does not exist
@@ -140,36 +132,26 @@
@Test
public void testPendingWidget_autoRestored() {
// A non-restored widget with no config screen gets restored automatically.
- LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false);
-
// Do not bind the widget
- LauncherAppWidgetInfo item = createWidgetInfo(info, getTargetContext(), false);
- item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID;
-
- addItemToScreen(item);
+ LauncherAppWidgetProviderInfo info = addWidgetToScreen(false, false,
+ item -> item.restoreStatus = FLAG_ID_NOT_VALID);
verifyWidgetPresent(info);
}
@Test
public void testPendingWidget_withConfigScreen() {
// A non-restored widget with config screen get bound and shows a 'Click to setup' UI.
- LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, true);
-
// Do not bind the widget
- LauncherAppWidgetInfo item = createWidgetInfo(info, getTargetContext(), false);
- item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID;
-
- addItemToScreen(item);
+ LauncherAppWidgetProviderInfo info = addWidgetToScreen(true, false,
+ item -> item.restoreStatus = FLAG_ID_NOT_VALID);
verifyPendingWidgetPresent();
- // Item deleted from db
- mCursor = mResolver.query(LauncherSettings.Favorites.getContentUri(item.id),
- null, null, null, null, null);
+ mCursor = queryItem();
mCursor.moveToNext();
// Widget has a valid Id now.
assertEquals(0, mCursor.getInt(mCursor.getColumnIndex(LauncherSettings.Favorites.RESTORED))
- & LauncherAppWidgetInfo.FLAG_ID_NOT_VALID);
+ & FLAG_ID_NOT_VALID);
assertNotNull(AppWidgetManager.getInstance(mTargetContext)
.getAppWidgetInfo(mCursor.getInt(mCursor.getColumnIndex(
LauncherSettings.Favorites.APPWIDGET_ID))));
@@ -185,7 +167,6 @@
appWidgetManager.updateAppWidgetOptions(appWidgetId, b);
appWidgetManager.updateAppWidget(appWidgetId, remoteViews);
-
// verify changes are reflected
waitForLauncherCondition("App widget options did not update",
l -> appWidgetManager.getAppWidgetOptions(appWidgetId).getBoolean(
@@ -197,17 +178,12 @@
@Test
public void testPendingWidget_notRestored_removed() {
- LauncherAppWidgetInfo item = getInvalidWidgetInfo();
- item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID
- | LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY;
-
- addItemToScreen(item);
+ addPendingItemToScreen(getInvalidWidgetInfo(), FLAG_ID_NOT_VALID | FLAG_PROVIDER_NOT_READY);
assertTrue("Pending widget exists",
mLauncher.getWorkspace().tryGetPendingWidget(0) == null);
// Item deleted from db
- mCursor = mResolver.query(LauncherSettings.Favorites.getContentUri(item.id),
- null, null, null, null, null);
+ mCursor = queryItem();
assertEquals(0, mCursor.getCount());
}
@@ -215,32 +191,25 @@
public void testPendingWidget_notRestored_brokenInstall() {
// A widget which is was being installed once, even if its not being
// installed at the moment is not removed.
- LauncherAppWidgetInfo item = getInvalidWidgetInfo();
- item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID
- | LauncherAppWidgetInfo.FLAG_RESTORE_STARTED
- | LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY;
-
- addItemToScreen(item);
+ addPendingItemToScreen(getInvalidWidgetInfo(),
+ FLAG_ID_NOT_VALID | FLAG_RESTORE_STARTED | FLAG_PROVIDER_NOT_READY);
verifyPendingWidgetPresent();
// Verify item still exists in db
- mCursor = mResolver.query(LauncherSettings.Favorites.getContentUri(item.id),
- null, null, null, null, null);
+ mCursor = queryItem();
assertEquals(1, mCursor.getCount());
// Widget still has an invalid id.
mCursor.moveToNext();
- assertEquals(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID,
+ assertEquals(FLAG_ID_NOT_VALID,
mCursor.getInt(mCursor.getColumnIndex(LauncherSettings.Favorites.RESTORED))
- & LauncherAppWidgetInfo.FLAG_ID_NOT_VALID);
+ & FLAG_ID_NOT_VALID);
}
@Test
public void testPendingWidget_notRestored_activeInstall() throws Exception {
// A widget which is being installed is not removed
LauncherAppWidgetInfo item = getInvalidWidgetInfo();
- item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID
- | LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY;
// Create an active installer session
SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL);
@@ -248,19 +217,18 @@
PackageInstaller installer = mTargetContext.getPackageManager().getPackageInstaller();
mSessionId = installer.createSession(params);
- addItemToScreen(item);
+ addPendingItemToScreen(item, FLAG_ID_NOT_VALID | FLAG_PROVIDER_NOT_READY);
verifyPendingWidgetPresent();
// Verify item still exists in db
- mCursor = mResolver.query(LauncherSettings.Favorites.getContentUri(item.id),
- null, null, null, null, null);
+ mCursor = queryItem();
assertEquals(1, mCursor.getCount());
// Widget still has an invalid id.
mCursor.moveToNext();
- assertEquals(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID,
+ assertEquals(FLAG_ID_NOT_VALID,
mCursor.getInt(mCursor.getColumnIndex(LauncherSettings.Favorites.RESTORED))
- & LauncherAppWidgetInfo.FLAG_ID_NOT_VALID);
+ & FLAG_ID_NOT_VALID);
}
private void verifyWidgetPresent(LauncherAppWidgetProviderInfo info) {
@@ -275,6 +243,28 @@
widget != null);
}
+ private void addPendingItemToScreen(LauncherAppWidgetInfo item, int restoreStatus) {
+ item.restoreStatus = restoreStatus;
+ item.screenId = FIRST_SCREEN_ID;
+ new FavoriteItemsTransaction(mTargetContext)
+ .addItem(() -> item)
+ .commitAndLoadHome(mLauncher);
+ }
+
+ private LauncherAppWidgetProviderInfo addWidgetToScreen(boolean hasConfigureScreen,
+ boolean bindWidget, Consumer<LauncherAppWidgetInfo> itemOverride) {
+ LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(hasConfigureScreen);
+ new FavoriteItemsTransaction(mTargetContext)
+ .addItem(() -> {
+ LauncherAppWidgetInfo item = createWidgetInfo(info, mTargetContext, bindWidget);
+ item.screenId = FIRST_SCREEN_ID;
+ itemOverride.accept(item);
+ return item;
+ })
+ .commitAndLoadHome(mLauncher);
+ return info;
+ }
+
/**
* Returns a LauncherAppWidgetInfo with package name which is not present on the device
*/
@@ -312,4 +302,14 @@
item.container = LauncherSettings.Favorites.CONTAINER_DESKTOP;
return item;
}
+
+ private Cursor queryItem() {
+ try {
+ return MODEL_EXECUTOR.submit(() ->
+ mModel.getModelDbController().query(
+ TABLE_NAME, null, itemIdMatch(0), null, null)).get();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
}
diff --git a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
index bf9eb5a..384fa46 100644
--- a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
@@ -33,6 +33,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.celllayout.FavoriteItemsTransaction;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -134,8 +135,7 @@
private void runTest(String activityMethod, boolean isWidget, ItemOperator itemMatcher,
Intent... commandIntents) throws Throwable {
- clearHomescreen();
- mDevice.pressHome();
+ new FavoriteItemsTransaction(mTargetContext).commitAndLoadHome(mLauncher);
// Open Pin item activity
BlockingBroadcastReceiver openMonitor = new BlockingBroadcastReceiver(
diff --git a/tests/src/com/android/launcher3/ui/workspace/TwoPanelWorkspaceTest.java b/tests/src/com/android/launcher3/ui/workspace/TwoPanelWorkspaceTest.java
index c4b6d43..0b2f335 100644
--- a/tests/src/com/android/launcher3/ui/workspace/TwoPanelWorkspaceTest.java
+++ b/tests/src/com/android/launcher3/ui/workspace/TwoPanelWorkspaceTest.java
@@ -29,6 +29,7 @@
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.tapl.Workspace;
import com.android.launcher3.ui.AbstractLauncherUiTest;
+import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
import com.android.launcher3.ui.TaplTestsLauncher3;
import com.android.launcher3.util.LauncherLayoutBuilder;
import com.android.launcher3.util.TestUtil;
diff --git a/tests/src/com/android/launcher3/util/DisplayControllerTest.kt b/tests/src/com/android/launcher3/util/DisplayControllerTest.kt
new file mode 100644
index 0000000..8e4e998
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/DisplayControllerTest.kt
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.util
+
+import android.content.Context
+import android.content.res.Configuration
+import android.content.res.Resources
+import android.graphics.Point
+import android.graphics.Rect
+import android.hardware.display.DisplayManager
+import android.util.ArrayMap
+import android.util.DisplayMetrics
+import android.view.Display
+import android.view.Surface
+import androidx.test.annotation.UiThreadTest
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.LauncherPrefs
+import com.android.launcher3.util.DisplayController.CHANGE_DENSITY
+import com.android.launcher3.util.DisplayController.CHANGE_ROTATION
+import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener
+import com.android.launcher3.util.MainThreadInitializedObject.SandboxContext
+import com.android.launcher3.util.window.CachedDisplayInfo
+import com.android.launcher3.util.window.WindowManagerProxy
+import kotlin.math.min
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.doNothing
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+import org.mockito.stubbing.Answer
+
+/** Unit tests for {@link DisplayController} */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DisplayControllerTest {
+
+ private val appContext: Context = ApplicationProvider.getApplicationContext()
+
+ @Mock private lateinit var context: SandboxContext
+ @Mock private lateinit var windowManagerProxy: WindowManagerProxy
+ @Mock private lateinit var launcherPrefs: LauncherPrefs
+ @Mock private lateinit var displayManager: DisplayManager
+ @Mock private lateinit var display: Display
+ @Mock private lateinit var resources: Resources
+ @Mock private lateinit var displayInfoChangeListener: DisplayInfoChangeListener
+
+ private lateinit var displayController: DisplayController
+
+ private val width = 2208
+ private val height = 1840
+ private val inset = 110
+ private val densityDpi = 420
+ private val density = densityDpi / DisplayMetrics.DENSITY_DEFAULT.toFloat()
+ private val bounds =
+ arrayOf(
+ WindowBounds(Rect(0, 0, width, height), Rect(0, inset, 0, 0), Surface.ROTATION_0),
+ WindowBounds(Rect(0, 0, height, width), Rect(0, inset, 0, 0), Surface.ROTATION_90),
+ WindowBounds(Rect(0, 0, width, height), Rect(0, inset, 0, 0), Surface.ROTATION_180),
+ WindowBounds(Rect(0, 0, height, width), Rect(0, inset, 0, 0), Surface.ROTATION_270)
+ )
+ private val configuration =
+ Configuration(appContext.resources.configuration).apply {
+ densityDpi = this@DisplayControllerTest.densityDpi
+ screenWidthDp = (bounds[0].bounds.width() / density).toInt()
+ screenHeightDp = (bounds[0].bounds.height() / density).toInt()
+ smallestScreenWidthDp = min(screenWidthDp, screenHeightDp)
+ }
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ whenever(context.getObject(eq(WindowManagerProxy.INSTANCE))).thenReturn(windowManagerProxy)
+ whenever(context.getObject(eq(LauncherPrefs.INSTANCE))).thenReturn(launcherPrefs)
+
+ // Mock WindowManagerProxy
+ val displayInfo =
+ CachedDisplayInfo(Point(width, height), Surface.ROTATION_0, Rect(0, 0, 0, 0))
+ whenever(windowManagerProxy.getDisplayInfo(any())).thenReturn(displayInfo)
+ whenever(windowManagerProxy.estimateInternalDisplayBounds(any()))
+ .thenAnswer(
+ Answer {
+ // Always create a new copy of bounds
+ val perDisplayBounds = ArrayMap<CachedDisplayInfo, List<WindowBounds>>()
+ perDisplayBounds[displayInfo] = bounds.toList()
+ return@Answer perDisplayBounds
+ }
+ )
+ whenever(windowManagerProxy.getRealBounds(any(), any())).thenAnswer { i ->
+ bounds[i.getArgument<CachedDisplayInfo>(1).rotation]
+ }
+
+ // Mock context
+ whenever(context.createWindowContext(any(), any(), nullable())).thenReturn(context)
+ whenever(context.getSystemService(eq(DisplayManager::class.java)))
+ .thenReturn(displayManager)
+ doNothing().`when`(context).registerComponentCallbacks(any())
+
+ // Mock display
+ whenever(display.rotation).thenReturn(displayInfo.rotation)
+ whenever(context.display).thenReturn(display)
+ whenever(displayManager.getDisplay(any())).thenReturn(display)
+
+ // Mock resources
+ whenever(resources.configuration).thenReturn(configuration)
+ whenever(context.resources).thenReturn(resources)
+
+ // Initialize DisplayController
+ displayController = DisplayController(context)
+ displayController.addChangeListener(displayInfoChangeListener)
+ }
+
+ @Test
+ @UiThreadTest
+ fun testRotation() {
+ val displayInfo =
+ CachedDisplayInfo(Point(height, width), Surface.ROTATION_90, Rect(0, 0, 0, 0))
+ whenever(windowManagerProxy.getDisplayInfo(any())).thenReturn(displayInfo)
+ whenever(display.rotation).thenReturn(displayInfo.rotation)
+ val configuration =
+ Configuration(configuration).apply {
+ screenWidthDp = configuration.screenHeightDp
+ screenHeightDp = configuration.screenWidthDp
+ }
+ whenever(resources.configuration).thenReturn(configuration)
+
+ displayController.onConfigurationChanged(configuration)
+
+ verify(displayInfoChangeListener).onDisplayInfoChanged(any(), any(), eq(CHANGE_ROTATION))
+ }
+
+ @Test
+ @UiThreadTest
+ fun testFontScale() {
+ val configuration = Configuration(configuration).apply { fontScale = 1.2f }
+ whenever(resources.configuration).thenReturn(configuration)
+
+ displayController.onConfigurationChanged(configuration)
+
+ verify(displayInfoChangeListener).onDisplayInfoChanged(any(), any(), eq(CHANGE_DENSITY))
+ }
+}
diff --git a/tests/src/com/android/launcher3/util/IconSizeStepsTest.kt b/tests/src/com/android/launcher3/util/IconSizeStepsTest.kt
new file mode 100644
index 0000000..d9e3377
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/IconSizeStepsTest.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.util
+
+import android.content.Context
+import android.content.res.Configuration
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.*
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class IconSizeStepsTest {
+ private var context: Context? = null
+ private val runningContext: Context = ApplicationProvider.getApplicationContext()
+ private lateinit var iconSizeSteps: IconSizeSteps
+
+ @Before
+ fun setup() {
+ // 160dp makes 1px = 1dp
+ val config =
+ Configuration(runningContext.resources.configuration).apply { this.densityDpi = 160 }
+ context = runningContext.createConfigurationContext(config)
+ iconSizeSteps = IconSizeSteps(context!!.resources)
+ }
+
+ @Test
+ fun minimumIconSize() {
+ assertThat(iconSizeSteps.minimumIconSize()).isEqualTo(52)
+ }
+
+ @Test
+ fun getNextLowerIconSize() {
+ assertThat(iconSizeSteps.getNextLowerIconSize(66)).isEqualTo(63)
+
+ // Assert that never goes below minimum
+ assertThat(iconSizeSteps.getNextLowerIconSize(52)).isEqualTo(52)
+ assertThat(iconSizeSteps.getNextLowerIconSize(30)).isEqualTo(52)
+ }
+
+ @Test
+ fun getIconSmallerThan() {
+ assertThat(iconSizeSteps.getIconSmallerThan(60)).isEqualTo(59)
+
+ // Assert that never goes below minimum
+ assertThat(iconSizeSteps.getIconSmallerThan(52)).isEqualTo(52)
+ assertThat(iconSizeSteps.getIconSmallerThan(30)).isEqualTo(52)
+ }
+}
diff --git a/tests/src/com/android/launcher3/util/LauncherModelHelper.java b/tests/src/com/android/launcher3/util/LauncherModelHelper.java
index 976afcd..4580082 100644
--- a/tests/src/com/android/launcher3/util/LauncherModelHelper.java
+++ b/tests/src/com/android/launcher3/util/LauncherModelHelper.java
@@ -15,34 +15,31 @@
*/
package com.android.launcher3.util;
-import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+import static android.content.pm.PackageInstaller.SessionParams.MODE_FULL_INSTALL;
+
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-import static com.android.launcher3.LauncherSettings.Favorites.CONTENT_URI;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+import static com.android.launcher3.util.TestUtil.runOnExecutorSync;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import android.content.ComponentName;
import android.content.ContentProvider;
import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageInstaller.SessionParams;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
-import android.content.res.Resources;
-import android.database.sqlite.SQLiteDatabase;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.Color;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.os.ParcelFileDescriptor.AutoCloseOutputStream;
-import android.os.Process;
import android.provider.Settings;
import android.test.mock.MockContentResolver;
import android.util.ArrayMap;
@@ -56,14 +53,10 @@
import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherModel.ModelUpdateTask;
import com.android.launcher3.LauncherPrefs;
-import com.android.launcher3.LauncherProvider;
-import com.android.launcher3.LauncherSettings;
import com.android.launcher3.model.AllAppsList;
import com.android.launcher3.model.BgDataModel;
import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.model.ItemInstallQueue;
-import com.android.launcher3.model.data.AppInfo;
-import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.pm.InstallSessionHelper;
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.testing.TestInformationProvider;
@@ -72,37 +65,25 @@
import com.android.launcher3.util.window.WindowManagerProxy;
import com.android.launcher3.widget.custom.CustomWidgetManager;
-import org.mockito.ArgumentCaptor;
-
-import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
-import java.io.InputStreamReader;
+import java.io.IOException;
import java.io.OutputStreamWriter;
-import java.lang.reflect.Field;
-import java.util.HashMap;
-import java.util.List;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
-import java.util.function.Function;
/**
* Utility class to help manage Launcher Model and related objects for test.
*/
public class LauncherModelHelper {
- public static final int DESKTOP = LauncherSettings.Favorites.CONTAINER_DESKTOP;
- public static final int HOTSEAT = LauncherSettings.Favorites.CONTAINER_HOTSEAT;
-
- public static final int APP_ICON = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
- public static final int SHORTCUT = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
- public static final int NO__ICON = -1;
-
- public static final String TEST_PACKAGE = testContext().getPackageName();
+ public static final String TEST_PACKAGE = getInstrumentation().getContext().getPackageName();
public static final String TEST_ACTIVITY = "com.android.launcher3.tests.Activity2";
+ public static final String TEST_ACTIVITY2 = "com.android.launcher3.tests.Activity3";
+ public static final String TEST_ACTIVITY3 = "com.android.launcher3.tests.Activity4";
// Authority for providing a test default-workspace-layout data.
private static final String TEST_PROVIDER_AUTHORITY =
@@ -110,39 +91,18 @@
private static final int DEFAULT_BITMAP_SIZE = 10;
private static final int DEFAULT_GRID_SIZE = 4;
- private final HashMap<Class, HashMap<String, Field>> mFieldCache = new HashMap<>();
- private final MockContentResolver mMockResolver = new MockContentResolver();
- public final TestLauncherProvider provider;
public final SandboxModelContext sandboxContext;
- public final long defaultProfileId;
+ private final RunnableList mDestroyTask = new RunnableList();
private BgDataModel mDataModel;
- private AllAppsList mAllAppsList;
public LauncherModelHelper() {
- Context context = getApplicationContext();
- // System settings cache content provider. Ensure that they are statically initialized
- Settings.Secure.getString(context.getContentResolver(), "test");
- Settings.System.getString(context.getContentResolver(), "test");
- Settings.Global.getString(context.getContentResolver(), "test");
-
- provider = new TestLauncherProvider();
sandboxContext = new SandboxModelContext();
- defaultProfileId = UserCache.INSTANCE.get(sandboxContext)
- .getSerialNumberForUser(Process.myUserHandle());
- setupProvider(LauncherProvider.AUTHORITY, provider);
}
public void setupProvider(String authority, ContentProvider provider) {
- ProviderInfo providerInfo = new ProviderInfo();
- providerInfo.authority = authority;
- providerInfo.applicationInfo = sandboxContext.getApplicationInfo();
- provider.attachInfo(sandboxContext, providerInfo);
- mMockResolver.addProvider(providerInfo.authority, provider);
- doReturn(providerInfo)
- .when(sandboxContext.mPm)
- .resolveContentProvider(eq(authority), anyInt());
+ sandboxContext.setupProvider(authority, provider);
}
public LauncherModel getModel() {
@@ -151,16 +111,35 @@
public synchronized BgDataModel getBgDataModel() {
if (mDataModel == null) {
- mDataModel = ReflectionHelpers.getField(getModel(), "mBgDataModel");
+ getModel().enqueueModelUpdateTask(new ModelUpdateTask() {
+ @Override
+ public void init(@NonNull LauncherAppState app, @NonNull LauncherModel model,
+ @NonNull BgDataModel dataModel, @NonNull AllAppsList allAppsList,
+ @NonNull Executor uiExecutor) {
+ mDataModel = dataModel;
+ }
+
+ @Override
+ public void run() { }
+ });
}
return mDataModel;
}
- public synchronized AllAppsList getAllAppsList() {
- if (mAllAppsList == null) {
- mAllAppsList = ReflectionHelpers.getField(getModel(), "mBgAllAppsList");
- }
- return mAllAppsList;
+ /**
+ * Creates a installer session for the provided package.
+ */
+ public int createInstallerSession(String pkg) throws IOException {
+ SessionParams sp = new SessionParams(MODE_FULL_INSTALL);
+ sp.setAppPackageName(pkg);
+ Bitmap icon = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
+ icon.eraseColor(Color.RED);
+ sp.setAppIcon(icon);
+ sp.setAppLabel(pkg);
+ PackageInstaller pi = sandboxContext.getPackageManager().getPackageInstaller();
+ int sessionId = pi.createSession(sp);
+ mDestroyTask.add(() -> pi.abandonSession(sessionId));
+ return sessionId;
}
public void destroy() {
@@ -175,6 +154,8 @@
waitOrThrow(l1);
sandboxContext.onDestroy();
l2.countDown();
+
+ mDestroyTask.executeAllAndDestroy();
}
private void waitOrThrow(CountDownLatch latch) {
@@ -186,184 +167,6 @@
}
/**
- * Synchronously executes the task and returns all the UI callbacks posted.
- */
- public List<Runnable> executeTaskForTest(ModelUpdateTask task) throws Exception {
- LauncherModel model = getModel();
- if (!model.isModelLoaded()) {
- ReflectionHelpers.setField(model, "mModelLoaded", true);
- }
- Executor mockExecutor = mock(Executor.class);
- model.enqueueModelUpdateTask(new ModelUpdateTask() {
- @Override
- public void init(@NonNull final LauncherAppState app,
- @NonNull final LauncherModel model, @NonNull final BgDataModel dataModel,
- @NonNull final AllAppsList allAppsList, @NonNull final Executor uiExecutor) {
- task.init(app, model, dataModel, allAppsList, mockExecutor);
- }
-
- @Override
- public void run() {
- task.run();
- }
- });
- MODEL_EXECUTOR.submit(() -> null).get();
-
- ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
- verify(mockExecutor, atLeast(0)).execute(captor.capture());
- return captor.getAllValues();
- }
-
- /**
- * Synchronously executes a task on the model
- */
- public <T> T executeSimpleTask(Function<BgDataModel, T> task) throws Exception {
- BgDataModel dataModel = getBgDataModel();
- return MODEL_EXECUTOR.submit(() -> task.apply(dataModel)).get();
- }
-
- /**
- * Initializes mock data for the test.
- */
- public void initializeData(String resourceName) throws Exception {
- BgDataModel bgDataModel = getBgDataModel();
- AllAppsList allAppsList = getAllAppsList();
-
- MODEL_EXECUTOR.submit(() -> {
- // Copy apk from resources to a local file and install from there.
- Resources resources = testContext().getResources();
- int resId = resources.getIdentifier(
- resourceName, "raw", testContext().getPackageName());
- try (BufferedReader reader = new BufferedReader(new InputStreamReader(
- resources.openRawResource(resId)))) {
- String line;
- HashMap<String, Class> classMap = new HashMap<>();
- while ((line = reader.readLine()) != null) {
- line = line.trim();
- if (line.startsWith("#") || line.isEmpty()) {
- continue;
- }
- String[] commands = line.split(" ");
- switch (commands[0]) {
- case "classMap":
- classMap.put(commands[1], Class.forName(commands[2]));
- break;
- case "bgItem":
- bgDataModel.addItem(sandboxContext,
- (ItemInfo) initItem(classMap.get(commands[1]), commands, 2),
- false);
- break;
- case "allApps":
- allAppsList.add((AppInfo) initItem(AppInfo.class, commands, 1), null);
- break;
- }
- }
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }).get();
- }
-
- private Object initItem(Class clazz, String[] fieldDef, int startIndex) throws Exception {
- HashMap<String, Field> cache = mFieldCache.get(clazz);
- if (cache == null) {
- cache = new HashMap<>();
- Class c = clazz;
- while (c != null) {
- for (Field f : c.getDeclaredFields()) {
- f.setAccessible(true);
- cache.put(f.getName(), f);
- }
- c = c.getSuperclass();
- }
- mFieldCache.put(clazz, cache);
- }
-
- Object item = clazz.newInstance();
- for (int i = startIndex; i < fieldDef.length; i++) {
- String[] fieldData = fieldDef[i].split("=", 2);
- Field f = cache.get(fieldData[0]);
- Class type = f.getType();
- if (type == int.class || type == long.class) {
- f.set(item, Integer.parseInt(fieldData[1]));
- } else if (type == CharSequence.class || type == String.class) {
- f.set(item, fieldData[1]);
- } else if (type == Intent.class) {
- if (!fieldData[1].startsWith("#Intent")) {
- fieldData[1] = "#Intent;" + fieldData[1] + ";end";
- }
- f.set(item, Intent.parseUri(fieldData[1], 0));
- } else if (type == ComponentName.class) {
- f.set(item, ComponentName.unflattenFromString(fieldData[1]));
- } else {
- throw new Exception("Added parsing logic for "
- + f.getName() + " of type " + f.getType());
- }
- }
- return item;
- }
-
- public int addItem(int type, int screen, int container, int x, int y) {
- return addItem(type, screen, container, x, y, defaultProfileId, TEST_PACKAGE);
- }
-
- public int addItem(int type, int screen, int container, int x, int y, long profileId) {
- return addItem(type, screen, container, x, y, profileId, TEST_PACKAGE);
- }
-
- public int addItem(int type, int screen, int container, int x, int y, String packageName) {
- return addItem(type, screen, container, x, y, defaultProfileId, packageName);
- }
-
- public int addItem(int type, int screen, int container, int x, int y, String packageName,
- int id, Uri contentUri) {
- addItem(type, screen, container, x, y, defaultProfileId, packageName, id, contentUri);
- return id;
- }
-
- /**
- * Adds a mock item in the DB.
- * @param type {@link #APP_ICON} or {@link #SHORTCUT} or >= 2 for
- * folder (where the type represents the number of items in the folder).
- */
- public int addItem(int type, int screen, int container, int x, int y, long profileId,
- String packageName) {
- int id = LauncherSettings.Settings.call(sandboxContext.getContentResolver(),
- LauncherSettings.Settings.METHOD_NEW_ITEM_ID)
- .getInt(LauncherSettings.Settings.EXTRA_VALUE);
- addItem(type, screen, container, x, y, profileId, packageName, id, CONTENT_URI);
- return id;
- }
-
- public void addItem(int type, int screen, int container, int x, int y, long profileId,
- String packageName, int id, Uri contentUri) {
- ContentValues values = new ContentValues();
- values.put(LauncherSettings.Favorites._ID, id);
- values.put(LauncherSettings.Favorites.CONTAINER, container);
- values.put(LauncherSettings.Favorites.SCREEN, screen);
- values.put(LauncherSettings.Favorites.CELLX, x);
- values.put(LauncherSettings.Favorites.CELLY, y);
- values.put(LauncherSettings.Favorites.SPANX, 1);
- values.put(LauncherSettings.Favorites.SPANY, 1);
- values.put(LauncherSettings.Favorites.PROFILE_ID, profileId);
-
- if (type == APP_ICON || type == SHORTCUT) {
- values.put(LauncherSettings.Favorites.ITEM_TYPE, type);
- values.put(LauncherSettings.Favorites.INTENT,
- new Intent(Intent.ACTION_MAIN).setPackage(packageName).toUri(0));
- } else {
- values.put(LauncherSettings.Favorites.ITEM_TYPE,
- LauncherSettings.Favorites.ITEM_TYPE_FOLDER);
- // Add folder items.
- for (int i = 0; i < type; i++) {
- addItem(APP_ICON, 0, id, 0, 0, profileId);
- }
- }
-
- sandboxContext.getContentResolver().insert(contentUri, values);
- }
-
- /**
* Sets up a mock provider to load the provided layout by default, next time the layout loads
*/
public LauncherModelHelper setupDefaultLayoutProvider(LauncherLayoutBuilder builder)
@@ -394,6 +197,9 @@
}
};
setupProvider(TEST_PROVIDER_AUTHORITY, cp);
+ mDestroyTask.add(() -> runOnExecutorSync(MODEL_EXECUTOR, () ->
+ UiDevice.getInstance(getInstrumentation()).executeShellCommand(
+ "settings delete secure launcher3.layout.provider")));
return this;
}
@@ -402,46 +208,16 @@
*/
public void loadModelSync() throws ExecutionException, InterruptedException {
Callbacks mockCb = new Callbacks() { };
- Executors.MAIN_EXECUTOR.submit(() -> getModel().addCallbacksAndLoad(mockCb)).get();
+ MAIN_EXECUTOR.submit(() -> getModel().addCallbacksAndLoad(mockCb)).get();
Executors.MODEL_EXECUTOR.submit(() -> { }).get();
- Executors.MAIN_EXECUTOR.submit(() -> { }).get();
- Executors.MAIN_EXECUTOR.submit(() -> getModel().removeCallbacks(mockCb)).get();
+ MAIN_EXECUTOR.submit(() -> { }).get();
+ MAIN_EXECUTOR.submit(() -> getModel().removeCallbacks(mockCb)).get();
}
- /**
- * An extension of LauncherProvider backed up by in-memory database.
- */
- public static class TestLauncherProvider extends LauncherProvider {
+ public static class SandboxModelContext extends SandboxContext {
- @Override
- public boolean onCreate() {
- return true;
- }
-
- public SQLiteDatabase getDb() {
- return getModelDbController().getDb();
- }
- }
-
- public static boolean deleteContents(File dir) {
- File[] files = dir.listFiles();
- boolean success = true;
- if (files != null) {
- for (File file : files) {
- if (file.isDirectory()) {
- success &= deleteContents(file);
- }
- if (!file.delete()) {
- success = false;
- }
- }
- }
- return success;
- }
-
- public class SandboxModelContext extends SandboxContext {
-
+ private final MockContentResolver mMockResolver = new MockContentResolver();
private final ArrayMap<String, Object> mSpiedServices = new ArrayMap<>();
private final PackageManager mPm;
private final File mDbDir;
@@ -453,10 +229,27 @@
DisplayController.INSTANCE, CustomWidgetManager.INSTANCE,
SettingsCache.INSTANCE, PluginManagerWrapper.INSTANCE, LockedUserState.INSTANCE,
ItemInstallQueue.INSTANCE, WindowManagerProxy.INSTANCE);
+
+ // System settings cache content provider. Ensure that they are statically initialized
+ Settings.Secure.getString(
+ ApplicationProvider.getApplicationContext().getContentResolver(), "test");
+ Settings.System.getString(
+ ApplicationProvider.getApplicationContext().getContentResolver(), "test");
+ Settings.Global.getString(
+ ApplicationProvider.getApplicationContext().getContentResolver(), "test");
+
mPm = spy(getBaseContext().getPackageManager());
mDbDir = new File(getCacheDir(), UUID.randomUUID().toString());
}
+ @Override
+ protected <T> T createObject(MainThreadInitializedObject<T> object) {
+ if (object == LauncherAppState.INSTANCE) {
+ return (T) new LauncherAppState(this, null /* iconCacheFileName */);
+ }
+ return super.createObject(object);
+ }
+
public SandboxModelContext allow(MainThreadInitializedObject object) {
mAllowedObjects.add(object);
return this;
@@ -505,9 +298,30 @@
mSpiedServices.put(name, result);
return result;
}
- }
- private static Context testContext() {
- return getInstrumentation().getContext();
+ public void setupProvider(String authority, ContentProvider provider) {
+ ProviderInfo providerInfo = new ProviderInfo();
+ providerInfo.authority = authority;
+ providerInfo.applicationInfo = getApplicationInfo();
+ provider.attachInfo(this, providerInfo);
+ mMockResolver.addProvider(providerInfo.authority, provider);
+ doReturn(providerInfo).when(mPm).resolveContentProvider(eq(authority), anyInt());
+ }
+
+ private static boolean deleteContents(File dir) {
+ File[] files = dir.listFiles();
+ boolean success = true;
+ if (files != null) {
+ for (File file : files) {
+ if (file.isDirectory()) {
+ success &= deleteContents(file);
+ }
+ if (!file.delete()) {
+ success = false;
+ }
+ }
+ }
+ return success;
+ }
}
}
diff --git a/tests/src/com/android/launcher3/util/LockedUserStateTest.kt b/tests/src/com/android/launcher3/util/LockedUserStateTest.kt
index 84156e7..92ab2cb 100644
--- a/tests/src/com/android/launcher3/util/LockedUserStateTest.kt
+++ b/tests/src/com/android/launcher3/util/LockedUserStateTest.kt
@@ -32,7 +32,7 @@
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
-/** Unit tests for {@link LockedUserUtil} */
+/** Unit tests for {@link LockedUserState} */
@SmallTest
@RunWith(AndroidJUnit4::class)
class LockedUserStateTest {
@@ -49,40 +49,31 @@
@Test
fun runOnUserUnlocked_runs_action_immediately_if_already_unlocked() {
`when`(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(true)
- LockedUserState.INSTANCE.initializeForTesting(LockedUserState(context))
val action: Runnable = mock()
-
- LockedUserState.get(context).runOnUserUnlocked(action)
+ LockedUserState(context).runOnUserUnlocked(action)
verify(action).run()
}
@Test
fun runOnUserUnlocked_waits_to_run_action_until_user_is_unlocked() {
`when`(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(false)
- LockedUserState.INSTANCE.initializeForTesting(LockedUserState(context))
val action: Runnable = mock()
-
- LockedUserState.get(context).runOnUserUnlocked(action)
+ val state = LockedUserState(context)
+ state.runOnUserUnlocked(action)
verifyZeroInteractions(action)
-
- LockedUserState.get(context)
- .mUserUnlockedReceiver
- .onReceive(context, Intent(Intent.ACTION_USER_UNLOCKED))
-
+ state.mUserUnlockedReceiver.onReceive(context, Intent(Intent.ACTION_USER_UNLOCKED))
verify(action).run()
}
@Test
fun isUserUnlocked_returns_true_when_user_is_unlocked() {
`when`(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(true)
- LockedUserState.INSTANCE.initializeForTesting(LockedUserState(context))
- assertThat(LockedUserState.get(context).isUserUnlocked).isTrue()
+ assertThat(LockedUserState(context).isUserUnlocked).isTrue()
}
@Test
fun isUserUnlocked_returns_false_when_user_is_locked() {
`when`(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(false)
- LockedUserState.INSTANCE.initializeForTesting(LockedUserState(context))
- assertThat(LockedUserState.get(context).isUserUnlocked).isFalse()
+ assertThat(LockedUserState(context).isUserUnlocked).isFalse()
}
}
diff --git a/tests/src/com/android/launcher3/util/RaceConditionReproducer.java b/tests/src/com/android/launcher3/util/RaceConditionReproducer.java
deleted file mode 100644
index ed2ec7b..0000000
--- a/tests/src/com/android/launcher3/util/RaceConditionReproducer.java
+++ /dev/null
@@ -1,500 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.util;
-
-import static com.android.launcher3.util.Executors.createAndStartNewLooper;
-
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import android.os.Handler;
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Event processor for reliably reproducing multithreaded apps race conditions in tests.
- *
- * The app notifies us about “events” that happen in its threads. The race condition test runs the
- * test action multiple times (aka iterations), trying to generate all possible permutations of
- * these events. It keeps a set of all seen event sequences and steers the execution towards
- * executing events in previously unseen order. It does it by postponing execution of threads that
- * would lead to an already seen sequence.
- *
- * If an event A occurs before event B in the sequence, this is how execution order looks like:
- * Events: ... A ... B ...
- * Events and instructions, guaranteed order:
- * (instructions executed prior to A) A ... B (instructions executed after B)
- *
- * Each iteration has 3 parts (phases).
- * Phase 1. Picking a previously seen event subsequence that we believe can have previously unseen
- * continuations. Reproducing this sequence by pausing threads that would lead to other sequences.
- * Phase 2. Trying to generate previously unseen continuation of the sequence from Phase 1. We need
- * one new event after that sequence. All threads leading to seen continuations will be postponed
- * for some short period of time. The phase ends once the new event is registered, or after the
- * period of time ends (in which case we declare that the sequence can’t have new continuations).
- * Phase 3. Releasing all threads and letting the test iteration run till its end.
- *
- * The iterations end when all seen paths have been declared “uncontinuable”.
- *
- * When we register event XXX:enter, we hold all other events until we register XXX:exit.
- */
-public class RaceConditionReproducer {
- private static final String TAG = "RaceConditionReproducer";
-
- private static final boolean ENTER = true;
- private static final boolean EXIT = false;
- private static final String ENTER_POSTFIX = "enter";
- private static final String EXIT_POSTFIX = "exit";
-
- private static final long SHORT_TIMEOUT_MS = 2000;
- private static final long LONG_TIMEOUT_MS = 60000;
- // Handler used to resume postponed events.
- private static final Handler POSTPONED_EVENT_RESUME_HANDLER =
- new Handler(createAndStartNewLooper("RaceConditionEventResumer"));
-
- public static String enterExitEvt(String eventName, boolean isEnter) {
- return eventName + ":" + (isEnter ? ENTER_POSTFIX : EXIT_POSTFIX);
- }
-
- public static String enterEvt(String eventName) {
- return enterExitEvt(eventName, ENTER);
- }
-
- public static String exitEvt(String eventName) {
- return enterExitEvt(eventName, EXIT);
- }
-
- /**
- * Event in a particular sequence of events. A node in the prefix tree of all seen event
- * sequences.
- */
- private class EventNode {
- // Events that were seen just after this event.
- private final Map<String, EventNode> mNextEvents = new HashMap<>();
- // Whether we believe that further iterations will not be able to add more events to
- // mNextEvents.
- private boolean mStoppedAddingChildren = true;
-
- private void debugDump(StringBuilder sb, int indent, String name) {
- for (int i = 0; i < indent; ++i) sb.append('.');
- sb.append(!mStoppedAddingChildren ? "+" : "-");
- sb.append(" : ");
- sb.append(name);
- if (mLastRegisteredEvent == this) sb.append(" <");
- sb.append('\n');
-
- for (String key : mNextEvents.keySet()) {
- mNextEvents.get(key).debugDump(sb, indent + 2, key);
- }
- }
-
- /** Number of leaves in the subtree with this node as a root. */
- private int numberOfLeafNodes() {
- if (mNextEvents.isEmpty()) return 1;
-
- int leaves = 0;
- for (String event : mNextEvents.keySet()) {
- leaves += mNextEvents.get(event).numberOfLeafNodes();
- }
- return leaves;
- }
-
- /**
- * Whether we believe that further iterations will not be able add nodes to the subtree with
- * this node as a root.
- */
- private boolean stoppedAddingChildrenToTree() {
- if (!mStoppedAddingChildren) return false;
-
- for (String event : mNextEvents.keySet()) {
- if (!mNextEvents.get(event).stoppedAddingChildrenToTree()) return false;
- }
- return true;
- }
-
- /**
- * In the subtree with this node as a root, tries finding a node where we may have a
- * chance to add new children.
- * If succeeds, returns true and fills 'path' with the sequence of events to that node;
- * otherwise returns false.
- */
- private boolean populatePathToGrowthPoint(List<String> path) {
- for (String event : mNextEvents.keySet()) {
- if (mNextEvents.get(event).populatePathToGrowthPoint(path)) {
- path.add(0, event);
- return true;
- }
- }
- if (!mStoppedAddingChildren) {
- // Mark that we have finished adding children. It will remain true if no new
- // children are added, or will be set to false upon adding a new child.
- mStoppedAddingChildren = true;
- return true;
- }
- return false;
- }
- }
-
- // Starting point of all event sequences; the root of the prefix tree representation all
- // sequences generated by test iterations. A test iteration can add nodes int it.
- private EventNode mRoot = new EventNode();
- // During a test iteration, the last event that was registered.
- private EventNode mLastRegisteredEvent;
- // Length of the current sequence of registered events for the current test iteration.
- private int mRegisteredEventCount = 0;
- // During the first part of a test iteration, we go to a specific node under mRoot by
- // 'playing back' mSequenceToFollow. During this part, all events that don't belong to this
- // sequence get postponed.
- private List<String> mSequenceToFollow = new ArrayList<>();
- // Collection of events that got postponed, with corresponding wait objects used to let them go.
- private Map<String, Semaphore> mPostponedEvents = new HashMap<>();
- // Callback to run by POSTPONED_EVENT_RESUME_HANDLER, used to let go of all currently
- // postponed events.
- private Runnable mResumeAllEventsCallback;
- // String representation of the sequence of events registered so far for the current test
- // iteration. After registering any event, we output it to the log. The last output before
- // the test failure can be later played back to reliable reproduce the exact sequence of
- // events that broke the test.
- // Format: EV1|EV2|...\EVN
- private StringBuilder mCurrentSequence;
- // When not null, we are in a repro mode. We run only one test iteration, and are trying to
- // reproduce the event sequence represented by this string. The format is same as for
- // mCurrentSequence.
- private final String mReproString;
-
- /* Constructor for a normal test. */
- public RaceConditionReproducer() {
- mReproString = null;
- }
-
- /**
- * Constructor for reliably reproducing a race condition failure. The developer should find in
- * the log the latest "Repro sequence:" record and locally modify the test by passing that
- * string to the constructor. Running the test will have only one iteration that will reliably
- * "play back" that sequence.
- */
- public RaceConditionReproducer(String reproString) {
- mReproString = reproString;
- }
-
- public RaceConditionReproducer(String... reproSequence) {
- this(String.join("|", reproSequence));
- }
-
- public synchronized String getCurrentSequenceString() {
- return mCurrentSequence.toString();
- }
-
- /**
- * Starts a new test iteration. Events reported via RaceConditionTracker.onEvent before this
- * call will be ignored.
- */
- public synchronized void startIteration() {
- mLastRegisteredEvent = mRoot;
- mRegisteredEventCount = 0;
- mCurrentSequence = new StringBuilder();
- Log.d(TAG, "Repro sequence: " + mCurrentSequence);
- mSequenceToFollow = mReproString != null ?
- parseReproString(mReproString) : generateSequenceToFollowLocked();
- Log.e(TAG, "---- Start of iteration; state:\n" + dumpStateLocked());
- checkIfCompletedSequenceToFollowLocked();
-
- TraceHelperForTest.setRaceConditionReproducer(this);
- }
-
- /**
- * Ends a new test iteration. Events reported via RaceConditionTracker.onEvent after this call
- * will be ignored.
- * Returns whether we need more iterations.
- */
- public synchronized boolean finishIteration() {
- TraceHelperForTest.setRaceConditionReproducer(null);
-
- runResumeAllEventsCallbackLocked();
- assertTrue("Non-empty postponed events", mPostponedEvents.isEmpty());
- assertTrue("Last registered event is :enter", lastEventAsEnter() == null);
-
- // No events came after mLastRegisteredEvent. It doesn't make sense to come to it again
- // because we won't see new continuations.
- mLastRegisteredEvent.mStoppedAddingChildren = true;
- Log.e(TAG, "---- End of iteration; state:\n" + dumpStateLocked());
- if (mReproString != null) {
- assertTrue("Repro mode: failed to reproduce the sequence",
- mCurrentSequence.toString().startsWith(mReproString));
- }
- // If we are in a repro mode, we need only one iteration. Otherwise, continue if the tree
- // has prospective growth points.
- return mReproString == null && !mRoot.stoppedAddingChildrenToTree();
- }
-
- private static List<String> parseReproString(String reproString) {
- return Arrays.asList(reproString.split("\\|"));
- }
-
- /**
- * Called when the app issues an event.
- */
- public void onEvent(String event) {
- final Semaphore waitObject = tryRegisterEvent(event);
- if (waitObject != null) {
- waitUntilCanRegister(event, waitObject);
- }
- }
-
- /**
- * Returns whether the last event was not an XXX:enter, or this event is a matching XXX:exit.
- */
- private boolean canRegisterEventNowLocked(String event) {
- final String lastEventAsEnter = lastEventAsEnter();
- final String thisEventAsExit = eventAsExit(event);
-
- if (lastEventAsEnter != null) {
- if (!lastEventAsEnter.equals(thisEventAsExit)) {
- assertTrue("YYY:exit after XXX:enter", thisEventAsExit == null);
- // Last event was :enter, but this event is not :exit.
- return false;
- }
- } else {
- // Previous event was not :enter.
- assertTrue(":exit after a non-enter event", thisEventAsExit == null);
- }
- return true;
- }
-
- /**
- * Registers an event issued by the app and returns null or decides that the event must be
- * postponed, and returns an object to wait on.
- */
- private synchronized Semaphore tryRegisterEvent(String event) {
- Log.d(TAG, "Event issued by the app: " + event);
-
- if (!canRegisterEventNowLocked(event)) {
- return createWaitObjectForPostponedEventLocked(event);
- }
-
- if (mRegisteredEventCount < mSequenceToFollow.size()) {
- // We are in the first part of the iteration. We only register events that follow the
- // mSequenceToFollow and postponing all other events.
- if (event.equals(mSequenceToFollow.get(mRegisteredEventCount))) {
- // The event is the next one expected in the sequence. Register it.
- registerEventLocked(event);
-
- // If there are postponed events that could continue the sequence, register them.
- while (mRegisteredEventCount < mSequenceToFollow.size() &&
- mPostponedEvents.containsKey(
- mSequenceToFollow.get(mRegisteredEventCount))) {
- registerPostponedEventLocked(mSequenceToFollow.get(mRegisteredEventCount));
- }
-
- // Perhaps we just completed the required sequence...
- checkIfCompletedSequenceToFollowLocked();
- } else {
- // The event is not the next one in the sequence. Postpone it.
- return createWaitObjectForPostponedEventLocked(event);
- }
- } else if (mRegisteredEventCount == mSequenceToFollow.size()) {
- // The second phase of the iteration. We have just registered the whole
- // mSequenceToFollow, and want to add previously not seen continuations for the last
- // node in the sequence aka 'growth point'.
- if (!mLastRegisteredEvent.mNextEvents.containsKey(event) || mReproString != null) {
- // The event was never seen as a continuation for the current node.
- // Or we are in repro mode, in which case we are not in business of generating
- // new sequences after we've played back the required sequence.
- // Register it immediately.
- registerEventLocked(event);
- } else {
- // The event was seen as a continuation for the current node. Postpone it, hoping
- // that a new event will come from other threads.
- return createWaitObjectForPostponedEventLocked(event);
- }
- } else {
- // The third phase of the iteration. We are past the growth point and register
- // everything that comes.
- registerEventLocked(event);
- // Register events that may have been postponed while waiting for an :exit event
- // during the third phase. We don't do this if just registered event is :enter.
- if (eventAsEnter(event) == null && mRegisteredEventCount > mSequenceToFollow.size()) {
- registerPostponedEventsLocked(new HashSet<>(mPostponedEvents.keySet()));
- }
- }
- return null;
- }
-
- /** Called when there are chances that we just have registered the whole mSequenceToFollow. */
- private void checkIfCompletedSequenceToFollowLocked() {
- if (mRegisteredEventCount == mSequenceToFollow.size()) {
- // We just entered the second phase of the iteration. We have just registered the
- // whole mSequenceToFollow, and want to add previously not seen continuations for the
- // last node in the sequence aka 'growth point'. All seen continuations will be
- // postponed for SHORT_TIMEOUT_MS. At the end of this time period, we'll let them go.
- scheduleResumeAllEventsLocked();
-
- // Among the events that were postponed during the first stage, there may be an event
- // that wasn't seen after the current. If so, register it immediately because this
- // creates a new sequence.
- final Set<String> keys = new HashSet<>(mPostponedEvents.keySet());
- keys.removeAll(mLastRegisteredEvent.mNextEvents.keySet());
- if (!keys.isEmpty()) {
- registerPostponedEventLocked(keys.iterator().next());
- }
- }
- }
-
- private Semaphore createWaitObjectForPostponedEventLocked(String event) {
- final Semaphore waitObject = new Semaphore(0);
- assertTrue("Event already postponed: " + event, !mPostponedEvents.containsKey(event));
- mPostponedEvents.put(event, waitObject);
- return waitObject;
- }
-
- private void waitUntilCanRegister(String event, Semaphore waitObject) {
- try {
- assertTrue("Never registered event: " + event,
- waitObject.tryAcquire(LONG_TIMEOUT_MS, TimeUnit.MILLISECONDS));
- } catch (InterruptedException e) {
- fail("Wait was interrupted");
- }
- }
-
- /** Schedules resuming all postponed events after SHORT_TIMEOUT_MS */
- private void scheduleResumeAllEventsLocked() {
- assertTrue(mResumeAllEventsCallback == null);
- mResumeAllEventsCallback = this::allEventsResumeCallback;
- POSTPONED_EVENT_RESUME_HANDLER.postDelayed(mResumeAllEventsCallback, SHORT_TIMEOUT_MS);
- }
-
- private synchronized void allEventsResumeCallback() {
- assertTrue("In callback, but callback is not set", mResumeAllEventsCallback != null);
- mResumeAllEventsCallback = null;
- registerPostponedEventsLocked(new HashSet<>(mPostponedEvents.keySet()));
- }
-
- private void registerPostponedEventsLocked(Collection<String> events) {
- for (String event : events) {
- registerPostponedEventLocked(event);
- if (eventAsEnter(event) != null) {
- // Once :enter is registered, switch to waiting for :exit to come. Won't register
- // other postponed events.
- break;
- }
- }
- }
-
- private void registerPostponedEventLocked(String event) {
- mPostponedEvents.remove(event).release();
- registerEventLocked(event);
- }
-
- /**
- * If the last registered event was XXX:enter, returns XXX, otherwise, null.
- */
- private String lastEventAsEnter() {
- return eventAsEnter(mCurrentSequence.substring(mCurrentSequence.lastIndexOf("|") + 1));
- }
-
- /**
- * If the event is XXX:postfix, returns XXX, otherwise, null.
- */
- private static String prefixFromPostfixedEvent(String event, String postfix) {
- final int columnPos = event.indexOf(':');
- if (columnPos != -1 && postfix.equals(event.substring(columnPos + 1))) {
- return event.substring(0, columnPos);
- }
- return null;
- }
-
- /**
- * If the event is XXX:enter, returns XXX, otherwise, null.
- */
- private static String eventAsEnter(String event) {
- return prefixFromPostfixedEvent(event, ENTER_POSTFIX);
- }
-
- /**
- * If the event is XXX:exit, returns XXX, otherwise, null.
- */
- private static String eventAsExit(String event) {
- return prefixFromPostfixedEvent(event, EXIT_POSTFIX);
- }
-
- private void registerEventLocked(String event) {
- assertTrue(canRegisterEventNowLocked(event));
-
- Log.d(TAG, "Actually registering event: " + event);
- EventNode next = mLastRegisteredEvent.mNextEvents.get(event);
- if (next == null) {
- // This event wasn't seen after mLastRegisteredEvent.
- next = new EventNode();
- mLastRegisteredEvent.mNextEvents.put(event, next);
- // The fact that we've added a new event after the previous one means that the
- // previous event is still a growth point, unless this event is :exit, which means
- // that the previous event is :enter.
- mLastRegisteredEvent.mStoppedAddingChildren = eventAsExit(event) != null;
- }
-
- mLastRegisteredEvent = next;
- mRegisteredEventCount++;
-
- if (mCurrentSequence.length() > 0) mCurrentSequence.append("|");
- mCurrentSequence.append(event);
- Log.d(TAG, "Repro sequence: " + mCurrentSequence);
- }
-
- private void runResumeAllEventsCallbackLocked() {
- if (mResumeAllEventsCallback != null) {
- POSTPONED_EVENT_RESUME_HANDLER.removeCallbacks(mResumeAllEventsCallback);
- mResumeAllEventsCallback.run();
- }
- }
-
- private CharSequence dumpStateLocked() {
- StringBuilder sb = new StringBuilder();
-
- sb.append("Sequence to follow: ");
- for (String event : mSequenceToFollow) sb.append(" " + event);
- sb.append(".\n");
- sb.append("Registered event count: " + mRegisteredEventCount);
-
- sb.append("\nPostponed events: ");
- for (String event : mPostponedEvents.keySet()) sb.append(" " + event);
- sb.append(".");
-
- sb.append("\nNodes: \n");
- mRoot.debugDump(sb, 0, "");
- return sb;
- }
-
- public int numberOfLeafNodes() {
- return mRoot.numberOfLeafNodes();
- }
-
- private List<String> generateSequenceToFollowLocked() {
- ArrayList<String> sequence = new ArrayList<>();
- mRoot.populatePathToGrowthPoint(sequence);
- return sequence;
- }
-}
diff --git a/tests/src/com/android/launcher3/util/RaceConditionReproducerTest.java b/tests/src/com/android/launcher3/util/RaceConditionReproducerTest.java
deleted file mode 100644
index 59f2173..0000000
--- a/tests/src/com/android/launcher3/util/RaceConditionReproducerTest.java
+++ /dev/null
@@ -1,209 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.util;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@LargeTest
-@RunWith(AndroidJUnit4.class)
-public class RaceConditionReproducerTest {
- private final static String SOME_VALID_SEQUENCE_3_3 = "B1|A1|A2|B2|A3|B3";
-
- private static int factorial(int n) {
- int res = 1;
- for (int i = 2; i <= n; ++i) res *= i;
- return res;
- }
-
- RaceConditionReproducer eventProcessor;
-
- @Before
- public void setup() {
- eventProcessor = new RaceConditionReproducer();
- }
-
- @After
- public void tearDown() {
- TraceHelperForTest.cleanup();
- }
-
- private void run3_3_TestAction() throws InterruptedException {
- Thread tb = new Thread(() -> {
- eventProcessor.onEvent("B1");
- eventProcessor.onEvent("B2");
- eventProcessor.onEvent("B3");
- });
- tb.start();
-
- eventProcessor.onEvent("A1");
- eventProcessor.onEvent("A2");
- eventProcessor.onEvent("A3");
-
- tb.join();
- }
-
- @Test
- @Ignore // The test is too long for continuous testing.
- // 2 threads, 3 events each.
- public void test3_3() throws Exception {
- boolean sawTheValidSequence = false;
-
- for (; ; ) {
- eventProcessor.startIteration();
- run3_3_TestAction();
- final boolean needMoreIterations = eventProcessor.finishIteration();
-
- sawTheValidSequence = sawTheValidSequence ||
- SOME_VALID_SEQUENCE_3_3.equals(eventProcessor.getCurrentSequenceString());
-
- if (!needMoreIterations) break;
- }
-
- assertEquals("Wrong number of leaf nodes",
- factorial(3 + 3) / (factorial(3) * factorial(3)),
- eventProcessor.numberOfLeafNodes());
- assertTrue(sawTheValidSequence);
- }
-
- @Test
- @Ignore // The test is too long for continuous testing.
- // 2 threads, 3 events, including enter-exit pairs each.
- public void test3_3_enter_exit() throws Exception {
- boolean sawTheValidSequence = false;
-
- for (; ; ) {
- eventProcessor.startIteration();
- Thread tb = new Thread(() -> {
- eventProcessor.onEvent("B1:enter");
- eventProcessor.onEvent("B1:exit");
- eventProcessor.onEvent("B2");
- eventProcessor.onEvent("B3:enter");
- eventProcessor.onEvent("B3:exit");
- });
- tb.start();
-
- eventProcessor.onEvent("A1");
- eventProcessor.onEvent("A2:enter");
- eventProcessor.onEvent("A2:exit");
- eventProcessor.onEvent("A3:enter");
- eventProcessor.onEvent("A3:exit");
-
- tb.join();
- final boolean needMoreIterations = eventProcessor.finishIteration();
-
- sawTheValidSequence = sawTheValidSequence ||
- "B1:enter|B1:exit|A1|A2:enter|A2:exit|B2|A3:enter|A3:exit|B3:enter|B3:exit".
- equals(eventProcessor.getCurrentSequenceString());
-
- if (!needMoreIterations) break;
- }
-
- assertEquals("Wrong number of leaf nodes",
- factorial(3 + 3) / (factorial(3) * factorial(3)),
- eventProcessor.numberOfLeafNodes());
- assertTrue(sawTheValidSequence);
- }
-
- @Test
- // 2 threads, 3 events each; reproducing a particular event sequence.
- public void test3_3_ReproMode() throws Exception {
- eventProcessor = new RaceConditionReproducer(SOME_VALID_SEQUENCE_3_3);
- eventProcessor.startIteration();
- run3_3_TestAction();
- assertTrue(!eventProcessor.finishIteration());
- assertEquals(SOME_VALID_SEQUENCE_3_3, eventProcessor.getCurrentSequenceString());
-
- assertEquals("Wrong number of leaf nodes", 1, eventProcessor.numberOfLeafNodes());
- }
-
- @Test
- @Ignore // The test is too long for continuous testing.
- // 2 threads with 2 events; 1 thread with 1 event.
- public void test2_1_2() throws Exception {
- for (; ; ) {
- eventProcessor.startIteration();
- Thread tb = new Thread(() -> {
- eventProcessor.onEvent("B1");
- eventProcessor.onEvent("B2");
- });
- tb.start();
-
- Thread tc = new Thread(() -> {
- eventProcessor.onEvent("C1");
- });
- tc.start();
-
- eventProcessor.onEvent("A1");
- eventProcessor.onEvent("A2");
-
- tb.join();
- tc.join();
-
- if (!eventProcessor.finishIteration()) break;
- }
-
- assertEquals("Wrong number of leaf nodes",
- factorial(2 + 2 + 1) / (factorial(2) * factorial(2) * factorial(1)),
- eventProcessor.numberOfLeafNodes());
- }
-
- @Test
- @Ignore // The test is too long for continuous testing.
- // 2 threads with 2 events; 1 thread with 1 event. Includes enter-exit pairs.
- public void test2_1_2_enter_exit() throws Exception {
- for (; ; ) {
- eventProcessor.startIteration();
- Thread tb = new Thread(() -> {
- eventProcessor.onEvent("B1:enter");
- eventProcessor.onEvent("B1:exit");
- eventProcessor.onEvent("B2:enter");
- eventProcessor.onEvent("B2:exit");
- });
- tb.start();
-
- Thread tc = new Thread(() -> {
- eventProcessor.onEvent("C1:enter");
- eventProcessor.onEvent("C1:exit");
- });
- tc.start();
-
- eventProcessor.onEvent("A1:enter");
- eventProcessor.onEvent("A1:exit");
- eventProcessor.onEvent("A2:enter");
- eventProcessor.onEvent("A2:exit");
-
- tb.join();
- tc.join();
-
- if (!eventProcessor.finishIteration()) break;
- }
-
- assertEquals("Wrong number of leaf nodes",
- factorial(2 + 2 + 1) / (factorial(2) * factorial(2) * factorial(1)),
- eventProcessor.numberOfLeafNodes());
- }
-}
diff --git a/tests/src/com/android/launcher3/util/TestResourceHelper.kt b/tests/src/com/android/launcher3/util/TestResourceHelper.kt
index fb03fe1..cf80ece 100644
--- a/tests/src/com/android/launcher3/util/TestResourceHelper.kt
+++ b/tests/src/com/android/launcher3/util/TestResourceHelper.kt
@@ -23,12 +23,18 @@
import com.android.launcher3.tests.R as TestR
import kotlin.IntArray
-class TestResourceHelper(private val context: Context, private val specsFileId: Int) :
+class TestResourceHelper(private val context: Context, specsFileId: Int) :
ResourceHelper(context, specsFileId) {
override fun obtainStyledAttributes(attrs: AttributeSet, styleId: IntArray): TypedArray {
- var clone = styleId.clone()
- if (styleId == R.styleable.SpecSize) clone = TestR.styleable.SpecSize
- else if (styleId == R.styleable.WorkspaceSpec) clone = TestR.styleable.WorkspaceSpec
+ val clone =
+ when {
+ styleId.contentEquals(R.styleable.SizeSpec) -> TestR.styleable.SizeSpec
+ styleId.contentEquals(R.styleable.WorkspaceSpec) -> TestR.styleable.WorkspaceSpec
+ styleId.contentEquals(R.styleable.FolderSpec) -> TestR.styleable.FolderSpec
+ styleId.contentEquals(R.styleable.AllAppsSpec) -> TestR.styleable.AllAppsSpec
+ else -> styleId.clone()
+ }
+
return context.obtainStyledAttributes(attrs, clone)
}
}
diff --git a/tests/src/com/android/launcher3/util/TestUtil.java b/tests/src/com/android/launcher3/util/TestUtil.java
index f8cd995..4cd6c53 100644
--- a/tests/src/com/android/launcher3/util/TestUtil.java
+++ b/tests/src/com/android/launcher3/util/TestUtil.java
@@ -18,14 +18,13 @@
import static android.util.Base64.NO_PADDING;
import static android.util.Base64.NO_WRAP;
-import static androidx.test.InstrumentationRegistry.getContext;
-import static androidx.test.InstrumentationRegistry.getInstrumentation;
-import static androidx.test.InstrumentationRegistry.getTargetContext;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_KEY;
import static com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_LABEL;
import static com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_TAG;
+import android.app.Instrumentation;
import android.app.blob.BlobHandle;
import android.app.blob.BlobStoreManager;
import android.content.Context;
@@ -35,9 +34,12 @@
import android.os.Handler;
import android.os.Looper;
import android.os.ParcelFileDescriptor.AutoCloseOutputStream;
+import android.os.Process;
import android.os.UserHandle;
import android.provider.Settings;
+import android.system.OsConstants;
import android.util.Base64;
+import android.util.Log;
import androidx.test.uiautomator.UiDevice;
@@ -52,26 +54,35 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.security.MessageDigest;
+import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
import java.util.function.Predicate;
import java.util.function.ToIntFunction;
public class TestUtil {
+ private static final String TAG = "TestUtil";
+
public static final String DUMMY_PACKAGE = "com.example.android.aardwolf";
- public static final int DEFAULT_USER_ID = 0;
+ public static final long DEFAULT_UI_TIMEOUT = 10000;
public static void installDummyApp() throws IOException {
- installDummyAppForUser(DEFAULT_USER_ID);
+ final int defaultUserId = getMainUserId();
+ installDummyAppForUser(defaultUserId);
}
public static void installDummyAppForUser(int userId) throws IOException {
+ Instrumentation instrumentation = getInstrumentation();
// Copy apk from resources to a local file and install from there.
- final Resources resources = getContext().getResources();
+ final Resources resources = instrumentation.getContext().getResources();
final InputStream in = resources.openRawResource(
resources.getIdentifier("aardwolf_dummy_app",
- "raw", getContext().getPackageName()));
- final String apkFilename = getInstrumentation().getTargetContext().
- getFilesDir().getPath() + "/dummy_app.apk";
+ "raw", instrumentation.getContext().getPackageName()));
+ final String apkFilename = instrumentation.getTargetContext()
+ .getFilesDir().getPath() + "/dummy_app.apk";
try (PackageInstallCheck pic = new PackageInstallCheck()) {
final FileOutputStream out = new FileOutputStream(apkFilename);
@@ -84,7 +95,7 @@
in.close();
out.close();
- final String result = UiDevice.getInstance(getInstrumentation())
+ final String result = UiDevice.getInstance(instrumentation)
.executeShellCommand("pm install --user " + userId + " " + apkFilename);
Assert.assertTrue(
"Failed to install wellbeing test apk; make sure the device is rooted",
@@ -96,6 +107,23 @@
}
/**
+ * Returns the main user ID. NOTE: For headless system it is NOT 0. Returns 0 by default, if
+ * there is no main user.
+ *
+ * @return a main user ID
+ */
+ public static int getMainUserId() throws IOException {
+ Instrumentation instrumentation = getInstrumentation();
+ final String result = UiDevice.getInstance(instrumentation)
+ .executeShellCommand("cmd user get-main-user");
+ try {
+ return Integer.parseInt(result.trim());
+ } catch (NumberFormatException e) {
+ return 0;
+ }
+ }
+
+ /**
* Utility class to override a boolean flag during test. Note that the returned SafeCloseable
* must be closed to restore the original state
*/
@@ -159,6 +187,50 @@
Settings.Secure.putString(context.getContentResolver(), LAYOUT_DIGEST_KEY, null);
}
+ /**
+ * Utility method to run a task synchronously which converts any exceptions to RuntimeException
+ */
+ public static void runOnExecutorSync(ExecutorService executor, UncheckedRunnable task) {
+ try {
+ executor.submit(() -> {
+ try {
+ task.run();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }).get();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Runs the callback on the UI thread and returns the result.
+ */
+ public static <T> T getOnUiThread(final Callable<T> callback) {
+ try {
+ FutureTask<T> task = new FutureTask<>(callback);
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ task.run();
+ } else {
+ new Handler(Looper.getMainLooper()).post(task);
+ }
+ return task.get(DEFAULT_UI_TIMEOUT, TimeUnit.MILLISECONDS);
+ } catch (TimeoutException e) {
+ Log.e(TAG, "Timeout in getOnUiThread, sending SIGABRT", e);
+ Process.sendSignal(Process.myPid(), OsConstants.SIGABRT);
+ throw new RuntimeException(e);
+ } catch (Throwable e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /** Interface to indicate a runnable which can throw any exception. */
+ public interface UncheckedRunnable {
+ /** Method to run the task */
+ void run() throws Exception;
+ }
+
private static class PackageInstallCheck extends LauncherApps.Callback
implements AutoCloseable {
@@ -166,7 +238,8 @@
final LauncherApps mLauncherApps;
PackageInstallCheck() {
- mLauncherApps = getTargetContext().getSystemService(LauncherApps.class);
+ mLauncherApps = getInstrumentation().getTargetContext()
+ .getSystemService(LauncherApps.class);
mLauncherApps.registerCallback(this, new Handler(Looper.getMainLooper()));
}
diff --git a/tests/src/com/android/launcher3/util/TraceHelperForTest.java b/tests/src/com/android/launcher3/util/TraceHelperForTest.java
deleted file mode 100644
index f1c8a67..0000000
--- a/tests/src/com/android/launcher3/util/TraceHelperForTest.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/**
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.util;
-
-import java.util.LinkedList;
-import java.util.function.IntConsumer;
-
-public class TraceHelperForTest extends TraceHelper {
-
- private static final TraceHelperForTest INSTANCE_FOR_TEST = new TraceHelperForTest();
-
- private final ThreadLocal<LinkedList<TraceInfo>> mStack =
- ThreadLocal.withInitial(LinkedList::new);
-
- private RaceConditionReproducer mRaceConditionReproducer;
- private IntConsumer mFlagsChangeListener;
-
- public static void setRaceConditionReproducer(RaceConditionReproducer reproducer) {
- TraceHelper.INSTANCE = INSTANCE_FOR_TEST;
- INSTANCE_FOR_TEST.mRaceConditionReproducer = reproducer;
- }
-
- public static void cleanup() {
- INSTANCE_FOR_TEST.mRaceConditionReproducer = null;
- INSTANCE_FOR_TEST.mFlagsChangeListener = null;
- }
-
- public static void setFlagsChangeListener(IntConsumer listener) {
- TraceHelper.INSTANCE = INSTANCE_FOR_TEST;
- INSTANCE_FOR_TEST.mFlagsChangeListener = listener;
- }
-
- private TraceHelperForTest() { }
-
- @Override
- public Object beginSection(String sectionName, int flags) {
- LinkedList<TraceInfo> stack = mStack.get();
- TraceInfo info = new TraceInfo(sectionName, flags);
- stack.add(info);
-
- if ((flags & TraceHelper.FLAG_CHECK_FOR_RACE_CONDITIONS) != 0
- && mRaceConditionReproducer != null) {
- mRaceConditionReproducer.onEvent(RaceConditionReproducer.enterEvt(sectionName));
- }
- updateBinderTracking(stack);
-
- super.beginSection(sectionName, flags);
- return info;
- }
-
- @Override
- public void endSection(Object token) {
- LinkedList<TraceInfo> stack = mStack.get();
- if (stack.size() == 0) {
- new Throwable().printStackTrace();
- }
- TraceInfo info = (TraceInfo) token;
- stack.remove(info);
- if ((info.flags & TraceHelper.FLAG_CHECK_FOR_RACE_CONDITIONS) != 0
- && mRaceConditionReproducer != null) {
- mRaceConditionReproducer.onEvent(RaceConditionReproducer.exitEvt(info.sectionName));
- }
- updateBinderTracking(stack);
-
- super.endSection(token);
- }
-
- @Override
- public Object beginFlagsOverride(int flags) {
- LinkedList<TraceInfo> stack = mStack.get();
- TraceInfo info = new TraceInfo(null, flags);
- stack.add(info);
- updateBinderTracking(stack);
- super.beginFlagsOverride(flags);
- return info;
- }
-
- @Override
- public void endFlagsOverride(Object token) {
- super.endFlagsOverride(token);
- LinkedList<TraceInfo> stack = mStack.get();
- TraceInfo info = (TraceInfo) token;
- stack.remove(info);
- updateBinderTracking(stack);
- }
-
- private void updateBinderTracking(LinkedList<TraceInfo> stack) {
- if (mFlagsChangeListener != null) {
- mFlagsChangeListener.accept(stack.stream()
- .mapToInt(info -> info.flags).reduce(0, (a, b) -> a | b));
- }
- }
-
- private static class TraceInfo {
- public final String sectionName;
- public final int flags;
-
- TraceInfo(String sectionName, int flags) {
- this.sectionName = sectionName;
- this.flags = flags;
- }
- }
-}
diff --git a/tests/src/com/android/launcher3/util/WidgetUtils.java b/tests/src/com/android/launcher3/util/WidgetUtils.java
index b0df055..027a31a 100644
--- a/tests/src/com/android/launcher3/util/WidgetUtils.java
+++ b/tests/src/com/android/launcher3/util/WidgetUtils.java
@@ -18,18 +18,14 @@
import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-import static com.android.launcher3.WorkspaceLayoutManager.FIRST_SCREEN_ID;
-
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
-import android.content.ContentResolver;
import android.content.Context;
import android.os.Bundle;
import android.os.Process;
import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.android.launcher3.widget.LauncherWidgetHolder;
@@ -88,33 +84,6 @@
}
/**
- * Adds {@param item} on the homescreen on the 0th screen
- */
- public static void addItemToScreen(ItemInfo item, Context targetContext) {
- ContentResolver resolver = targetContext.getContentResolver();
- int screenId = FIRST_SCREEN_ID;
- // Update the screen id counter for the provider.
- LauncherSettings.Settings.call(resolver,
- LauncherSettings.Settings.METHOD_NEW_SCREEN_ID);
-
- if (screenId > FIRST_SCREEN_ID) {
- screenId = FIRST_SCREEN_ID;
- }
-
- // Insert the item
- ContentWriter writer = new ContentWriter(targetContext);
- item.id = LauncherSettings.Settings.call(
- resolver, LauncherSettings.Settings.METHOD_NEW_ITEM_ID)
- .getInt(LauncherSettings.Settings.EXTRA_VALUE);
- item.screenId = screenId;
- item.onAddToDatabase(writer);
- writer.put(LauncherSettings.Favorites._ID, item.id);
- resolver.insert(LauncherSettings.Favorites.CONTENT_URI,
- writer.getValues(targetContext));
- }
-
-
- /**
* Creates a {@link AppWidgetProviderInfo} for the provided component name
*/
public static AppWidgetProviderInfo createAppWidgetProviderInfo(ComponentName cn) {
diff --git a/tests/src/com/android/launcher3/util/rule/FailureWatcher.java b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
index 7ca6a06..f2ae9d3 100644
--- a/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
+++ b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
@@ -8,10 +8,9 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.test.core.app.ApplicationProvider;
import androidx.test.uiautomator.UiDevice;
-import com.android.app.viewcapture.ViewCapture;
+import com.android.app.viewcapture.data.ExportedData;
import com.android.launcher3.tapl.LauncherInstrumentation;
import com.android.launcher3.ui.AbstractLauncherUiTest;
@@ -24,22 +23,21 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
+import java.util.function.Supplier;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class FailureWatcher extends TestWatcher {
private static final String TAG = "FailureWatcher";
private static boolean sSavedBugreport = false;
- final private UiDevice mDevice;
private final LauncherInstrumentation mLauncher;
@NonNull
- private final ViewCapture mViewCapture;
+ private final Supplier<ExportedData> mViewCaptureDataSupplier;
- public FailureWatcher(UiDevice device, LauncherInstrumentation launcher,
- @NonNull ViewCapture viewCapture) {
- mDevice = device;
+ public FailureWatcher(LauncherInstrumentation launcher,
+ @NonNull Supplier<ExportedData> viewCaptureDataSupplier) {
mLauncher = launcher;
- mViewCapture = viewCapture;
+ mViewCaptureDataSupplier = viewCaptureDataSupplier;
}
@Override
@@ -71,7 +69,7 @@
@Override
protected void failed(Throwable e, Description description) {
- onError(mLauncher, description, e, mViewCapture);
+ onError(mLauncher, description, e, mViewCaptureDataSupplier);
}
static File diagFile(Description description, String prefix, String ext) {
@@ -86,7 +84,7 @@
}
private static void onError(LauncherInstrumentation launcher, Description description,
- Throwable e, @Nullable ViewCapture viewCapture) {
+ Throwable e, @Nullable Supplier<ExportedData> viewCaptureDataSupplier) {
final File sceenshot = diagFile(description, "TestScreenshot", "png");
final File hierarchy = diagFile(description, "Hierarchy", "zip");
@@ -103,9 +101,9 @@
dumpCommand("cmd window dump-visible-window-views", out);
out.closeEntry();
- if (viewCapture != null) {
+ if (viewCaptureDataSupplier != null) {
out.putNextEntry(new ZipEntry("FS/data/misc/wmtrace/failed_test.vc"));
- viewCapture.dumpTo(out, ApplicationProvider.getApplicationContext());
+ viewCaptureDataSupplier.get().writeTo(out);
out.closeEntry();
}
} catch (Exception ignored) {
diff --git a/tests/src/com/android/launcher3/util/rule/ViewCaptureAnalysisRule.java b/tests/src/com/android/launcher3/util/rule/ViewCaptureAnalysisRule.java
new file mode 100644
index 0000000..702757f
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/rule/ViewCaptureAnalysisRule.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.util.rule;
+
+import androidx.annotation.NonNull;
+import androidx.test.InstrumentationRegistry;
+
+import com.android.app.viewcapture.ViewCapture;
+import com.android.app.viewcapture.data.ExportedData;
+
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
+
+import java.util.concurrent.ExecutionException;
+
+/**
+ * After the test succeeds, the rule looks for anomalies in the data accumulated by ViewCapture
+ * that's passed as a parameter. If anomalies are detected, throws an exception and fails the test.
+ */
+public class ViewCaptureAnalysisRule extends TestWatcher {
+ @NonNull
+ private final ViewCapture mViewCapture;
+
+ public ViewCaptureAnalysisRule(@NonNull ViewCapture viewCapture) {
+ mViewCapture = viewCapture;
+ }
+
+ @Override
+ protected void succeeded(Description description) {
+ super.succeeded(description);
+ try {
+ analyzeViewCaptureData(mViewCapture.getExportedData(
+ InstrumentationRegistry.getTargetContext()));
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ } catch (ExecutionException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static void analyzeViewCaptureData(ExportedData viewCaptureData) {
+ }
+}
diff --git a/tests/src/com/android/launcher3/util/rule/ViewCaptureRule.kt b/tests/src/com/android/launcher3/util/rule/ViewCaptureRule.kt
index 0c65539..6c06502 100644
--- a/tests/src/com/android/launcher3/util/rule/ViewCaptureRule.kt
+++ b/tests/src/com/android/launcher3/util/rule/ViewCaptureRule.kt
@@ -22,7 +22,9 @@
import androidx.test.core.app.ApplicationProvider
import com.android.app.viewcapture.SimpleViewCapture
import com.android.app.viewcapture.ViewCapture.MAIN_EXECUTOR
+import com.android.app.viewcapture.data.ExportedData
import com.android.launcher3.util.ActivityLifecycleCallbacksAdapter
+import java.util.function.Supplier
import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement
@@ -33,24 +35,27 @@
*
* This rule will not work in OOP tests that don't have access to the activity under test.
*/
-class ViewCaptureRule : TestRule {
+class ViewCaptureRule(var alreadyOpenActivitySupplier: Supplier<Activity?>) : TestRule {
val viewCapture = SimpleViewCapture("test-view-capture")
+ var viewCaptureData: ExportedData? = null
+ private set
override fun apply(base: Statement, description: Description): Statement {
return object : Statement() {
override fun evaluate() {
+ viewCaptureData = null
val windowListenerCloseables = mutableListOf<SafeCloseable>()
+ val alreadyOpenActivity = alreadyOpenActivitySupplier.get()
+ if (alreadyOpenActivity != null) {
+ startCapture(windowListenerCloseables, alreadyOpenActivity)
+ }
+
val lifecycleCallbacks =
object : ActivityLifecycleCallbacksAdapter {
override fun onActivityCreated(activity: Activity, bundle: Bundle?) {
super.onActivityCreated(activity, bundle)
- windowListenerCloseables.add(
- viewCapture.startCapture(
- activity.window.decorView,
- "${description.testClass?.simpleName}.${description.methodName}"
- )
- )
+ startCapture(windowListenerCloseables, activity)
}
override fun onActivityDestroyed(activity: Activity) {
@@ -67,6 +72,9 @@
} finally {
application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks)
+ viewCaptureData =
+ viewCapture.getExportedData(ApplicationProvider.getApplicationContext())
+
// Clean up ViewCapture references here rather than in onActivityDestroyed so
// test code can access view hierarchy capture. onActivityDestroyed would delete
// view capture data before FailureWatcher could output it as a test artifact.
@@ -75,6 +83,18 @@
MAIN_EXECUTOR.execute { windowListenerCloseables.onEach(SafeCloseable::close) }
}
}
+
+ private fun startCapture(
+ windowListenerCloseables: MutableCollection<SafeCloseable>,
+ activity: Activity
+ ) {
+ windowListenerCloseables.add(
+ viewCapture.startCapture(
+ activity.window.decorView,
+ "${description.testClass?.simpleName}.${description.methodName}"
+ )
+ )
+ }
}
}
}
diff --git a/tests/src/com/android/launcher3/workspace/WorkspaceSpecsTest.kt b/tests/src/com/android/launcher3/workspace/WorkspaceSpecsTest.kt
index 9cd0a2e..8b99a3a 100644
--- a/tests/src/com/android/launcher3/workspace/WorkspaceSpecsTest.kt
+++ b/tests/src/com/android/launcher3/workspace/WorkspaceSpecsTest.kt
@@ -50,16 +50,24 @@
"specType=HEIGHT, " +
"startPadding=SizeSpec(fixedSize=0.0, " +
"ofAvailableSpace=0.0, " +
- "ofRemainderSpace=0.0), " +
+ "ofRemainderSpace=0.0, " +
+ "matchWorkspace=false, " +
+ "maxSize=2147483647), " +
"endPadding=SizeSpec(fixedSize=84.0, " +
"ofAvailableSpace=0.0, " +
- "ofRemainderSpace=0.0), " +
+ "ofRemainderSpace=0.0, " +
+ "matchWorkspace=false, " +
+ "maxSize=2147483647), " +
"gutter=SizeSpec(fixedSize=42.0, " +
"ofAvailableSpace=0.0, " +
- "ofRemainderSpace=0.0), " +
+ "ofRemainderSpace=0.0, " +
+ "matchWorkspace=false, " +
+ "maxSize=2147483647), " +
"cellSize=SizeSpec(fixedSize=0.0, " +
"ofAvailableSpace=0.15808, " +
- "ofRemainderSpace=0.0)" +
+ "ofRemainderSpace=0.0, " +
+ "matchWorkspace=false, " +
+ "maxSize=2147483647)" +
")"
)
assertThat(workspaceSpecs.workspaceHeightSpecList[1].toString())
@@ -69,16 +77,24 @@
"specType=HEIGHT, " +
"startPadding=SizeSpec(fixedSize=0.0, " +
"ofAvailableSpace=0.0, " +
- "ofRemainderSpace=0.0), " +
+ "ofRemainderSpace=0.0, " +
+ "matchWorkspace=false, " +
+ "maxSize=2147483647), " +
"endPadding=SizeSpec(fixedSize=0.0, " +
"ofAvailableSpace=0.0, " +
- "ofRemainderSpace=1.0), " +
+ "ofRemainderSpace=1.0, " +
+ "matchWorkspace=false, " +
+ "maxSize=2147483647), " +
"gutter=SizeSpec(fixedSize=42.0, " +
"ofAvailableSpace=0.0, " +
- "ofRemainderSpace=0.0), " +
+ "ofRemainderSpace=0.0, " +
+ "matchWorkspace=false, " +
+ "maxSize=2147483647), " +
"cellSize=SizeSpec(fixedSize=273.0, " +
"ofAvailableSpace=0.0, " +
- "ofRemainderSpace=0.0)" +
+ "ofRemainderSpace=0.0, " +
+ "matchWorkspace=false, " +
+ "maxSize=2147483647)" +
")"
)
assertThat(workspaceSpecs.workspaceHeightSpecList[2].toString())
@@ -88,16 +104,24 @@
"specType=HEIGHT, " +
"startPadding=SizeSpec(fixedSize=21.0, " +
"ofAvailableSpace=0.0, " +
- "ofRemainderSpace=0.0), " +
+ "ofRemainderSpace=0.0, " +
+ "matchWorkspace=false, " +
+ "maxSize=2147483647), " +
"endPadding=SizeSpec(fixedSize=0.0, " +
"ofAvailableSpace=0.0, " +
- "ofRemainderSpace=1.0), " +
+ "ofRemainderSpace=1.0, " +
+ "matchWorkspace=false, " +
+ "maxSize=2147483647), " +
"gutter=SizeSpec(fixedSize=42.0, " +
"ofAvailableSpace=0.0, " +
- "ofRemainderSpace=0.0), " +
+ "ofRemainderSpace=0.0, " +
+ "matchWorkspace=false, " +
+ "maxSize=2147483647), " +
"cellSize=SizeSpec(fixedSize=273.0, " +
"ofAvailableSpace=0.0, " +
- "ofRemainderSpace=0.0)" +
+ "ofRemainderSpace=0.0, " +
+ "matchWorkspace=false, " +
+ "maxSize=2147483647)" +
")"
)
assertThat(workspaceSpecs.workspaceWidthSpecList.size).isEqualTo(1)
@@ -108,16 +132,24 @@
"specType=WIDTH, " +
"startPadding=SizeSpec(fixedSize=58.0, " +
"ofAvailableSpace=0.0, " +
- "ofRemainderSpace=0.0), " +
+ "ofRemainderSpace=0.0, " +
+ "matchWorkspace=false, " +
+ "maxSize=2147483647), " +
"endPadding=SizeSpec(fixedSize=58.0, " +
"ofAvailableSpace=0.0, " +
- "ofRemainderSpace=0.0), " +
+ "ofRemainderSpace=0.0, " +
+ "matchWorkspace=false, " +
+ "maxSize=2147483647), " +
"gutter=SizeSpec(fixedSize=42.0, " +
"ofAvailableSpace=0.0, " +
- "ofRemainderSpace=0.0), " +
+ "ofRemainderSpace=0.0, " +
+ "matchWorkspace=false, " +
+ "maxSize=2147483647), " +
"cellSize=SizeSpec(fixedSize=0.0, " +
"ofAvailableSpace=0.0, " +
- "ofRemainderSpace=0.25)" +
+ "ofRemainderSpace=0.25, " +
+ "matchWorkspace=false, " +
+ "maxSize=2147483647)" +
")"
)
}
@@ -136,4 +168,9 @@
fun parseInvalidFile_valueBiggerThan1_throwsError() {
WorkspaceSpecs(TestResourceHelper(context!!, TestR.xml.invalid_workspace_file_case_3))
}
+
+ @Test(expected = IllegalStateException::class)
+ fun parseInvalidFile_matchWorkspace_true_throwsError() {
+ WorkspaceSpecs(TestResourceHelper(context!!, TestR.xml.invalid_workspace_file_case_4))
+ }
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Background.java b/tests/tapl/com/android/launcher3/tapl/Background.java
index 5a96d95..31e9aa2 100644
--- a/tests/tapl/com/android/launcher3/tapl/Background.java
+++ b/tests/tapl/com/android/launcher3/tapl/Background.java
@@ -18,6 +18,8 @@
import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
+import static com.android.launcher3.tapl.LauncherInstrumentation.EVENT_TOUCH_DOWN_TIS;
+import static com.android.launcher3.tapl.LauncherInstrumentation.EVENT_TOUCH_UP_TIS;
import static com.android.launcher3.tapl.OverviewTask.TASK_START_EVENT;
import static com.android.launcher3.testing.shared.TestProtocol.OVERVIEW_STATE_ORDINAL;
@@ -138,6 +140,10 @@
mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN,
LauncherInstrumentation.EVENT_TOUCH_UP);
}
+ if (mLauncher.isTrackpadGestureEnabled()) {
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_DOWN_TIS);
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_UP_TIS);
+ }
mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SQUARE_BUTTON_EVENT);
mLauncher.runToState(
() -> mLauncher.waitForNavigationUiObject("recent_apps").click(),
@@ -280,6 +286,10 @@
mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN,
LauncherInstrumentation.EVENT_TOUCH_UP);
}
+ if (mLauncher.isTrackpadGestureEnabled()) {
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_DOWN_TIS);
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_UP_TIS);
+ }
mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SQUARE_BUTTON_EVENT);
mLauncher.runToState(() -> recentsButton.click(), OVERVIEW_STATE_ORDINAL,
"clicking Recents button for the first time");
@@ -290,6 +300,10 @@
mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN,
LauncherInstrumentation.EVENT_TOUCH_UP);
}
+ if (mLauncher.isTrackpadGestureEnabled()) {
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_DOWN_TIS);
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_UP_TIS);
+ }
mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SQUARE_BUTTON_EVENT);
mLauncher.executeAndWaitForEvent(
() -> recentsButton.click(),
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index c4f5da5..2286d7e 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -138,17 +138,6 @@
OUTSIDE_WITH_KEYCODE,
}
- /**
- * Represents a point in the code at which a callback can run.
- */
- public enum CALLBACK_RUN_POINT {
- CALLBACK_HOLD_BEFORE_DROP,
- CALLBACK_HOVER_ENTER,
- CALLBACK_HOVER_EXIT,
- }
-
- private Consumer<CALLBACK_RUN_POINT> mCallbackAtRunPoint = null;
-
// Base class for launcher containers.
abstract static class VisibleContainer {
protected final LauncherInstrumentation mLauncher;
@@ -1035,6 +1024,10 @@
expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_TOUCH_DOWN);
expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_TOUCH_UP);
}
+ if (isTrackpadGestureEnabled()) {
+ expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_DOWN_TIS);
+ expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_UP_TIS);
+ }
runToState(
waitForNavigationUiObject("home")::click,
@@ -1074,6 +1067,10 @@
expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_TOUCH_DOWN);
expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_TOUCH_UP);
}
+ if (isTrackpadGestureEnabled()) {
+ expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_DOWN_TIS);
+ expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_UP_TIS);
+ }
}
if (launcherVisible) {
if (getContext().getApplicationInfo().isOnBackInvokedCallbackEnabled()) {
@@ -1646,9 +1643,14 @@
}
private boolean hasTIS() {
- return getTestInfo(TestProtocol.REQUEST_HAS_TIS).getBoolean(TestProtocol.REQUEST_HAS_TIS);
+ return getTestInfo(TestProtocol.REQUEST_HAS_TIS).getBoolean(
+ TestProtocol.TEST_INFO_RESPONSE_FIELD);
}
+ boolean isTrackpadGestureEnabled() {
+ return getTestInfo(TestProtocol.REQUEST_IS_TRACKPAD_GESTURE_ENABLED).getBoolean(
+ TestProtocol.TEST_INFO_RESPONSE_FIELD);
+ }
public void sendPointer(long downTime, long currentTime, int action, Point point,
GestureScope gestureScope) {
@@ -1660,7 +1662,8 @@
&& gestureScope != GestureScope.OUTSIDE_WITH_KEYCODE) {
expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_TOUCH_DOWN);
}
- if (hasTIS && getNavigationModel() != NavigationModel.THREE_BUTTON) {
+ if (hasTIS && (isTrackpadGestureEnabled()
+ || getNavigationModel() != NavigationModel.THREE_BUTTON)) {
expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_DOWN_TIS);
}
break;
@@ -1679,7 +1682,8 @@
|| gestureScope == GestureScope.OUTSIDE_WITHOUT_PILFER
? EVENT_TOUCH_UP : EVENT_TOUCH_CANCEL);
}
- if (hasTIS && getNavigationModel() != NavigationModel.THREE_BUTTON) {
+ if (hasTIS && (isTrackpadGestureEnabled()
+ || getNavigationModel() != NavigationModel.THREE_BUTTON)) {
expectEvent(TestProtocol.SEQUENCE_TIS,
gestureScope == GestureScope.INSIDE_TO_OUTSIDE_WITH_KEYCODE
|| gestureScope == GestureScope.OUTSIDE_WITH_KEYCODE
@@ -2049,22 +2053,6 @@
}
/**
- * Sets the consumer to run callbacks at all run-points.
- */
- public void setRunPointCallback(Consumer<CALLBACK_RUN_POINT> callback) {
- mCallbackAtRunPoint = callback;
- }
-
- /**
- * Runs the callback at the specified point if it exists.
- */
- void runCallbackIfActive(CALLBACK_RUN_POINT runPoint) {
- if (mCallbackAtRunPoint != null) {
- mCallbackAtRunPoint.accept(runPoint);
- }
- }
-
- /**
* Waits until a particular condition is true. Based on WaitMixin.
*/
boolean waitAndGet(BooleanSupplier condition, long timeout, long interval) {
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewActions.java b/tests/tapl/com/android/launcher3/tapl/OverviewActions.java
index 386deac..2f7596e 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewActions.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewActions.java
@@ -51,7 +51,7 @@
"clicked screenshot button")) {
UiObject2 closeScreenshot = mLauncher.waitForSystemUiObject(
"screenshot_dismiss_image");
- if (mLauncher.getNavigationModel()
+ if (mLauncher.isTrackpadGestureEnabled() || mLauncher.getNavigationModel()
!= LauncherInstrumentation.NavigationModel.THREE_BUTTON) {
mLauncher.expectEvent(TestProtocol.SEQUENCE_TIS,
LauncherInstrumentation.EVENT_TOUCH_DOWN_TIS);
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenuItem.java b/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenuItem.java
index b2cc92d..e3035bf 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenuItem.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenuItem.java
@@ -15,13 +15,7 @@
*/
package com.android.launcher3.tapl;
-import static com.android.launcher3.tapl.LauncherInstrumentation.CALLBACK_RUN_POINT.CALLBACK_HOVER_ENTER;
-import static com.android.launcher3.tapl.LauncherInstrumentation.CALLBACK_RUN_POINT.CALLBACK_HOVER_EXIT;
-
-import android.graphics.Point;
import android.graphics.Rect;
-import android.os.SystemClock;
-import android.view.MotionEvent;
import androidx.test.uiautomator.UiObject2;
@@ -42,28 +36,4 @@
public Rect getVisibleBounds() {
return mMenuItem.getVisibleBounds();
}
-
- /**
- * Emulate the cursor entering and exiting a hover over this menu item.
- */
- public void hoverCursor() {
- try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
- LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
- "cursor hover entering menu item")) {
- long downTime = SystemClock.uptimeMillis();
- mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_HOVER_ENTER,
- new Point(mMenuItem.getVisibleCenter().x, mMenuItem.getVisibleCenter().y),
- null);
- mLauncher.runCallbackIfActive(CALLBACK_HOVER_ENTER);
-
- try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
- "cursor hover exiting menu item")) {
- downTime = SystemClock.uptimeMillis();
- mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_HOVER_EXIT,
- new Point(mMenuItem.getVisibleCenter().x, mMenuItem.getVisibleCenter().y),
- null);
- mLauncher.runCallbackIfActive(CALLBACK_HOVER_EXIT);
- }
- }
- }
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index 7bb02cb..8604988 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -18,7 +18,6 @@
import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_SCROLLED;
-import static com.android.launcher3.tapl.LauncherInstrumentation.CALLBACK_RUN_POINT.CALLBACK_HOLD_BEFORE_DROP;
import static com.android.launcher3.testing.shared.TestProtocol.ALL_APPS_STATE_ORDINAL;
import static com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL;
@@ -553,7 +552,6 @@
launcher.movePointer(dragStart, targetDest, DEFAULT_DRAG_STEPS, isDecelerating,
downTime, SystemClock.uptimeMillis(), false,
LauncherInstrumentation.GestureScope.INSIDE);
- launcher.runCallbackIfActive(CALLBACK_HOLD_BEFORE_DROP);
dropDraggedIcon(launcher, targetDest, downTime, expectDropEvents);
}
}
@@ -587,7 +585,6 @@
launcher.movePointer(dragStart, targetDest, DEFAULT_DRAG_STEPS, isDecelerating,
downTime, SystemClock.uptimeMillis(), false,
LauncherInstrumentation.GestureScope.INSIDE);
- launcher.runCallbackIfActive(CALLBACK_HOLD_BEFORE_DROP);
dropDraggedIcon(launcher, targetDest, downTime, expectDropEvents);
}
}