Merge "Revert "Introducing new variable to enable/disable shortcuts."" into tm-qpr-dev
diff --git a/Android.bp b/Android.bp
index 330c32e..0bbb3d2 100644
--- a/Android.bp
+++ b/Android.bp
@@ -19,6 +19,54 @@
min_launcher3_sdk_version = "26"
+// Common source files used to build launcher (java and kotlin)
+// All sources are split so they can be reused in many other libraries/apps in other folders
+filegroup {
+ name: "launcher-src",
+ srcs: [ "src/**/*.java", "src/**/*.kt" ],
+}
+
+filegroup {
+ name: "launcher-quickstep-src",
+ srcs: [ "quickstep/src/**/*.java", "quickstep/src/**/*.kt" ],
+}
+
+filegroup {
+ name: "launcher-go-src",
+ srcs: [ "go/src/**/*.java", "go/src/**/*.kt" ],
+}
+
+filegroup {
+ name: "launcher-go-quickstep-src",
+ srcs: [ "go/quickstep/src/**/*.java", "go/quickstep/src/**/*.kt" ],
+}
+
+filegroup {
+ name: "launcher-src_shortcuts_overrides",
+ srcs: [ "src_shortcuts_overrides/**/*.java", "src_shortcuts_overrides/**/*.kt" ],
+}
+
+filegroup {
+ name: "launcher-src_ui_overrides",
+ srcs: [ "src_ui_overrides/**/*.java", "src_ui_overrides/**/*.kt" ],
+}
+
+filegroup {
+ name: "launcher-ext_tests",
+ srcs: [ "ext_tests/**/*.java", "ext_tests/**/*.kt" ],
+}
+
+filegroup {
+ name: "launcher-quickstep-ext_tests",
+ srcs: [ "quickstep/ext_tests/**/*.java", "quickstep/ext_tests/**/*.kt" ],
+}
+
+// Proguard files for Launcher3
+filegroup {
+ name: "launcher-proguard-rules",
+ srcs: ["proguard.flags"],
+}
+
android_library {
name: "launcher-aosp-tapl",
libs: [
@@ -105,6 +153,7 @@
"androidx.cardview_cardview",
"com.google.android.material_material",
"iconloader_base",
+ "view_capture"
],
manifest: "AndroidManifest-common.xml",
sdk_version: "current",
@@ -139,14 +188,10 @@
"Launcher3CommonDepsLib",
],
srcs: [
- "src/**/*.java",
- "src/**/*.kt",
- "src_shortcuts_overrides/**/*.java",
- "src_shortcuts_overrides/**/*.kt",
- "src_ui_overrides/**/*.java",
- "src_ui_overrides/**/*.kt",
- "ext_tests/src/**/*.java",
- "ext_tests/src/**/*.kt",
+ ":launcher-src",
+ ":launcher-src_shortcuts_overrides",
+ ":launcher-src_ui_overrides",
+ ":launcher-ext_tests",
],
resource_dirs: [
"ext_tests/res",
@@ -202,61 +247,14 @@
}
-// Source code used for test helpers
-filegroup {
- name: "launcher-src-ext-tests",
- srcs: [
- "ext_tests/src/**/*.java",
- "ext_tests/src/**/*.kt",
- "quickstep/ext_tests/src/**/*.java",
- "quickstep/ext_tests/src/**/*.kt",
- ],
-}
-
-// Common source files used to build launcher
-filegroup {
- name: "launcher-src-no-build-config",
- srcs: [
- "src/**/*.java",
- "src/**/*.kt",
- "src_shortcuts_overrides/**/*.java",
- "src_shortcuts_overrides/**/*.kt",
- "quickstep/src/**/*.java",
- "quickstep/src/**/*.kt",
- ],
-}
-
-// Common source files used to build go launcher except go/src files
-filegroup {
- name: "launcher-go-src-no-build-config",
- srcs: [
- "src/**/*.java",
- "src/**/*.kt",
- "quickstep/src/**/*.java",
- "quickstep/src/**/*.kt",
- "go/quickstep/src/**/*.java",
- "go/quickstep/src/**/*.kt",
- ],
-}
-
-// Proguard files for Launcher3
-filegroup {
- name: "launcher-proguard-rules",
- srcs: ["proguard.flags"],
-}
-
// Library with all the dependencies for building Launcher Go
android_library {
name: "LauncherGoResLib",
srcs: [
- "src/**/*.java",
- "src/**/*.kt",
- "quickstep/src/**/*.java",
- "quickstep/src/**/*.kt",
- "go/src/**/*.java",
- "go/src/**/*.kt",
- "go/quickstep/src/**/*.java",
- "go/quickstep/src/**/*.kt",
+ ":launcher-src",
+ ":launcher-quickstep-src",
+ ":launcher-go-src",
+ ":launcher-go-quickstep-src",
],
resource_dirs: [
"go/res",
@@ -287,7 +285,9 @@
android_library {
name: "Launcher3QuickStepLib",
srcs: [
- ":launcher-src-no-build-config",
+ ":launcher-src",
+ ":launcher-quickstep-src",
+ ":launcher-src_shortcuts_overrides",
],
resource_dirs: [],
libs: [
@@ -319,9 +319,9 @@
static_libs: ["Launcher3CommonDepsLib"],
srcs: [
- "src/**/*.java",
- "src_ui_overrides/**/*.java",
- "go/src/**/*.java",
+ ":launcher-src",
+ ":launcher-go-src",
+ ":launcher-src_ui_overrides",
],
resource_dirs: ["go/res"],
@@ -405,12 +405,7 @@
min_sdk_version: "current",
target_sdk_version: "current",
- srcs: [
- "src/**/*.java",
- "quickstep/src/**/*.java",
- "go/src/**/*.java",
- "go/quickstep/src/**/*.java",
- ],
+ srcs: [ ],
resource_dirs: [
"go/quickstep/res",
diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml
index 02b83fe..951be4e 100644
--- a/AndroidManifest-common.xml
+++ b/AndroidManifest-common.xml
@@ -43,7 +43,8 @@
<!-- for rotating surface by arbitrary degree -->
<uses-permission android:name="android.permission.ROTATE_SURFACE_FLINGER" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
-
+ <uses-permission android:name="android.permission.READ_HOME_APP_SEARCH_DATA" />
+
<!--
Permissions required for read/write access to the workspace data. These permission name
should not conflict with that defined in other apps, as such an app should embed its package
diff --git a/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java b/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java
index a91ff44..bdac88a 100644
--- a/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java
+++ b/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java
@@ -17,6 +17,7 @@
package com.android.launcher3.testing;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import android.app.Activity;
import android.app.Application;
@@ -248,6 +249,9 @@
return response;
}
+ case TestProtocol.REQUEST_MODEL_QUEUE_CLEARED:
+ return getFromExecutorSync(MODEL_EXECUTOR, Bundle::new);
+
default:
return super.call(method, arg, extras);
}
diff --git a/go/quickstep/res/values-am/strings.xml b/go/quickstep/res/values-am/strings.xml
index 1bfaf66..ed34797 100644
--- a/go/quickstep/res/values-am/strings.xml
+++ b/go/quickstep/res/values-am/strings.xml
@@ -9,12 +9,12 @@
<string name="dialog_cancel" msgid="6464336969134856366">"ይቅር"</string>
<string name="dialog_settings" msgid="6564397136021186148">"ቅንብሮች"</string>
<string name="niu_actions_confirmation_title" msgid="3863451714863526143">"በማያ ገጹ ላይ ጽሑፍን ይተረጉሙ ወይም ያዳምጡ"</string>
- <string name="niu_actions_confirmation_text" msgid="2105271481950866089">"እንደ በማያ ገጽዎ ላይ ያለ ጽሑፍ፣ የድር አድራሻዎች እና ቅጽበታዊ ገጽ እይታዎች ያሉ መረጃዎች ለGoogle ሊጋሩ ይችላሉ።\n\nምን መረጃ እንደሚያጋሩ ለመቀየር ወደ "<b>"ቅንብሮች > መተግበሪያዎች > ነባሪ መተግበሪያዎች > ዲጂታል ረዳት መተግበሪያ"</b>" ይሂዱ።"</string>
+ <string name="niu_actions_confirmation_text" msgid="2105271481950866089">"እንደ በማያ ገጽዎ ላይ ያለ ጽሁፍ፣ የድር አድራሻዎች እና ቅጽበታዊ ገጽ እይታዎች ያሉ መረጃዎች ለGoogle ሊጋሩ ይችላሉ።\n\nምን መረጃ እንደሚያጋሩ ለመቀየር ወደ "<b>"ቅንብሮች > መተግበሪያዎች > ነባሪ መተግበሪያዎች > ዲጂታል ረዳት መተግበሪያ"</b>" ይሂዱ።"</string>
<string name="assistant_not_selected_title" msgid="5017072974603345228">"ይህንን ባህሪ ለመጠቀም ረዳት ይምረጡ"</string>
<string name="assistant_not_selected_text" msgid="3244613673884359276">"በማያ ገጽዎ ላይ ጽሑፍን ለማዳመጥ ወይም ለመተርጎም በቅንብሮች ውስጥ የዲጂታል ረዳት መተግበሪያን ይምረጡ"</string>
<string name="assistant_not_supported_title" msgid="1675788067597484142">"ይህንን ባህሪ ለመጠቀም ረዳትዎን ይቀይሩ"</string>
<string name="assistant_not_supported_text" msgid="1708031078549268884">"በማያ ገጽዎ ላይ ጽሑፍን ለማዳመጥ ወይም ለመተርጎም በቅንብሮች ውስጥ የዲጂታል ረዳት መተግበሪያዎን ይቀይሩ"</string>
- <string name="tooltip_listen" msgid="7634466447860989102">"በዚህ ማያ ገጽ ላይ ጽሑፍ ለማዳመጥ እዚህ መታ ያድርጉ"</string>
- <string name="tooltip_translate" msgid="4184845868901542567">"በዚህ ማያ ገጽ ላይ ጽሑፍ ለመተርጎም እዚህ መታ ያድርጉ"</string>
+ <string name="tooltip_listen" msgid="7634466447860989102">"በዚህ ማያ ገጽ ላይ ጽሁፍ ለማዳመጥ እዚህ መታ ያድርጉ"</string>
+ <string name="tooltip_translate" msgid="4184845868901542567">"በዚህ ማያ ገጽ ላይ ጽሁፍ ለመተርጎም እዚህ መታ ያድርጉ"</string>
<string name="toast_p2p_app_not_shareable" msgid="7229739094132131536">"ይህ መተግበሪያ ሊጋራ አይችልም"</string>
</resources>
diff --git a/go/quickstep/res/values-en-rCA/strings.xml b/go/quickstep/res/values-en-rCA/strings.xml
index 676ac43..664bd94 100644
--- a/go/quickstep/res/values-en-rCA/strings.xml
+++ b/go/quickstep/res/values-en-rCA/strings.xml
@@ -1,19 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_share_drop_target_label" msgid="5804774105974539508">"Share app"</string>
+ <string name="app_share_drop_target_label" msgid="5804774105974539508">"Share App"</string>
<string name="action_listen" msgid="2370304050784689486">"Listen"</string>
<string name="action_translate" msgid="8028378961867277746">"Translate"</string>
<string name="action_search" msgid="6269564710943755464">"Lens"</string>
- <string name="dialog_acknowledge" msgid="2804025517675853172">"OK"</string>
+ <string name="dialog_acknowledge" msgid="2804025517675853172">"GOT IT"</string>
<string name="dialog_cancel" msgid="6464336969134856366">"CANCEL"</string>
<string name="dialog_settings" msgid="6564397136021186148">"SETTINGS"</string>
<string name="niu_actions_confirmation_title" msgid="3863451714863526143">"Translate or listen to text on screen"</string>
- <string name="niu_actions_confirmation_text" msgid="2105271481950866089">"Information such as text on your screen, web addresses and screenshots may be shared with Google.\n\nTo change what information you share, go to "<b>"Settings > Apps > Default apps > Digital assistant app"</b>"."</string>
+ <string name="niu_actions_confirmation_text" msgid="2105271481950866089">"Information such as text on your screen, web addresses, and screenshots may be shared with Google.\n\nTo change what information you share, go to "<b>"Settings > Apps > Default apps > Digital assistant app"</b>"."</string>
<string name="assistant_not_selected_title" msgid="5017072974603345228">"Choose an assistant to use this feature"</string>
- <string name="assistant_not_selected_text" msgid="3244613673884359276">"To listen to or translate text on your screen, choose a digital assistant app in settings"</string>
+ <string name="assistant_not_selected_text" msgid="3244613673884359276">"To listen to or translate text on your screen, choose a digital assistant app in Settings"</string>
<string name="assistant_not_supported_title" msgid="1675788067597484142">"Change your assistant to use this feature"</string>
- <string name="assistant_not_supported_text" msgid="1708031078549268884">"To listen to or translate text on your screen, change your digital assistant app in settings"</string>
+ <string name="assistant_not_supported_text" msgid="1708031078549268884">"To listen to or translate text on your screen, change your digital assistant app in Settings"</string>
<string name="tooltip_listen" msgid="7634466447860989102">"Tap here to listen to text on this screen"</string>
<string name="tooltip_translate" msgid="4184845868901542567">"Tap here to translate text on this screen"</string>
<string name="toast_p2p_app_not_shareable" msgid="7229739094132131536">"This app can’t be shared"</string>
diff --git a/go/quickstep/res/values-my/strings.xml b/go/quickstep/res/values-my/strings.xml
index 0ca0e9c..cbb485a 100644
--- a/go/quickstep/res/values-my/strings.xml
+++ b/go/quickstep/res/values-my/strings.xml
@@ -5,7 +5,7 @@
<string name="action_listen" msgid="2370304050784689486">"နားထောင်ရန်"</string>
<string name="action_translate" msgid="8028378961867277746">"ဘာသာပြန်ရန်"</string>
<string name="action_search" msgid="6269564710943755464">"Lens"</string>
- <string name="dialog_acknowledge" msgid="2804025517675853172">"ရပြီ"</string>
+ <string name="dialog_acknowledge" msgid="2804025517675853172">"နားလည်ပြီ"</string>
<string name="dialog_cancel" msgid="6464336969134856366">"မလုပ်တော့"</string>
<string name="dialog_settings" msgid="6564397136021186148">"ဆက်တင်များ"</string>
<string name="niu_actions_confirmation_title" msgid="3863451714863526143">"ဖန်သားပြင်ပေါ်ရှိ စာသားကို ဘာသာပြန်ပါ (သို့) နားထောင်ပါ"</string>
diff --git a/protos/launcher_atom.proto b/protos/launcher_atom.proto
index 10eedc8..151ec5a 100644
--- a/protos/launcher_atom.proto
+++ b/protos/launcher_atom.proto
@@ -45,6 +45,9 @@
// Stores the origin of the Item
repeated Attribute item_attributes = 12;
+
+ // Stores whether the navigation bar is in kids mode.
+ optional bool is_kids_mode = 13;
}
message LauncherAttributes{
diff --git a/protos/view_capture.proto b/protos/view_capture.proto
deleted file mode 100644
index 98574dd..0000000
--- a/protos/view_capture.proto
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-syntax = "proto2";
-
-package com.android.launcher3.view;
-
-option java_outer_classname = "ViewCaptureData";
-
-message ExportedData {
-
- repeated FrameData frameData = 1;
-}
-
-message FrameData {
- optional int64 timestamp = 1;
- optional ViewNode node = 2;
-}
-
-message ViewNode {
- optional string classname = 1;
- optional string id = 2;
- optional int32 left = 3;
- optional int32 top = 4;
- optional int32 width = 5;
- optional int32 height = 6;
- optional int32 scrollX = 7;
- optional int32 scrollY = 8;
-
- optional float translationX = 9;
- optional float translationY = 10;
- optional float scaleX = 11 [default = 1];
- optional float scaleY = 12 [default = 1];
- optional float alpha = 13 [default = 1];
-
- optional bool willNotDraw = 14;
- optional bool clipChildren = 15;
- optional int32 visibility = 16;
-
- repeated ViewNode children = 17;
-}
diff --git a/quickstep/Android.bp b/quickstep/Android.bp
index f739f81..f5a8253 100644
--- a/quickstep/Android.bp
+++ b/quickstep/Android.bp
@@ -18,6 +18,11 @@
}
filegroup {
+ name: "launcher3-quickstep-manifest",
+ srcs: ["AndroidManifest.xml"],
+}
+
+filegroup {
name: "launcher3-quickstep-robolectric-src",
path: "robolectric_tests",
srcs: ["robolectric_tests/src/**/*.java"],
@@ -33,6 +38,7 @@
name: "launcher3-quickstep-oop-tests-src",
path: "tests",
srcs: [
+ "tests/src/com/android/quickstep/TaskbarModeSwitchRule.java",
"tests/src/com/android/quickstep/NavigationModeSwitchRule.java",
"tests/src/com/android/quickstep/AbstractQuickStepTest.java",
"tests/src/com/android/quickstep/TaplTestsQuickstep.java",
diff --git a/quickstep/ext_tests/src/com/android/quickstep/DebugQuickstepTestInformationHandler.java b/quickstep/ext_tests/src/com/android/quickstep/DebugQuickstepTestInformationHandler.java
index 84b907f..0b17a7b 100644
--- a/quickstep/ext_tests/src/com/android/quickstep/DebugQuickstepTestInformationHandler.java
+++ b/quickstep/ext_tests/src/com/android/quickstep/DebugQuickstepTestInformationHandler.java
@@ -15,23 +15,14 @@
*/
package com.android.quickstep;
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-
import android.content.Context;
-import android.content.res.Resources;
import android.os.Bundle;
import androidx.annotation.Nullable;
-import com.android.launcher3.BaseQuickstepLauncher;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.R;
-import com.android.launcher3.taskbar.LauncherTaskbarUIController;
import com.android.launcher3.testing.DebugTestInformationHandler;
import com.android.launcher3.testing.shared.TestProtocol;
-import java.util.concurrent.ExecutionException;
-
/**
* Class to handle requests from tests, including debug ones, to Quickstep Launcher builds.
*/
@@ -47,74 +38,14 @@
@Override
public Bundle call(String method, String arg, @Nullable Bundle extras) {
Bundle response = new Bundle();
- switch (method) {
- case TestProtocol.REQUEST_ENABLE_MANUAL_TASKBAR_STASHING:
- runOnUIThread(l -> {
- enableManualTaskbarStashing(l, true);
- });
- return response;
-
- case TestProtocol.REQUEST_DISABLE_MANUAL_TASKBAR_STASHING:
- runOnUIThread(l -> {
- enableManualTaskbarStashing(l, false);
- });
- return response;
-
- case TestProtocol.REQUEST_UNSTASH_TASKBAR_IF_STASHED:
- runOnUIThread(l -> {
- enableManualTaskbarStashing(l, true);
-
- BaseQuickstepLauncher quickstepLauncher = (BaseQuickstepLauncher) l;
- LauncherTaskbarUIController taskbarUIController =
- quickstepLauncher.getTaskbarUIController();
-
- // Allow null-pointer to catch illegal states.
- taskbarUIController.unstashTaskbarIfStashed();
-
- enableManualTaskbarStashing(l, false);
- });
- return response;
-
- case TestProtocol.REQUEST_STASHED_TASKBAR_HEIGHT: {
- final Resources resources = mContext.getResources();
- response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD,
- resources.getDimensionPixelSize(R.dimen.taskbar_stashed_size));
- return response;
- }
-
- default:
- response = super.call(method, arg, extras);
- if (response != null) return response;
- return mDebugTestInformationHandler.call(method, arg, extras);
+ if (TestProtocol.REQUEST_RECREATE_TASKBAR.equals(method)) {
+ // Allow null-pointer to catch illegal states.
+ runOnTISBinder(tisBinder -> tisBinder.getTaskbarManager().recreateTaskbar());
+ return response;
}
- }
-
- private void enableManualTaskbarStashing(Launcher launcher, boolean enable) {
- BaseQuickstepLauncher quickstepLauncher = (BaseQuickstepLauncher) launcher;
- LauncherTaskbarUIController taskbarUIController =
- quickstepLauncher.getTaskbarUIController();
-
- // Allow null-pointer to catch illegal states.
- taskbarUIController.enableManualStashingForTests(enable);
- }
-
- /**
- * Runs the given command on the UI thread.
- */
- private static void runOnUIThread(UIThreadCommand command) {
- try {
- MAIN_EXECUTOR.submit(() -> {
- command.execute(Launcher.ACTIVITY_TRACKER.getCreatedActivity());
- return null;
- }).get();
- } catch (ExecutionException | InterruptedException e) {
- throw new RuntimeException(e);
- }
- }
-
- private interface UIThreadCommand {
-
- void execute(Launcher launcher);
+ response = super.call(method, arg, extras);
+ if (response != null) return response;
+ return mDebugTestInformationHandler.call(method, arg, extras);
}
}
diff --git a/quickstep/res/layout-sw600dp/allset_navigation_and_hint.xml b/quickstep/res/layout-sw600dp/allset_navigation_and_hint.xml
new file mode 100644
index 0000000..44b3ecb
--- /dev/null
+++ b/quickstep/res/layout-sw600dp/allset_navigation_and_hint.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+
+ <TextView
+ android:id="@+id/navigation_settings"
+ style="@style/TextAppearance.GestureTutorial.LinkText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="24dp"
+ android:background="?android:attr/selectableItemBackground"
+ android:minHeight="48dp"
+ android:text="@string/allset_navigation_settings"
+ app:layout_constraintTop_toBottomOf="@id/subtitle"
+ app:layout_constraintStart_toStartOf="parent" />
+
+ <TextView
+ android:id="@+id/hint"
+ style="@style/TextAppearance.GestureTutorial.Feedback.Subtitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="24dp"
+ android:text="@string/allset_hint"
+ android:textSize="@dimen/allset_page_swipe_up_text_size"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent" />
+
+</merge>
\ No newline at end of file
diff --git a/quickstep/res/layout/activity_allset.xml b/quickstep/res/layout/activity_allset.xml
index 56e1d16..f08cabe 100644
--- a/quickstep/res/layout/activity_allset.xml
+++ b/quickstep/res/layout/activity_allset.xml
@@ -34,8 +34,6 @@
android:layout_height="match_parent"
android:gravity="center"
android:scaleType="centerCrop"
-
- app:lottie_rawRes="@raw/all_set_page_bg"
app:lottie_autoPlay="true"
app:lottie_loop="true" />
@@ -79,42 +77,8 @@
app:layout_constraintStart_toStartOf="parent"
android:gravity="start"/>
- <androidx.constraintlayout.widget.Guideline
- android:id="@+id/navigation_settings_guideline_bottom"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- app:layout_constraintGuide_percent="0.83" />
+ <include layout="@layout/allset_navigation_and_hint"/>
- <TextView
- android:id="@+id/navigation_settings"
- style="@style/TextAppearance.GestureTutorial.LinkText"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintBottom_toBottomOf="@id/navigation_settings_guideline_bottom"
- android:minHeight="48dp"
- android:background="?android:attr/selectableItemBackground"
- android:text="@string/allset_navigation_settings" />
-
- <androidx.constraintlayout.widget.Guideline
- android:id="@+id/hint_guideline_bottom"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- app:layout_constraintGuide_percent="0.94" />
-
- <TextView
- android:id="@+id/hint"
- style="@style/TextAppearance.GestureTutorial.Feedback.Subtitle"
- android:textSize="14sp"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintBottom_toBottomOf="@id/hint_guideline_bottom"
- android:text="@string/allset_hint"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/quickstep/res/layout/allset_navigation_and_hint.xml b/quickstep/res/layout/allset_navigation_and_hint.xml
new file mode 100644
index 0000000..4d5cf01
--- /dev/null
+++ b/quickstep/res/layout/allset_navigation_and_hint.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/navigation_settings_guideline_bottom"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ app:layout_constraintGuide_percent="0.83" />
+
+ <TextView
+ android:id="@+id/navigation_settings"
+ style="@style/TextAppearance.GestureTutorial.LinkText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="?android:attr/selectableItemBackground"
+ android:minHeight="48dp"
+ android:text="@string/allset_navigation_settings"
+ app:layout_constraintBottom_toBottomOf="@id/navigation_settings_guideline_bottom"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent" />
+
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/hint_guideline_bottom"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ app:layout_constraintGuide_percent="0.94" />
+
+ <TextView
+ android:id="@+id/hint"
+ style="@style/TextAppearance.GestureTutorial.Feedback.Subtitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/allset_hint"
+ android:textSize="@dimen/allset_page_swipe_up_text_size"
+ app:layout_constraintBottom_toBottomOf="@id/hint_guideline_bottom"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent" />
+
+</merge>
\ No newline at end of file
diff --git a/quickstep/res/layout/digital_wellbeing_toast.xml b/quickstep/res/layout/digital_wellbeing_toast.xml
index c4642e4..d5e3670 100644
--- a/quickstep/res/layout/digital_wellbeing_toast.xml
+++ b/quickstep/res/layout/digital_wellbeing_toast.xml
@@ -25,4 +25,6 @@
android:gravity="center"
android:importantForAccessibility="noHideDescendants"
android:textColor="?priv-android:attr/textColorOnAccent"
- android:textSize="14sp"/>
\ No newline at end of file
+ android:textSize="14sp"
+ android:autoSizeTextType="uniform"
+ android:autoSizeMaxTextSize="14sp"/>
\ No newline at end of file
diff --git a/quickstep/res/layout/task_desktop.xml b/quickstep/res/layout/task_desktop.xml
new file mode 100644
index 0000000..0c8543f
--- /dev/null
+++ b/quickstep/res/layout/task_desktop.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<com.android.quickstep.views.DesktopTaskView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipChildren="true"
+ android:clipToOutline="true"
+ android:defaultFocusHighlightEnabled="false"
+ android:focusable="true">
+
+ <!--
+ TODO(b249371338): DesktopTaskView extends from TaskView. TaskView expects TaskThumbnailView
+ and IconView with these ids to be present. Need to refactor RecentsView to accept child
+ views that do not inherint from TaskView only or create a generic TaskView that have
+ N number of tasks.
+ -->
+ <com.android.quickstep.views.TaskThumbnailView
+ android:id="@+id/snapshot"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ <com.android.quickstep.views.IconView
+ android:id="@+id/icon"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:focusable="false"
+ android:importantForAccessibility="no"
+ android:visibility="gone" />
+
+</com.android.quickstep.views.DesktopTaskView>
diff --git a/quickstep/res/layout/taskbar_all_apps_button.xml b/quickstep/res/layout/taskbar_all_apps_button.xml
new file mode 100644
index 0000000..b275305
--- /dev/null
+++ b/quickstep/res/layout/taskbar_all_apps_button.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<com.android.launcher3.views.IconButtonView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="@dimen/taskbar_icon_touch_size"
+ android:layout_height="@dimen/taskbar_icon_touch_size"
+ android:contentDescription="@string/all_apps_button_label"
+ android:backgroundTint="@color/all_apps_button_bg_color"
+ android:icon="@drawable/ic_all_apps_button"
+ />
diff --git a/quickstep/res/layout/transient_taskbar.xml b/quickstep/res/layout/transient_taskbar.xml
new file mode 100644
index 0000000..f9ece84
--- /dev/null
+++ b/quickstep/res/layout/transient_taskbar.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<com.android.launcher3.taskbar.TaskbarDragLayer
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/taskbar_container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:clipChildren="false">
+
+ <com.android.launcher3.taskbar.TaskbarView
+ android:id="@+id/taskbar_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:forceHasOverlappingRendering="false"
+ android:layout_gravity="bottom"
+ android:layout_marginBottom="@dimen/transient_taskbar_margin"
+ android:clipChildren="false" />
+
+ <com.android.launcher3.taskbar.TaskbarScrimView
+ android:id="@+id/taskbar_scrim"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+
+ <FrameLayout
+ android:id="@+id/navbuttons_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom" >
+
+ <FrameLayout
+ android:id="@+id/start_contextual_buttons"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:paddingStart="@dimen/taskbar_contextual_button_padding"
+ android:paddingEnd="@dimen/taskbar_contextual_button_padding"
+ android:paddingTop="@dimen/taskbar_contextual_padding_top"
+ android:gravity="center_vertical"
+ android:layout_gravity="start"/>
+
+ <LinearLayout
+ android:id="@+id/end_nav_buttons"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ android:gravity="center_vertical"
+ android:layout_gravity="end"/>
+
+ <FrameLayout
+ android:id="@+id/end_contextual_buttons"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:paddingTop="@dimen/taskbar_contextual_padding_top"
+ android:gravity="center_vertical"
+ android:layout_gravity="end"/>
+ </FrameLayout>
+
+ <com.android.launcher3.taskbar.StashedHandleView
+ android:id="@+id/stashed_handle"
+ tools:comment1="The actual size and shape will be set as a ViewOutlineProvider at runtime"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@color/taskbar_stashed_handle_dark_color"
+ android:clipToOutline="true"
+ android:layout_gravity="bottom"/>
+
+</com.android.launcher3.taskbar.TaskbarDragLayer>
\ No newline at end of file
diff --git a/quickstep/res/raw-sw600dp-land/all_set_page_bg.json b/quickstep/res/raw-sw600dp-land/all_set_page_bg.json
new file mode 100644
index 0000000..0863c31
--- /dev/null
+++ b/quickstep/res/raw-sw600dp-land/all_set_page_bg.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":180,"w":1280,"h":800,"nm":"3Second_MainWelcomeScreen_Tablet_Landscape_V02","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 5","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[288,540,0],"ix":2,"l":2},"a":{"a":0,"k":[50,50,0],"ix":1,"l":2},"s":{"a":0,"k":[25,25,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"colorAccentPrimaryVariant","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":180,"s":[56]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.07,"y":0.986},"o":{"x":0.167,"y":0.167},"t":0,"s":[231.832,-1174.545,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.773,"y":0.01},"t":95,"s":[231.832,-1979,0],"to":[0,0,0],"ti":[0,0,0]},{"t":180,"s":[231.832,-1174.545,0]}],"ix":2,"l":2},"a":{"a":0,"k":[-3514.717,-358.642,0],"ix":1,"l":2},"s":{"a":0,"k":[110,110,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[75.615,-96.908],[89.338,-70.276],[111.99,-50.668],[111.764,-20.709],[122.709,7.18],[108.586,33.602],[105.316,63.383],[80.533,80.216],[63.797,105.066],[34.03,108.453],[7.663,122.679],[-20.269,111.845],[-50.226,112.189],[-69.924,89.614],[-96.61,75.997],[-103.56,46.854],[-120.861,22.395],[-113.472,-6.639],[-117.425,-36.337],[-97.389,-58.612],[-87.087,-86.745],[-58.996,-97.158],[-36.8,-117.281],[-7.086,-113.445],[21.918,-120.948],[46.446,-103.744]],"o":[[-75.615,96.909],[-89.338,70.276],[-111.99,50.668],[-111.764,20.709],[-122.709,-7.18],[-108.586,-33.602],[-105.316,-63.383],[-80.533,-80.216],[-63.797,-105.066],[-34.03,-108.453],[-7.663,-122.679],[20.269,-111.845],[50.226,-112.188],[69.924,-89.614],[96.61,-75.997],[103.56,-46.854],[120.861,-22.395],[113.472,6.64],[117.425,36.337],[97.389,58.612],[87.088,86.745],[58.995,97.158],[36.8,117.281],[7.087,113.445],[-21.918,120.948],[-46.446,103.744]],"v":[[733.209,572.105],[531.711,675.932],[383.354,847.313],[156.685,845.606],[-54.323,928.412],[-254.235,821.562],[-479.555,796.823],[-606.913,609.309],[-794.927,482.691],[-820.554,257.47],[-928.191,57.981],[-846.217,-153.353],[-848.817,-380.013],[-678.021,-529.044],[-574.99,-730.949],[-354.499,-783.537],[-169.439,-914.435],[50.234,-858.532],[274.928,-888.434],[443.46,-736.847],[656.313,-658.903],[735.094,-446.359],[887.344,-278.426],[858.327,-53.616],[915.095,165.835],[784.928,351.409]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.839215686275,0.439215686275,0.388235294118,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":10,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-3509.952,-363.731],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Polystar 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":720,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"colorAccentPrimary","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.07],"y":[1.248]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[-56]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.719],"y":[0.172]},"t":95,"s":[-38]},{"t":180,"s":[-56]}],"ix":10},"p":{"s":true,"x":{"a":1,"k":[{"i":{"x":[0.07],"y":[1.032]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[138]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.719],"y":[0.022]},"t":95,"s":[-38]},{"t":180,"s":[138]}],"ix":3},"y":{"a":1,"k":[{"i":{"x":[0.07],"y":[1.034]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[1535]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.719],"y":[0.024]},"t":95,"s":[1338]},{"t":180,"s":[1535]}],"ix":4}},"a":{"a":0,"k":[164.438,1433.781,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[3079.125,4685.989],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.305882352941,0.309803921569,0.321568627451,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":10,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[164.438,1481.781],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/quickstep/res/raw-sw600dp/all_set_page_bg.json b/quickstep/res/raw-sw600dp/all_set_page_bg.json
new file mode 100644
index 0000000..14e8933
--- /dev/null
+++ b/quickstep/res/raw-sw600dp/all_set_page_bg.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":180,"w":800,"h":1280,"nm":"3Second_MainWelcomeScreen_Tablet_Portrait_V02","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":1,"nm":"Null 5","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[288,528,0],"ix":2,"l":2},"a":{"a":0,"k":[50,50,0],"ix":1,"l":2},"s":{"a":0,"k":[25,25,100],"ix":6,"l":2}},"ao":0,"sw":100,"sh":100,"sc":"#ffffff","ip":600,"op":600,"st":0,"bm":0,"hidden":0},{"ddd":0,"ind":2,"ty":4,"nm":".colorAccentPrimaryVariant","cl":"colorAccentPrimaryVariant","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":180,"s":[56]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.07,"y":0.986},"o":{"x":0.167,"y":0.167},"t":0,"s":[375.832,-1366.545,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.773,"y":0.01},"t":95,"s":[375.832,-2171,0],"to":[0,0,0],"ti":[0,0,0]},{"t":180,"s":[375.832,-1366.545,0]}],"ix":2,"l":2},"a":{"a":0,"k":[-3514.717,-358.642,0],"ix":1,"l":2},"s":{"a":0,"k":[135,135,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[75.615,-96.908],[89.338,-70.276],[111.99,-50.668],[111.764,-20.709],[122.709,7.18],[108.586,33.602],[105.316,63.383],[80.533,80.216],[63.797,105.066],[34.03,108.453],[7.663,122.679],[-20.269,111.845],[-50.226,112.189],[-69.924,89.614],[-96.61,75.997],[-103.56,46.854],[-120.861,22.395],[-113.472,-6.639],[-117.425,-36.337],[-97.389,-58.612],[-87.087,-86.745],[-58.996,-97.158],[-36.8,-117.281],[-7.086,-113.445],[21.918,-120.948],[46.446,-103.744]],"o":[[-75.615,96.909],[-89.338,70.276],[-111.99,50.668],[-111.764,20.709],[-122.709,-7.18],[-108.586,-33.602],[-105.316,-63.383],[-80.533,-80.216],[-63.797,-105.066],[-34.03,-108.453],[-7.663,-122.679],[20.269,-111.845],[50.226,-112.188],[69.924,-89.614],[96.61,-75.997],[103.56,-46.854],[120.861,-22.395],[113.472,6.64],[117.425,36.337],[97.389,58.612],[87.088,86.745],[58.995,97.158],[36.8,117.281],[7.087,113.445],[-21.918,120.948],[-46.446,103.744]],"v":[[733.209,572.105],[531.711,675.932],[383.354,847.313],[156.685,845.606],[-54.323,928.412],[-254.235,821.562],[-479.555,796.823],[-606.913,609.309],[-794.927,482.691],[-820.554,257.47],[-928.191,57.981],[-846.217,-153.353],[-848.817,-380.013],[-678.021,-529.044],[-574.99,-730.949],[-354.499,-783.537],[-169.439,-914.435],[50.234,-858.532],[274.928,-888.434],[443.46,-736.847],[656.313,-658.903],[735.094,-446.359],[887.344,-278.426],[858.327,-53.616],[915.095,165.835],[784.928,351.409]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.839215686275,0.439215686275,0.388235294118,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":10,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-3509.952,-363.731],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Polystar 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":720,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".colorAccentPrimary","cl":"colorAccentPrimary","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.07],"y":[1.248]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[-56]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.719],"y":[0.172]},"t":95,"s":[-38]},{"t":180,"s":[-56]}],"ix":10},"p":{"s":true,"x":{"a":1,"k":[{"i":{"x":[0.07],"y":[1.032]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[138]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.719],"y":[0.022]},"t":95,"s":[-38]},{"t":180,"s":[138]}],"ix":3},"y":{"a":1,"k":[{"i":{"x":[0.07],"y":[1.034]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[1535]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.719],"y":[0.024]},"t":95,"s":[1338]},{"t":180,"s":[1535]}],"ix":4}},"a":{"a":0,"k":[164.438,1433.781,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[3079.125,4685.989],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.305882352941,0.309803921569,0.321568627451,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":10,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[164.438,1481.781],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/res/raw/all_set_page_bg.json b/quickstep/res/raw/all_set_page_bg.json
similarity index 99%
rename from res/raw/all_set_page_bg.json
rename to quickstep/res/raw/all_set_page_bg.json
index 9705837..859d356 100644
--- a/res/raw/all_set_page_bg.json
+++ b/quickstep/res/raw/all_set_page_bg.json
@@ -1 +1 @@
-{"v":"5.7.8","fr":24,"ip":0,"op":72,"w":2472,"h":5352,"nm":"3Second_MAIN_Welcome","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 60","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[1508,1364,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":240,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"PinkFlower","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":72,"s":[56]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.07,"y":0.986},"o":{"x":0.167,"y":0.167},"t":0,"s":[1505.832,1379.455,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.773,"y":0.01},"t":38,"s":[1505.832,575,0],"to":[0,0,0],"ti":[0,0,0]},{"t":72,"s":[1505.832,1379.455,0]}],"ix":2,"l":2},"a":{"a":0,"k":[-3514.717,-358.642,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[75.615,-96.908],[89.338,-70.276],[111.99,-50.668],[111.764,-20.709],[122.709,7.18],[108.586,33.602],[105.316,63.383],[80.533,80.216],[63.797,105.066],[34.03,108.453],[7.663,122.679],[-20.269,111.845],[-50.226,112.189],[-69.924,89.614],[-96.61,75.997],[-103.56,46.854],[-120.861,22.395],[-113.472,-6.639],[-117.425,-36.337],[-97.389,-58.612],[-87.087,-86.745],[-58.996,-97.158],[-36.8,-117.281],[-7.086,-113.445],[21.918,-120.948],[46.446,-103.744]],"o":[[-75.615,96.909],[-89.338,70.276],[-111.99,50.668],[-111.764,20.709],[-122.709,-7.18],[-108.586,-33.602],[-105.316,-63.383],[-80.533,-80.216],[-63.797,-105.066],[-34.03,-108.453],[-7.663,-122.679],[20.269,-111.845],[50.226,-112.188],[69.924,-89.614],[96.61,-75.997],[103.56,-46.854],[120.861,-22.395],[113.472,6.64],[117.425,36.337],[97.389,58.612],[87.088,86.745],[58.995,97.158],[36.8,117.281],[7.087,113.445],[-21.918,120.948],[-46.446,103.744]],"v":[[733.209,572.105],[531.711,675.932],[383.354,847.313],[156.685,845.606],[-54.323,928.412],[-254.235,821.562],[-479.555,796.823],[-606.913,609.309],[-794.927,482.691],[-820.554,257.47],[-928.191,57.981],[-846.217,-153.353],[-848.817,-380.013],[-678.021,-529.044],[-574.99,-730.949],[-354.499,-783.537],[-169.439,-914.435],[50.234,-858.532],[274.928,-888.434],[443.46,-736.847],[656.313,-658.903],[735.094,-446.359],[887.344,-278.426],[858.327,-53.616],[915.095,165.835],[784.928,351.409]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.839215686275,0.439215686275,0.388235294118,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.839215746113,0.439215716194,0.388235324037,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":true},{"ty":"tr","p":{"a":0,"k":[-3509.952,-363.731],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Polystar 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":288,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Ellipse_Bottom","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.07],"y":[1.248]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[-56]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.719],"y":[0.172]},"t":38,"s":[-38]},{"t":72,"s":[-56]}],"ix":10},"p":{"s":true,"x":{"a":1,"k":[{"i":{"x":[0.07],"y":[1.032]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[1720]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.719],"y":[0.022]},"t":38,"s":[1544]},{"t":72,"s":[1720]}],"ix":3},"y":{"a":1,"k":[{"i":{"x":[0.07],"y":[1.034]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[4069]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.719],"y":[0.024]},"t":38,"s":[3872]},{"t":72,"s":[4069]}],"ix":4}},"a":{"a":0,"k":[164.438,1433.781,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[3079.125,4685.989],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.305882352941,0.309803921569,0.321568627451,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.882353001015,0.894118006089,0.886274988511,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":true},{"ty":"tr","p":{"a":0,"k":[164.438,1481.781],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":240,"st":0,"bm":0}],"markers":[]}
+{"v":"5.7.8","fr":24,"ip":0,"op":72,"w":2472,"h":5352,"nm":"3Second_MAIN_Welcome","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 60","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[1508,1364,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":240,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"PinkFlower","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":72,"s":[56]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.07,"y":0.986},"o":{"x":0.167,"y":0.167},"t":0,"s":[1505.832,1379.455,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.773,"y":0.01},"t":38,"s":[1505.832,575,0],"to":[0,0,0],"ti":[0,0,0]},{"t":72,"s":[1505.832,1379.455,0]}],"ix":2,"l":2},"a":{"a":0,"k":[-3514.717,-358.642,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[75.615,-96.908],[89.338,-70.276],[111.99,-50.668],[111.764,-20.709],[122.709,7.18],[108.586,33.602],[105.316,63.383],[80.533,80.216],[63.797,105.066],[34.03,108.453],[7.663,122.679],[-20.269,111.845],[-50.226,112.189],[-69.924,89.614],[-96.61,75.997],[-103.56,46.854],[-120.861,22.395],[-113.472,-6.639],[-117.425,-36.337],[-97.389,-58.612],[-87.087,-86.745],[-58.996,-97.158],[-36.8,-117.281],[-7.086,-113.445],[21.918,-120.948],[46.446,-103.744]],"o":[[-75.615,96.909],[-89.338,70.276],[-111.99,50.668],[-111.764,20.709],[-122.709,-7.18],[-108.586,-33.602],[-105.316,-63.383],[-80.533,-80.216],[-63.797,-105.066],[-34.03,-108.453],[-7.663,-122.679],[20.269,-111.845],[50.226,-112.188],[69.924,-89.614],[96.61,-75.997],[103.56,-46.854],[120.861,-22.395],[113.472,6.64],[117.425,36.337],[97.389,58.612],[87.088,86.745],[58.995,97.158],[36.8,117.281],[7.087,113.445],[-21.918,120.948],[-46.446,103.744]],"v":[[733.209,572.105],[531.711,675.932],[383.354,847.313],[156.685,845.606],[-54.323,928.412],[-254.235,821.562],[-479.555,796.823],[-606.913,609.309],[-794.927,482.691],[-820.554,257.47],[-928.191,57.981],[-846.217,-153.353],[-848.817,-380.013],[-678.021,-529.044],[-574.99,-730.949],[-354.499,-783.537],[-169.439,-914.435],[50.234,-858.532],[274.928,-888.434],[443.46,-736.847],[656.313,-658.903],[735.094,-446.359],[887.344,-278.426],[858.327,-53.616],[915.095,165.835],[784.928,351.409]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.839215686275,0.439215686275,0.388235294118,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.839215746113,0.439215716194,0.388235324037,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":true},{"ty":"tr","p":{"a":0,"k":[-3509.952,-363.731],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Polystar 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":288,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Ellipse_Bottom","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.07],"y":[1.248]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[-56]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.719],"y":[0.172]},"t":38,"s":[-38]},{"t":72,"s":[-56]}],"ix":10},"p":{"s":true,"x":{"a":1,"k":[{"i":{"x":[0.07],"y":[1.032]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[1720]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.719],"y":[0.022]},"t":38,"s":[1544]},{"t":72,"s":[1720]}],"ix":3},"y":{"a":1,"k":[{"i":{"x":[0.07],"y":[1.034]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[4069]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.719],"y":[0.024]},"t":38,"s":[3872]},{"t":72,"s":[4069]}],"ix":4}},"a":{"a":0,"k":[164.438,1433.781,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[3079.125,4685.989],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.305882352941,0.309803921569,0.321568627451,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.882353001015,0.894118006089,0.886274988511,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":true},{"ty":"tr","p":{"a":0,"k":[164.438,1481.781],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":240,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/quickstep/res/values-af/strings.xml b/quickstep/res/values-af/strings.xml
index e834157..8bfca3f 100644
--- a/quickstep/res/values-af/strings.xml
+++ b/quickstep/res/values-af/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Kry programvoorstelle op jou tuisskerm se gunstelingery"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Kry maklik toegang tot jou programme wat die meeste gebruik word, direk van die tuisskerm af. Voorstelle sal verander op grond van jou roetines. Programme in die onderste ry sal opskuif na jou tuisskerm."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Kry maklik toegang tot jou programme wat die meeste gebruik word, direk van die tuisskerm af. Voorstelle sal verander op grond van jou roetines. Programme in die gunstelingery sal na jou tuisskerm toe skuif."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Kry maklik toegang tot jou programme wat die meeste gebruik word, direk van die tuisskerm af. Voorstelle sal verander op grond van jou roetines. Programme in die onderste ry sal na \'n nuwe vouer toe skuif."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Kry programvoorstelle"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Nee, dankie"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Instellings"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"Tutoriaal <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"Gereed!"</string>
<string name="allset_hint" msgid="2384632994739392447">"Swiep op om na die tuisskerm toe te gaan"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"Jy is gereed om jou foon te begin gebruik"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"Jy is gereed om jou tablet te begin gebruik"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Stelselnavigasie-instellings"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Skermkiekie"</string>
<string name="action_split" msgid="2098009717623550676">"Verdeel"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Probeer ander program om verdeelde skerm te gebruik"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"Program steun nie verdeelde skerm nie."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"Jou organisasie laat nie hierdie program toe nie"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Slaan navigasietutoriaal oor?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Jy kan dit later in die <xliff:g id="NAME">%1$s</xliff:g>-program kry"</string>
diff --git a/quickstep/res/values-am/strings.xml b/quickstep/res/values-am/strings.xml
index 342bbe2..bc4209e 100644
--- a/quickstep/res/values-am/strings.xml
+++ b/quickstep/res/values-am/strings.xml
@@ -20,7 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recent_task_option_pin" msgid="7929860679018978258">"ሰካ"</string>
- <string name="recent_task_option_freeform" msgid="48863056265284071">"ነጻ ቅጽ"</string>
+ <string name="recent_task_option_freeform" msgid="48863056265284071">"ነፃ ቅጽ"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"ምንም የቅርብ ጊዜ ንጥሎች የሉም"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"የመተግበሪያ አጠቃቀም ቅንብሮች"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"ሁሉንም አጽዳ"</string>
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"በመነሻ ማያ ገጽዎ የተወዳጆች ረድፍ ላይ የመተግበሪያ አስተያየት ጥቆማዎችን ያግኙ"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"በጣም ስራ ላይ የዋሉ መተግበሪያዎችዎን በቀላሉ ከመነሻ ገጹ ሆነው ይድረሱባቸው። የአስተያየት ጥቆማዎች በእርስዎ ዕለት ተዕለት ተግባራት ላይ በመመስረት ይቀየራሉ። በታችኛው ረድፍ ላይ ያሉ መተግበሪያዎች ወደ መነሻ ገጽዎ ይወሰዳሉ።"</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"በጣም ሥራ ላይ የዋሉ መተግበሪያዎችዎን በቀላሉ ከመነሻ ገጹ ሆነው ይድረሱባቸው። የአስተያየት ጥቆማዎች በእርስዎ ዕለት ተዕለት ተግባራት ላይ በመመሥረት ይቀየራሉ። በተወዳጆች ረድፍ ውስጥ ያሉ መተግበሪያዎች ወደ የእርስዎ መነሻ ማያ ገጽ ይንቀሳቀሳሉ።"</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"በጣም ስራ ላይ የዋሉ መተግበሪያዎችዎን በቀላሉ ከመነሻ ገጹ ሆነው ይድረሱባቸው። የአስተያየት ጥቆማዎች በእርስዎ ዕለት ተዕለት ተግባራት ላይ በመመስረት ይቀየራሉ። በታችኛው ረድፍ ላይ ያሉ መተግበሪያዎች ወደ አዲስ አቃፊ ይወሰዳሉ።"</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"የመተግበሪያ አስተያየት ጥቆማዎችን አግኝ"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"አይ፣ አመሰግናለሁ"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"ቅንብሮች"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"አጋዥ ሥልጠና <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"ሁሉም ዝግጁ!"</string>
<string name="allset_hint" msgid="2384632994739392447">"ወደ መነሻ ለመሄድ በጣት ወደ ላይ ማንሸራተት"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"ስልክዎን መጠቀም ለመጀመር ዝግጁ ነዎት"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"ጡባዊዎን መጠቀም ለመጀመር ዝግጁ ነዎት"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"የስርዓት አሰሳ ቅንብሮች"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"ቅጽበታዊ ገጽ እይታ"</string>
<string name="action_split" msgid="2098009717623550676">"ክፈል"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"የተከፈለ ማያን ለመጠቀም ሌላ መተግበሪያ መታ ያድርጉ"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"መተግበሪያው የተከፈለ ማያ ገጽን አይደግፍም።"</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"ይህ ድርጊት በመተግበሪያው ወይም በእርስዎ ድርጅት አይፈቀድም"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"የአሰሳ አጋዥ ሥልጠናን ይዝለሉ?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"ይህን በኋላ በ<xliff:g id="NAME">%1$s</xliff:g> መተግበሪያው ውስጥ ማግኘት ይችላሉ"</string>
diff --git a/quickstep/res/values-ar/strings.xml b/quickstep/res/values-ar/strings.xml
index f15fcd4..b461230 100644
--- a/quickstep/res/values-ar/strings.xml
+++ b/quickstep/res/values-ar/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"رؤية التطبيقات المقترحة في صف التطبيقات المفضّلة في الشاشة الرئيسية"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"يمكنك الوصول إلى التطبيقات الأكثر استخدامًا بسهولة من الشاشة الرئيسية مباشرةً. سيتم تغيير الاقتراحات استنادًا إلى استخدامك الروتيني. وسيتم نقل التطبيقات من الصف السفلي في الشاشة الرئيسية إلى الصف الأعلى."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"يمكنك الوصول إلى التطبيقات الأكثر استخدامًا بسهولة من الشاشة الرئيسية مباشرةً. سيتم تغيير الاقتراحات استنادًا إلى سلاسل الإجراءات. سيتم نقل التطبيقات من صف التطبيقات المفضّلة إلى الشاشة الرئيسية."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"يمكنك الوصول إلى التطبيقات الأكثر استخدامًا بسهولة من الشاشة الرئيسية مباشرةً. سيتم تغيير الاقتراحات استنادًا إلى سلاسل الإجراءات. سيتم نقل التطبيقات من الصف الأسفل إلى مجلد جديد."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"رؤية تطبيقات مقترحة"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"لا، شكرًا"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"الإعدادات"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"الدليل التوجيهي <xliff:g id="CURRENT">%1$d</xliff:g> من إجمالي <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"اكتملت عملية الإعداد"</string>
<string name="allset_hint" msgid="2384632994739392447">"مرِّر سريعًا للأعلى للانتقال إلى الشاشة الرئيسية."</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"يمكنك الآن بدء استخدام هاتفك."</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"يمكنك الآن بدء استخدام جهازك اللوحي."</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"إعدادات التنقّل داخل النظام"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"لقطة شاشة"</string>
<string name="action_split" msgid="2098009717623550676">"تقسيم"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"انقر على تطبيق آخر لاستخدام وضع تقسيم الشاشة."</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"التطبيق لا يتيح تقسيم الشاشة."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"لا يسمح التطبيق أو لا تسمح مؤسستك بهذا الإجراء."</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"هل تريد تخطي الدليل التوجيهي؟"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"يمكنك العثور على هذا الدليل التوجيهي لاحقًا في التطبيق \"<xliff:g id="NAME">%1$s</xliff:g>\""</string>
diff --git a/quickstep/res/values-as/strings.xml b/quickstep/res/values-as/strings.xml
index b33abed..3bae94e 100644
--- a/quickstep/res/values-as/strings.xml
+++ b/quickstep/res/values-as/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"আপোনাৰ গৃহ স্ক্ৰীনৰ প্ৰিয় সমলৰ শাৰীটোত এপৰ পৰামর্শসমূহ পাওক"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"আপোনাৰ সকলোতকৈ বেছিকৈ ব্যৱহৃত এপ্সমূহ গৃহ স্ক্ৰীনতে সহজে এক্সেছ কৰক। আপোনাৰ ৰুটিনসমূহৰ ভিত্তিত পৰামর্শসমূহ সলনি হ\'ব। একেবাৰে তলৰ শাৰীটোত থকা এপ্সমূহ ওপৰৰ আপোনাৰ গৃহ স্ক্ৰীনলৈ যাব।"</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"আপোনাৰ সকলোতকৈ বেছিকৈ ব্যৱহৃত এপ্সমূহ গৃহ স্ক্ৰীনতে সহজে এক্সেছ কৰক। আপোনাৰ ৰুটিনসমূহৰ ভিত্তিত পৰামর্শসমূহ সলনি হ’ব। প্ৰিয় সমলৰ শাৰীত থকা এপ্সমূহ আপোনাৰ গৃহ স্ক্রীনলৈ যাব।"</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"আপোনাৰ সকলোতকৈ বেছিকৈ ব্যৱহৃত এপ্সমূহ গৃহ স্ক্ৰীনতে সহজে এক্সেছ কৰক। আপোনাৰ ৰুটিনসমূহৰ ভিত্তিত পৰামর্শসমূহ সলনি হ\'ব। একেবাৰে তলৰ শাৰীটোত থকা এপ্সমূহ এটা নতুন ফ\'ল্ডাৰলৈ যাব।"</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"এপৰ পৰামর্শসমূহ পাওক"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"নালাগে, ধন্যবাদ"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"ছেটিং"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"টিউট’ৰিয়েল <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"সকলো সাজু!"</string>
<string name="allset_hint" msgid="2384632994739392447">"গৃহ স্ক্ৰীনলৈ যাবলৈ ওপৰলৈ ছোৱাইপ কৰক"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"আপুনি আপোনাৰ ফ’নটো ব্যৱহাৰ কৰিবলৈ সাজু"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"আপুনি আপোনাৰ টেবলেটটো ব্যৱহাৰ কৰিবলৈ সাজু"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"ছিষ্টেম নেভিগেশ্বনৰ ছেটিং"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"স্ক্ৰীনশ্বট"</string>
<string name="action_split" msgid="2098009717623550676">"বিভাজন কৰক"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"বিভাজিত স্ক্ৰীন ব্যৱহাৰ কৰিবলৈ অন্য এটা এপত টিপক"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"এপ্টোৱে বিভাজিত স্ক্ৰীন সমৰ্থন নকৰে।"</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"এপ্টোৱে অথবা আপোনাৰ প্ৰতিষ্ঠানে এই কাৰ্যটোৰ অনুমতি নিদিয়ে"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"নেভিগেশ্বনৰ টিউট’ৰিয়েল এৰিব বিচাৰে নেকি?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"আপুনি এয়া পাছত <xliff:g id="NAME">%1$s</xliff:g> এপ্টোত বিচাৰিব পাৰিব"</string>
diff --git a/quickstep/res/values-az/strings.xml b/quickstep/res/values-az/strings.xml
index 07e29bb..ff01f86 100644
--- a/quickstep/res/values-az/strings.xml
+++ b/quickstep/res/values-az/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Ana ekranın sevimlilər sırasında tətbiq təklifləri alın"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Birbaşa Ana ekrandan ən çox istifadə edilən tətbiqlərə asanlıqla daxil olun. Təkliflər rejimlərinizə uyğun olaraq dəyişəcək. Aşağı sıradakı tətbiqlər Ana ekrana köçürüləcək."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Birbaşa Ana ekrandan ən çox işlədilən tətbiqlərə asanlıqla girin. Təkliflər rejimlərinizə uyğun olaraq dəyişəcək. Sevimlilər sırasındakı tətbiqlər Əsas ekrana köçürüləcək."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Birbaşa Əsas səhifədən ən çox istifadə edilən tətbiqlərə asanlıqla daxil olun. Təkliflər rejimlərinizə uyğun olaraq dəyişəcək. Aşağı sıradakı tətbiqlər yeni qovluğa köçürüləcək."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Tətbiq təklifləri əldə edin"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Yox, çox sağolun"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Ayarlar"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"Dərslik <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"Hər şey hazırdır!"</string>
<string name="allset_hint" msgid="2384632994739392447">"Əsas səhifəyə keçmək üçün yuxarı çəkin"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"Telefondan istifadəyə başlamağa hazırsınız"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"Planşetdən istifadəyə başlamağa hazırsınız"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Sistem naviqasiya ayarları"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Skrinşot"</string>
<string name="action_split" msgid="2098009717623550676">"Ayırın"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Bölmə ekranını istifadə etmək üçün başqa tətbiqə toxunun"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"Tətbiq ekran bölünməsini dəstəkləmir."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"Bu əməliyyata tətbiq və ya təşkilatınız tərəfindən icazə verilmir"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Naviqasiya dərsliyi ötürülsün?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Bunu sonra <xliff:g id="NAME">%1$s</xliff:g> tətbiqində tapa bilərsiniz"</string>
diff --git a/quickstep/res/values-b+sr+Latn/strings.xml b/quickstep/res/values-b+sr+Latn/strings.xml
index 42023d3..881237c 100644
--- a/quickstep/res/values-b+sr+Latn/strings.xml
+++ b/quickstep/res/values-b+sr+Latn/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Dobijajte predloge aplikacija u redu sa omiljenim stavkama na početnom ekranu"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Lako pristupajte aplikacijama koje najčešće koristite direktno sa početnog ekrana. Predlozi se menjaju na osnovu upotrebe. Aplikacije iz donjeg reda se premeštaju nagore na početni ekran."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Lako pristupajte aplikacijama koje najčešće koristite direktno sa početnog ekrana. Predlozi se menjaju na osnovu vaših rutina. Aplikacije iz reda sa omiljenim stavkama se premeštaju na početni ekran."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Lako pristupajte aplikacijama koje najčešće koristite direktno sa početnog ekrana. Predlozi se menjaju na osnovu upotrebe. Aplikacije iz donjeg reda se premeštaju u nov folder."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Prikazuj predloge aplikacija"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Ne, hvala"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Podešavanja"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"Vodič <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"Gotovo!"</string>
<string name="allset_hint" msgid="2384632994739392447">"Prevucite nagore da biste otvorili početni ekran"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"Spremni ste da počnete da koristite telefon"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"Spremni ste da počnete da koristite tablet"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Podešavanja kretanja kroz sistem"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Snimak ekrana"</string>
<string name="action_split" msgid="2098009717623550676">"Podeli"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Dodirnite drugu aplikaciju za podeljeni ekran"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"Aplikacija ne podržava podeljeni ekran."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"Aplikacija ili organizacija ne dozvoljavaju ovu radnju"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Želite da preskočite vodič za kretanje?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Možete da pronađete ovo kasnije u aplikaciji <xliff:g id="NAME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-be/strings.xml b/quickstep/res/values-be/strings.xml
index dc4dc65..db280f2 100644
--- a/quickstep/res/values-be/strings.xml
+++ b/quickstep/res/values-be/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Атрымлівайце прапановы праграм у пераліку абраных на Галоўным экране"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Атрымлiвайце доступ да праграм, якімі вы карыстаецеся найбольш часта, непасрэдна з Галоўнага экрана. Прапановы будуць змяняцца ў залежнасці ад вашых дзеянняў. Праграмы, якія знаходзяцца ў ніжнім радку, будуць перамешчаны на Галоўны экран."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Атрымлівайце доступ да праграм, якімі вы карыстаецеся найбольш часта, непасрэдна з Галоўнага экрана. Прапановы будуць змяняцца ў залежнасці ад вашых дзеянняў. Пералік абраных праграм будзе перамешчаны на Галоўны экран."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Атрымлiвайце просты доступ да праграм, якімі вы карыстаецеся найбольш часта, непасрэдна з Галоўнага экрана. Прапановы будуць змяняцца ў залежнасці ад вашых дзеянняў. Праграмы, якія знаходзяцца ў ніжнім радку, будуць перамешчаны ў новую папку."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Атрымліваць прапановы праграм"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Не, дзякуй"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Налады"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"Дапаможнік <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"Гатова!"</string>
<string name="allset_hint" msgid="2384632994739392447">"Каб перайсці на галоўны экран, правядзіце пальцам уверх"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"Вы можаце пачаць карыстанне тэлефонам"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"Вы можаце пачаць карыстанне планшэтам"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Налады навігацыі ў сістэме"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Здымак экрана"</string>
<string name="action_split" msgid="2098009717623550676">"Падзелены экран"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Для падзеленага экрана націсніце на іншую праграму"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"Праграма не падтрымлівае рэжым падзеленага экрана."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"Гэта дзеянне не дазволена праграмай ці вашай арганізацыяй"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Прапусціць дапаможнік па навігацыі?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Знайсці дапаможнік можна ў праграме \"<xliff:g id="NAME">%1$s</xliff:g>\""</string>
diff --git a/quickstep/res/values-bg/strings.xml b/quickstep/res/values-bg/strings.xml
index 560a03b..375fa6a 100644
--- a/quickstep/res/values-bg/strings.xml
+++ b/quickstep/res/values-bg/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Получаване на предложения за приложения в реда с любими на началния екран"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Осъществявайте лесен достъп до най-използваните от вас приложения директно от началния екран. Предложенията ще се променят въз основа на действията ви. Приложенията на най-долния ред ще се преместят на началния ви екран."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Осъществявайте лесен достъп до най-използваните от вас приложения директно от началния екран. Предложенията ще се променят въз основа на действията ви. Приложенията в реда с любими ще бъдат преместени на началния екран."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Осъществявайте лесен достъп до най-използваните от вас приложения директно от началния екран. Предложенията ще се променят въз основа на действията ви. Приложенията на най-долния ред ще се преместят в нова папка."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Предложения"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Не, благодаря"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Настройки"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"Урок <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"Готово!"</string>
<string name="allset_hint" msgid="2384632994739392447">"Прекарайте пръст нагоре, за да отворите началния екран"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"Можете да започнете да използвате телефона си"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"Можете да започнете да използвате таблета си"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Настройки за навигиране в системата"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Екранна снимка"</string>
<string name="action_split" msgid="2098009717623550676">"Разделяне на екрана"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Докоснете друго прил., за да ползвате разд. екран"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"Приложението не поддържа разделен екран."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"Това действие не е разрешено от приложението или организацията ви"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Пропускане на урока за навигиране?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Урокът е налице в приложението <xliff:g id="NAME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-bn/strings.xml b/quickstep/res/values-bn/strings.xml
index d4774a7..24d2f1a 100644
--- a/quickstep/res/values-bn/strings.xml
+++ b/quickstep/res/values-bn/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"হোম স্ক্রিনের \'ফেভারিট রো\' বিকল্পের জন্য অ্যাপ সাজেশন পান"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"হোম স্ক্রিন থেকে সরাসরি সব থেকে বেশি ব্যবহার করা অ্যাপগুলি অ্যাক্সেস করুন। আপনার রুটিনের উপর ভিত্তি করে সাজেশন পরির্তন করা হবে। নিচের সারিতে থাকা অ্যাপ আপনার হোম স্ক্রিনে সরানো হবে।"</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"খুব বেশি ব্যবহার করেন এমন অ্যাপগুলি হোম স্ক্রিন থেকে সহজে সরাসরি অ্যাক্সেস করুন। আপনার রুটিন অনুযায়ী সাজেশন পরির্তন করা হবে। \'ফেভারিট রো\' বিকল্পে থাকা অ্যাপগুলি হোম স্ক্রিনে সরিয়ে দেওয়া হবে।"</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"হোম স্ক্রিনের পাশে সব থেকে ব্যবহার করা অ্যাপ সহজেই অ্যাক্সেস করুন। আপনার রুটিনের উপর ভিত্তি করে সাজেশন পরির্তন করা হবে। নিচের সারিতে থাকা অ্যাপ নতুন ফোল্ডারে সরানো হবে।"</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"অ্যাপ সাজেশন পান"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"না থাক"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"সেটিংস"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"টিউটোরিয়াল <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"সব রেডি!"</string>
<string name="allset_hint" msgid="2384632994739392447">"হোম স্ক্রিনে যেতে উপরের দিকে সোয়াইপ করুন"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"এবারে আপনি ফোন ব্যবহার করতে পারবেন"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"এবারে আপনি ট্যাবলেট ব্যবহার করতে পারবেন"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"সিস্টেম নেভিগেশন সেটিংস"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"স্ক্রিনশট নিন"</string>
<string name="action_split" msgid="2098009717623550676">"স্প্লিট"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"স্প্লিটস্ক্রিন ব্যবহার করতে অন্য অ্যাপে ট্যাপ করুন"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"স্প্লিট-স্ক্রিনে এই অ্যাপ কাজ করে না।"</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"এই অ্যাপ বা আপনার প্রতিষ্ঠান এই অ্যাকশনটি পারফর্ম করার অনুমতি দেয়নি"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"নেভিগেশন টিউটোরিয়াল এড়িয়ে যেতে চান?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"আপনি <xliff:g id="NAME">%1$s</xliff:g> অ্যাপে পরে এটি খুঁজে পাবেন"</string>
diff --git a/quickstep/res/values-bs/strings.xml b/quickstep/res/values-bs/strings.xml
index 3743e24..208e457 100644
--- a/quickstep/res/values-bs/strings.xml
+++ b/quickstep/res/values-bs/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Primajte prijedloge aplikacija u redu omiljenih stavki početnog ekrana"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Jednostavno pristupite najčešće korištenim aplikacijama direktno s početnog ekrana. Prijedlozi će se mijenjati na osnovu vaših rutina. Aplikacije koje se nalaze u donjem redu će se premjestiti na početni ekran."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Jednostavno pristupite najčešće korištenim aplikacijama direktno s početnog ekrana. Prijedlozi će se mijenjati na osnovu vaših rutina. Aplikacije u redu omiljenih stavki će se premjestiti na početni ekran."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Jednostavno pristupite najčešće korištenim aplikacijama, direktno s početnog ekrana. Prijedlozi će se mijenjati na osnovu vaših rutina. Aplikacije koje se nalaze u donjem redu će se premjestiti u novi folder."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Prikaži prijedloge aplikacija"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Ne, hvala"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Postavke"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"Vodič <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"Sve je spremno!"</string>
<string name="allset_hint" msgid="2384632994739392447">"Prevucite prema gore da odete na početni ekran"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"Sve je spremno da počnete koristiti telefon"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"Sve je spremno da počnete koristiti tablet"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Postavke navigiranja sistemom"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Snimak ekrana"</string>
<string name="action_split" msgid="2098009717623550676">"Podijeli"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Dodirnite drugu apl. da koristite podijeljeni ekran"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"Aplikacija ne podržava podijeljeni ekran."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"Ovu radnju ne dozvoljava aplikacija ili vaša organizacija"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Preskočiti vodič za navigiranje?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Možete ga pronaći kasnije u aplikaciji <xliff:g id="NAME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-ca/strings.xml b/quickstep/res/values-ca/strings.xml
index 9400fba..2eb1492 100644
--- a/quickstep/res/values-ca/strings.xml
+++ b/quickstep/res/values-ca/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Obtén suggeriments d\'aplicacions a la fila Preferides de la teva pantalla d\'inici"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Accedeix fàcilment a les aplicacions que més utilitzes des de la pantalla d\'inici. Els suggeriments variaran en funció dels teus hàbits. Les aplicacions de la fila inferior pujaran a la pantalla d\'inici."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Accedeix fàcilment a les aplicacions que més utilitzes des de la pantalla d\'inici. Els suggeriments variaran en funció dels teus hàbits. Les aplicacions de la fila Preferides es mouran a la teva pantalla d\'inici."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Accedeix fàcilment a les aplicacions que més utilitzes des de la pantalla d\'inici. Els suggeriments variaran en funció dels teus hàbits. Les aplicacions de la fila inferior es mouran a una carpeta nova."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Mostra suggeriments d\'aplicacions"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"No, gràcies"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Configuració"</string>
@@ -78,6 +77,8 @@
<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">"Tot a punt!"</string>
<string name="allset_hint" msgid="2384632994739392447">"Llisca cap amunt per anar a la pàgina d\'inici"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"Ja pots començar a utilitzar el telèfon"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"Ja pots començar a utilitzar la tauleta"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Configuració de navegació del sistema"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Captura de pantalla"</string>
<string name="action_split" msgid="2098009717623550676">"Divideix"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Toca una altra aplicació per dividir la pantalla"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"L\'aplicació no admet la pantalla dividida."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"L\'aplicació o la teva organització no permeten aquesta acció"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Vols ometre el tutorial de navegació?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Pots trobar-lo més tard a l\'aplicació <xliff:g id="NAME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-cs/strings.xml b/quickstep/res/values-cs/strings.xml
index cb49a35..e337de0 100644
--- a/quickstep/res/values-cs/strings.xml
+++ b/quickstep/res/values-cs/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Nechte si na řádku oblíbených na ploše zobrazovat návrhy aplikací"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Mějte nejpoužívanější aplikace k dispozici přímo na ploše. Návrhy se budou měnit podle vašich zvyklostí. Aplikace ve spodním řádku se přesunou nahoru na vaši plochu."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Mějte nejpoužívanější aplikace k dispozici přímo na ploše. Návrhy se budou měnit podle vašich zvyklostí. Aplikace na řádku oblíbených se přesunou na plochu."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Mějte nejpoužívanější aplikace k dispozici přímo na ploše. Návrhy se budou měnit podle vašich zvyklostí. Aplikace ve spodním řádku se přesunou do nové složky."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Zobrazovat návrhy aplikací"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Ne, díky"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Nastavení"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"Výukový program <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"Hotovo!"</string>
<string name="allset_hint" msgid="2384632994739392447">"Přejetím nahoru se vrátíte na plochu"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"Jste připraveni začít používat telefon"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"Jste připraveni začít používat tablet"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Nastavení navigace v systému"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Snímek obrazovky"</string>
<string name="action_split" msgid="2098009717623550676">"Rozdělit"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Klepnutím na jinou aplikaci rozdělíte obrazovku"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"Aplikace nepodporuje režim rozdělené obrazovky."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"Aplikace nebo organizace zakazuje tuto akci"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Přeskočit výukový program k navigaci?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Program později najdete v aplikaci <xliff:g id="NAME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-da/strings.xml b/quickstep/res/values-da/strings.xml
index 621006b..2c9591a 100644
--- a/quickstep/res/values-da/strings.xml
+++ b/quickstep/res/values-da/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Få appforslag i rækken med favoritter på din startskærm"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Få nem adgang til dine mest brugte apps direkte fra startskærmen. Forslagene ændres ud fra dine vaner. Apps i nederste række bliver flyttet op til din startskærm."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Få nem adgang til dine mest brugte apps direkte fra startskærmen. Forslagene ændres ud fra dine vaner. Apps i rækken med favoritter bliver flyttet til din startskærm."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Få nem adgang til dine mest brugte apps direkte fra startskærmen. Forslagene ændres ud fra dine vaner. Apps i nederste række bliver flyttet til en ny mappe."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Få appforslag"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Nej tak"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Indstillinger"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"Selvstudie <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"Alt er parat!"</string>
<string name="allset_hint" msgid="2384632994739392447">"Stryg opad for at gå til startsiden"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"Du er klar til at bruge din telefon"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"Du er klar til at bruge din tablet"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Indstillinger for systemnavigation"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string>
<string name="action_split" msgid="2098009717623550676">"Opdel"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Tryk på en anden app for at bruge opdelt skærm"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"Appen understøtter ikke opdelt skærm."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"Appen eller din organisation tillader ikke denne handling"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Vil du springe selvstudiet for navigation over?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Du kan finde dette senere i appen <xliff:g id="NAME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-de/strings.xml b/quickstep/res/values-de/strings.xml
index d19c23b..d0b8983 100644
--- a/quickstep/res/values-de/strings.xml
+++ b/quickstep/res/values-de/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Lass dir in der Favoritenleiste auf dem Startbildschirm App-Vorschläge anzeigen"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Schneller Zugriff auf deine meistverwendeten Apps direkt über den Startbildschirm. Die Vorschläge werden deiner Nutzung entsprechend laufend angepasst. Apps in der unteren Reihe werden nach oben auf den Startbildschirm verschoben."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Schneller Zugriff auf deine meistverwendeten Apps direkt über den Startbildschirm. Die Vorschläge werden deiner Nutzung entsprechend laufend angepasst. Apps der Favoritenleiste werden auf den Startbildschirm verschoben."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Schneller Zugriff auf deine meistverwendeten Apps direkt über den Startbildschirm. Die Vorschläge werden deiner Nutzung entsprechend laufend angepasst. Apps in der unteren Reihe werden in einen neuen Ordner verschoben."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"App-Vorschläge erhalten"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Nein danke"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Einstellungen"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"Anleitung <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"Fertig!"</string>
<string name="allset_hint" msgid="2384632994739392447">"Nach oben wischen, um den Startbildschirm aufzurufen"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"Du kannst dein Smartphone jetzt verwenden"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"Du kannst dein Tablet jetzt verwenden"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Einstellungen der Systemsteuerung"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string>
<string name="action_split" msgid="2098009717623550676">"Teilen"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Für „Bildschirm teilen“ auf weitere App tippen"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"„Geteilter Bildschirm“ wird v. d. App nicht unterstützt."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"Die App oder deine Organisation lässt diese Aktion nicht zu"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Navigationstutorial überspringen?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Du findest es später auch in der <xliff:g id="NAME">%1$s</xliff:g> App"</string>
diff --git a/quickstep/res/values-el/strings.xml b/quickstep/res/values-el/strings.xml
index 2c6702b..a2ab151 100644
--- a/quickstep/res/values-el/strings.xml
+++ b/quickstep/res/values-el/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Δείτε τις προτεινόμενες εφαρμογές στη σειρά Αγαπημένα της αρχικής οθόνης."</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Αποκτήστε εύκολα πρόσβαση στις εφαρμογές που χρησιμοποιείτε περισσότερο απευθείας από την αρχική οθόνη. Οι προτάσεις θα αλλάζουν με βάση τις ρουτίνες σας. Οι εφαρμογές στην κάτω σειρά θα μετακινηθούν προς τα επάνω στην αρχική οθόνη."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Αποκτήστε εύκολα πρόσβαση στις εφαρμογές που χρησιμοποιείτε περισσότερο απευθείας από την αρχική οθόνη. Οι προτάσεις θα αλλάζουν με βάση τις ρουτίνες σας. Οι εφαρμογές στην σειρά Αγαπημένα θα μετακινηθούν στην αρχική οθόνη σας."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Αποκτήστε εύκολα πρόσβαση στις εφαρμογές που χρησιμοποιείτε περισσότερο, απευθείας από την αρχική οθόνη. Οι προτάσεις θα αλλάζουν με βάση τις ρουτίνες σας. Οι εφαρμογές στην κάτω σειρά θα μεταφερθούν σε νέο φάκελο."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Προβολή προτεινόμενων εφαρμογών"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Όχι, ευχαριστώ"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Ρυθμίσεις"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"Οδηγός <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"Όλα έτοιμα!"</string>
<string name="allset_hint" msgid="2384632994739392447">"Σύρετε προς τα πάνω για μετάβαση στην αρχική οθόνη."</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"Είστε έτοιμοι να ξεκινήσετε να χρησιμοποιείτε το τηλέφωνό σας"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"Είστε έτοιμοι να ξεκινήσετε να χρησιμοποιείτε το tablet."</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Ρυθμίσεις πλοήγησης συστήματος"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Στιγμιότυπο οθόνης"</string>
<string name="action_split" msgid="2098009717623550676">"Διαχωρισμός"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Πατήστε άλλη εφαρμογή για χρήση διαχωρισμού οθόνης"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"Η εφαρμογή δεν υποστηρίζει διαχωρισμό οθόνης."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"Αυτή η ενέργεια δεν επιτρέπεται από την εφαρμογή ή τον οργανισμό σας."</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Παράβλεψη οδηγού πλοήγησης;"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Βρείτε τον αργότερα στην εφαρμογή <xliff:g id="NAME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-en-rAU/strings.xml b/quickstep/res/values-en-rAU/strings.xml
index 7b297af..19b0c3c 100644
--- a/quickstep/res/values-en-rAU/strings.xml
+++ b/quickstep/res/values-en-rAU/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Get app suggestions on the favourites row of your home screen"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Easily access your most-used apps directly from the home screen. Suggestions will change based on your routines. Apps on the bottom row will move up to your home screen."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Easily access your most-used apps directly from the home screen. Suggestions will change based on your routines. Apps in the favourites row will move to your home screen."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Easily access your most-used apps directly from the home screen. Suggestions will change based on your routines. Apps on the bottom row will be moved to a new folder."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Get app suggestions"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"No, thanks"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Settings"</string>
@@ -78,6 +77,8 @@
<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">"Ready!"</string>
<string name="allset_hint" msgid="2384632994739392447">"Swipe up to go home"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"You’re ready to start using your phone"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"You’re ready to start using your tablet"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"System navigation settings"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string>
<string name="action_split" msgid="2098009717623550676">"Split"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Tap another app to use split-screen"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"App does not support split-screen."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"This action isn\'t allowed by the app or your organisation"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Skip navigation tutorial?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"You can find this later in the <xliff:g id="NAME">%1$s</xliff:g> app"</string>
diff --git a/quickstep/res/values-en-rCA/strings.xml b/quickstep/res/values-en-rCA/strings.xml
index 4013a71..f6498c3 100644
--- a/quickstep/res/values-en-rCA/strings.xml
+++ b/quickstep/res/values-en-rCA/strings.xml
@@ -25,19 +25,18 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"App usage settings"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Clear all"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Recent apps"</string>
- <string name="task_view_closed" msgid="9170038230110856166">"Task closed"</string>
+ <string name="task_view_closed" msgid="9170038230110856166">"Task Closed"</string>
<string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
<string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 minute"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> left today"</string>
<string name="title_app_suggestions" msgid="4185902664111965088">"App suggestions"</string>
<string name="all_apps_prediction_tip" msgid="2672336544844936186">"Your predicted apps"</string>
- <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Get app suggestions on the bottom row of your home screen"</string>
- <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Get app suggestions on the favourites row of your home screen"</string>
- <string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Easily access your most-used apps directly from the home screen. Suggestions will change based on your routines. Apps on the bottom row will move up to your home screen."</string>
- <string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Easily access your most-used apps directly from the home screen. Suggestions will change based on your routines. Apps in the favourites row will move to your home screen."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Easily access your most-used apps directly from the home screen. Suggestions will change based on your routines. Apps on the bottom row will be moved to a new folder."</string>
+ <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Get app suggestions on the bottom row of your Home screen"</string>
+ <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Get app suggestions on favorites row of your Home screen"</string>
+ <string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Easily access your most-used apps right on the Home screen. Suggestions will change based on your routines. Apps on the bottom row will move up to your Home screen."</string>
+ <string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Easily access your most-used apps right on the Home screen. Suggestions will change based on your routines. Apps in favorites row will move to your Home screen."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Get app suggestions"</string>
- <string name="hotseat_edu_dismiss" msgid="2781161822780201689">"No, thanks"</string>
+ <string name="hotseat_edu_dismiss" msgid="2781161822780201689">"No thanks"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Settings"</string>
<string name="hotseat_auto_enrolled" msgid="522100018967146807">"Most-used apps appear here, and change based on routines"</string>
<string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Drag apps off the bottom row to get app suggestions"</string>
@@ -45,47 +44,50 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"App suggestions enabled"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"App suggestions are disabled"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Predicted app: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
- <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Make sure that you swipe from the far-right or far-left edge."</string>
- <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Make sure that you swipe from the right or left edge to the middle of the screen and let go."</string>
- <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"You learned how to swipe from the right to go back. Next, learn how to switch apps."</string>
+ <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Make sure you swipe from the far-right or far-left edge."</string>
+ <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Make sure you swipe from the right or left edge to the middle of the screen and let go."</string>
+ <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"You learned how to swipe from the right to go back. Next up, learn how to switch apps."</string>
<string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"You completed the go back gesture."</string>
- <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Make sure that you don\'t swipe too close to the bottom of the screen."</string>
- <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"To change sensitivity of the back gesture, go to Settings"</string>
+ <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Make sure you don\'t swipe too close to the bottom of the screen."</string>
+ <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"To change the sensitivity of the back gesture, go to Settings"</string>
<string name="back_gesture_intro_title" msgid="19551256430224428">"Swipe to go back"</string>
<string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"To go back to the last screen, swipe from the left or right edge to the middle of the screen."</string>
- <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"To go back to the last screen, swipe with two fingers from the left or right edge to the middle of the screen."</string>
- <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Make sure that you swipe up from the bottom edge of the screen."</string>
- <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Make sure that you don\'t pause before letting go."</string>
- <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Make sure that you swipe straight up."</string>
- <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"You completed the go home gesture. Next, learn how to go back."</string>
- <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"You completed the go home gesture."</string>
+ <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"To go back to the last screen, swipe with 2 fingers from the left or right edge to the middle of the screen."</string>
+ <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Make sure you swipe up from the bottom edge of the screen."</string>
+ <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Make sure you don\'t pause before letting go."</string>
+ <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Make sure you swipe straight up."</string>
+ <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"You completed the go Home gesture. Next up, learn how to go back."</string>
+ <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"You completed the go Home gesture."</string>
<string name="home_gesture_intro_title" msgid="836590312858441830">"Swipe to go home"</string>
- <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Swipe up from the bottom of your screen. This gesture always takes you to the home screen."</string>
- <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Swipe up with two fingers from the bottom of the screen. This gesture always takes you to the home screen."</string>
- <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Make sure that you swipe up from the bottom edge of the screen."</string>
+ <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Swipe up from the bottom of your screen. This gesture always takes you to the Home screen."</string>
+ <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Swipe up with 2 fingers from the bottom of the screen. This gesture always takes you to the Home screen."</string>
+ <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Make sure you swipe up from the bottom edge of the screen."</string>
<string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Try holding the window for longer before releasing."</string>
- <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Make sure that you swipe straight up, then pause."</string>
+ <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Make sure you swipe straight up, then pause."</string>
<string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"You learned how to use gestures. To turn off gestures, go to Settings."</string>
<string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"You completed the switch apps gesture."</string>
<string name="overview_gesture_intro_title" msgid="2902054412868489378">"Swipe to switch apps"</string>
<string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"To switch between apps, swipe up from the bottom of your screen, hold, then release."</string>
- <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"To switch between apps, swipe up with two fingers from the bottom of your screen, hold, then release."</string>
+ <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"To switch between apps, swipe up with 2 fingers from the bottom of your screen, hold, then release."</string>
<string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"All set"</string>
<string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Done"</string>
<string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Settings"</string>
<string name="gesture_tutorial_try_again" msgid="65962545858556697">"Try again"</string>
<string name="gesture_tutorial_nice" msgid="2936275692616928280">"Nice!"</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">"Ready!"</string>
- <string name="allset_hint" msgid="2384632994739392447">"Swipe up to go home"</string>
+ <string name="allset_title" msgid="5021126669778966707">"All set!"</string>
+ <string name="allset_hint" msgid="2384632994739392447">"Swipe up to go Home"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"You’re ready to start using your phone"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"You’re ready to start using your tablet"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"System navigation settings"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Share"</string>
<string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string>
<string name="action_split" msgid="2098009717623550676">"Split"</string>
- <string name="toast_split_select_app" msgid="5453865907322018352">"Tap another app to use split-screen"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"App does not support split-screen."</string>
+ <string name="toast_split_select_app" msgid="5453865907322018352">"Tap another app to use splitscreen"</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"This action isn\'t allowed by the app or your organization"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Skip navigation tutorial?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"You can find this later in the <xliff:g id="NAME">%1$s</xliff:g> app"</string>
diff --git a/quickstep/res/values-en-rGB/strings.xml b/quickstep/res/values-en-rGB/strings.xml
index 7b297af..19b0c3c 100644
--- a/quickstep/res/values-en-rGB/strings.xml
+++ b/quickstep/res/values-en-rGB/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Get app suggestions on the favourites row of your home screen"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Easily access your most-used apps directly from the home screen. Suggestions will change based on your routines. Apps on the bottom row will move up to your home screen."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Easily access your most-used apps directly from the home screen. Suggestions will change based on your routines. Apps in the favourites row will move to your home screen."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Easily access your most-used apps directly from the home screen. Suggestions will change based on your routines. Apps on the bottom row will be moved to a new folder."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Get app suggestions"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"No, thanks"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Settings"</string>
@@ -78,6 +77,8 @@
<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">"Ready!"</string>
<string name="allset_hint" msgid="2384632994739392447">"Swipe up to go home"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"You’re ready to start using your phone"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"You’re ready to start using your tablet"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"System navigation settings"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string>
<string name="action_split" msgid="2098009717623550676">"Split"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Tap another app to use split-screen"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"App does not support split-screen."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"This action isn\'t allowed by the app or your organisation"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Skip navigation tutorial?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"You can find this later in the <xliff:g id="NAME">%1$s</xliff:g> app"</string>
diff --git a/quickstep/res/values-en-rIN/strings.xml b/quickstep/res/values-en-rIN/strings.xml
index 7b297af..19b0c3c 100644
--- a/quickstep/res/values-en-rIN/strings.xml
+++ b/quickstep/res/values-en-rIN/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Get app suggestions on the favourites row of your home screen"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Easily access your most-used apps directly from the home screen. Suggestions will change based on your routines. Apps on the bottom row will move up to your home screen."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Easily access your most-used apps directly from the home screen. Suggestions will change based on your routines. Apps in the favourites row will move to your home screen."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Easily access your most-used apps directly from the home screen. Suggestions will change based on your routines. Apps on the bottom row will be moved to a new folder."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Get app suggestions"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"No, thanks"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Settings"</string>
@@ -78,6 +77,8 @@
<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">"Ready!"</string>
<string name="allset_hint" msgid="2384632994739392447">"Swipe up to go home"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"You’re ready to start using your phone"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"You’re ready to start using your tablet"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"System navigation settings"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string>
<string name="action_split" msgid="2098009717623550676">"Split"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Tap another app to use split-screen"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"App does not support split-screen."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"This action isn\'t allowed by the app or your organisation"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Skip navigation tutorial?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"You can find this later in the <xliff:g id="NAME">%1$s</xliff:g> app"</string>
diff --git a/quickstep/res/values-en-rXC/strings.xml b/quickstep/res/values-en-rXC/strings.xml
index bd2a024..55b99c8 100644
--- a/quickstep/res/values-en-rXC/strings.xml
+++ b/quickstep/res/values-en-rXC/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Get app suggestions on favorites row of your Home screen"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Easily access your most-used apps right on the Home screen. Suggestions will change based on your routines. Apps on the bottom row will move up to your Home screen."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Easily access your most-used apps right on the Home screen. Suggestions will change based on your routines. Apps in favorites row will move to your Home screen."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Easily access your most-used apps, right on the Home screen. Suggestions will change based on your routines. Apps on the bottom row will move to a new folder."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Get app suggestions"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"No thanks"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Settings"</string>
@@ -78,6 +77,8 @@
<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">"All set!"</string>
<string name="allset_hint" msgid="2384632994739392447">"Swipe up to go Home"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"You’re ready to start using your phone"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"You’re ready to start using your tablet"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027">""<annotation id="link">"System navigation settings"</annotation>""</string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string>
<string name="action_split" msgid="2098009717623550676">"Split"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Tap another app to use splitscreen"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"App does not support split-screen."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"This action isn\'t allowed by the app or your organization"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Skip navigation tutorial?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"You can find this later in the <xliff:g id="NAME">%1$s</xliff:g> app"</string>
diff --git a/quickstep/res/values-es-rUS/strings.xml b/quickstep/res/values-es-rUS/strings.xml
index 06ee5bb..7bb40e9 100644
--- a/quickstep/res/values-es-rUS/strings.xml
+++ b/quickstep/res/values-es-rUS/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Obtén sugerencias de apps en la fila de favoritos de la pantalla principal"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Accede fácilmente en la pantalla principal a las apps que más usas. Las sugerencias cambiarán según tus rutinas. Las apps de la fila inferior se desplazarán hacia arriba en la pantalla principal."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Accede fácilmente en la pantalla principal a las apps que más usas. Las sugerencias cambiarán según tus rutinas. Se moverán a la pantalla principal las apps que estén en la fila de favoritos."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Accede fácilmente a las apps que más usas en la pantalla principal. Las sugerencias cambiarán según tus rutinas. Las apps de la fila inferior se moverán a una nueva carpeta."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Obtén sugerencias de aplicaciones"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"No, gracias"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Configuración"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"Instructivo <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"Todo listo"</string>
<string name="allset_hint" msgid="2384632994739392447">"Desliza el dedo hacia arriba para ir a la pantalla principal"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"Ya puedes empezar a usar tu teléfono"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"Ya puedes empezar a usar tu tablet"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Configuración de navegación del sistema"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Captura de pantalla"</string>
<string name="action_split" msgid="2098009717623550676">"Pantalla dividida"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Presiona otra app para usar la pantalla dividida"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"La app no es compatible con la función de pantalla dividida."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"La app o tu organización no permiten realizar esta acción"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"¿Omitir el instructivo de navegación?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Puedes encontrarlo en la app de <xliff:g id="NAME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-es/strings.xml b/quickstep/res/values-es/strings.xml
index 80c8b0b..6610768 100644
--- a/quickstep/res/values-es/strings.xml
+++ b/quickstep/res/values-es/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Recibe sugerencias de aplicaciones en la fila de aplicaciones favoritas de la pantalla de inicio"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Accede fácilmente a las aplicaciones que más usas desde la pantalla de inicio. Las sugerencias cambiarán según tus hábitos. Las aplicaciones de la fila inferior que tengas ahora se moverán hacia arriba en la pantalla de inicio."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Accede fácilmente a las aplicaciones que más usas desde la pantalla de inicio. Las sugerencias cambiarán según tus hábitos. Las aplicaciones de la fila de aplicaciones favoritas se moverán a la pantalla de inicio."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Accede fácilmente a las aplicaciones que más usas desde la pantalla de inicio. Las sugerencias cambiarán según tus hábitos. Las aplicaciones de la fila inferior que tengas ahora se moverán a una carpeta nueva."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Sí, obtener sugerencias"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"No, gracias"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Ajustes"</string>
@@ -78,6 +77,8 @@
<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">"¡Ya está!"</string>
<string name="allset_hint" msgid="2384632994739392447">"Desliza el dedo hacia arriba para ir a la pantalla de inicio"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"Ya puedes empezar a usar tu teléfono"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"Ya puedes empezar a usar tu tablet"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Ajustes de navegación del sistema"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Hacer captura"</string>
<string name="action_split" msgid="2098009717623550676">"Dividir"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Toca otra aplicación para usar la pantalla dividida"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"La aplicación no admite la pantalla dividida."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"No puedes hacerlo porque la aplicación o tu organización no lo permiten"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"¿Saltar tutorial de navegación?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Puedes consultarlo en otro momento en la aplicación <xliff:g id="NAME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-et/strings.xml b/quickstep/res/values-et/strings.xml
index 7f37a4b..67f4e1c 100644
--- a/quickstep/res/values-et/strings.xml
+++ b/quickstep/res/values-et/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Hankige avakuva lemmikute reale rakenduste soovitusi"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Pääsete enim kasutatavatele rakendustele hõlpsasti juurde otse avakuvalt. Soovitused muutuvad olenevalt teie rutiinist. Alumisel real olevad rakendused teisaldatakse teie avakuvale."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Pääsete enim kasutatavatele rakendustele hõlpsasti juurde otse avakuvalt. Soovitused muutuvad olenevalt teie rutiinist. Lemmikute real olevad rakendused teisaldatakse teie avakuvale."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Pääsete enim kasutatavatele rakendustele hõlpsasti juurde otse avakuvalt. Soovitused muutuvad olenevalt teie rutiinist. Alumisel real olevad rakendused teisaldatakse uude kausta."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Hangi rakenduste soovitusi"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Tänan, ei"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Seaded"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"Õpetus <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"Valmis!"</string>
<string name="allset_hint" msgid="2384632994739392447">"Avakuvale liikumiseks pühkige üles"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"Olete valmis oma telefoni kasutama."</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"Olete valmis oma tahvelarvutit kasutama"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Süsteemi navigeerimisseaded"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Ekraanipilt"</string>
<string name="action_split" msgid="2098009717623550676">"Eralda"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Jagatud kuva kasutamiseks puudutage muud rakendust"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"Rakendus ei toeta jagatud ekraani."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"Rakendus või teie organisatsioon on selle toimingu keelanud"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Kas jätta navigeerimise õpetused vahele?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Leiate selle hiljem rakendusest <xliff:g id="NAME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-eu/strings.xml b/quickstep/res/values-eu/strings.xml
index 4169838..0185bb9 100644
--- a/quickstep/res/values-eu/strings.xml
+++ b/quickstep/res/values-eu/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Jaso aplikazioen iradokizunak hasierako pantailako gogokoen errenkadan"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Atzitu erraz aplikazio erabilienak hasierako pantailatik bertatik. Ohituren arabera aldatuko dira iradokizunak. Hasierako pantailara eramango dira beheko errenkadan dauden aplikazioak."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Atzitu erraz aplikazio erabilienak hasierako pantailatik bertatik. Ohituren arabera aldatuko dira iradokizunak. Gogokoen errenkadako aplikazioak hasierako pantailara eramango ditugu."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Atzitu erraz aplikazio erabilienak hasierako pantailatik bertatik. Ohituren arabera aldatuko dira iradokizunak. Karpeta berri batera eramango dira beheko errenkadan dauden aplikazioak."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Jaso aplikazioen iradokizunak"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Ez, eskerrik asko"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Ezarpenak"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"Tutoriala: <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"Dena prest!"</string>
<string name="allset_hint" msgid="2384632994739392447">"Pasatu hatza gora hasierako pantailara joateko"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"Prest zaude telefonoa erabiltzen hasteko"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"Prest zaude tableta erabiltzen hasteko"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Sisteman nabigatzeko ezarpenak"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Atera pantaila-argazki bat"</string>
<string name="action_split" msgid="2098009717623550676">"Zatitu"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Sakatu beste aplikazio bat pantaila zatitzeko"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"Aplikazioak ez du onartzen pantaila zatitua."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"Aplikazioak edo erakundeak ez du eman ekintza hori gauzatzeko baimena"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Nabigazio-tutoriala saltatu nahi duzu?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"<xliff:g id="NAME">%1$s</xliff:g> aplikazioan dago eskuragarri tutoriala"</string>
diff --git a/quickstep/res/values-fa/strings.xml b/quickstep/res/values-fa/strings.xml
index de6d7e5..44c9975 100644
--- a/quickstep/res/values-fa/strings.xml
+++ b/quickstep/res/values-fa/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"دریافت «پیشنهاد برنامه» در ردیف موارد دلخواه صفحه اصلی"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"بهراحتی در صفحه اصلی به پرکاربردترین برنامهها دسترسی داشته باشید. پیشنهادها براساس روالهایتان تغییر خواهد کرد. برنامههای ردیف پایین در صفحه اصلی به بالا منتقل خواهند شد."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"بهراحتی در صفحه اصلی به پرکاربردترین برنامهها دسترسی داشته باشید. پیشنهادها براساس روالهایتان تغییر خواهد کرد. برنامههای موجود در ردیف موارد دلخواه به صفحه اصلی منتقل میشوند."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"بهراحتی در صفحه اصلی به پرکاربردترین برنامهها دسترسی داشته باشید. پیشنهادها براساس روالهایتان تغییر خواهد کرد. برنامههای ردیف پایین به پوشه جدیدی منتقل خواهند شد."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"دریافت پیشنهادهای برنامه"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"نه متشکرم"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"تنظیمات"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"آموزش گامبهگام <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"همه چیز آماده است!"</string>
<string name="allset_hint" msgid="2384632994739392447">"برای رفتن به «صفحه اصلی»، تند بهبالا بکشید"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"آمادهاید از تلفنتان استفاده کنید"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"آمادهاید از رایانه لوحیتان استفاده کنید"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"تنظیمات پیمایش سیستم"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"نماگرفت"</string>
<string name="action_split" msgid="2098009717623550676">"دونیمه"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"برای استفاده از صفحهٔ دونیمه، روی برنامه دیگری ضربه بزنید"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"برنامه از صفحهٔ دونیمه پشتیبانی نمیکند."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"برنامه یا سازمان شما اجازه نمیدهد این کنش انجام شود."</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"آموزش گامبهگام پیمایش رد شود؟"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"میتوانید آن را بعداً در برنامه <xliff:g id="NAME">%1$s</xliff:g> پیدا کنید"</string>
diff --git a/quickstep/res/values-fi/strings.xml b/quickstep/res/values-fi/strings.xml
index 8c50f70..adff666 100644
--- a/quickstep/res/values-fi/strings.xml
+++ b/quickstep/res/values-fi/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Näytä sovellusehdotuksia aloitusnäytön Suosikit-rivillä"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Voit avata käytetyimmät sovellukset kätevästi aloitusnäytöltä. Ehdotukset muuttuvat rutiiniesi perusteella. Alimmalla rivillä olevat sovellukset siirretään aloitusnäytön yläosaan."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Voit avata käytetyimmät sovellukset kätevästi aloitusnäytöltä. Ehdotukset muuttuvat rutiiniesi perusteella. Suosikit-rivillä olevat sovellukset siirretään aloitusnäytölle."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Voit avata käytetyimmät sovellukset kätevästi aloitusnäytöltä. Ehdotukset muuttuvat rutiiniesi perusteella. Alimmalla rivillä olevat sovellukset siirretään uuteen kansioon."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Näytä sovellusehdotuksia"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Ei kiitos"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Asetukset"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"Ohje <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"Valmis"</string>
<string name="allset_hint" msgid="2384632994739392447">"Siirry aloitusnäytölle pyyhkäisemällä ylös"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"Olet valmis aloittamaan puhelimen käytön"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"Olet valmis aloittamaan tabletin käytön"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Järjestelmän navigointiasetukset"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Kuvakaappaus"</string>
<string name="action_split" msgid="2098009717623550676">"Jaa"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Avaa jaettu näyttö napauttamalla toista sovellusta"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"Sovellus ei tue jaetun näytön tilaa."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"Sovellus tai organisaatio ei salli tätä toimintoa"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Ohitetaanko navigointiohje?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Löydät tämän myöhemmin sovelluksesta: <xliff:g id="NAME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-fr-rCA/strings.xml b/quickstep/res/values-fr-rCA/strings.xml
index f92d55c..63f1a1a 100644
--- a/quickstep/res/values-fr-rCA/strings.xml
+++ b/quickstep/res/values-fr-rCA/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Retrouvez des suggestions d\'applications dans la rangée des favoris de votre écran d\'accueil"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Accédez facilement aux applications que vous utilisez le plus, directement à l\'écran d\'accueil. Les suggestions changeront en fonction de vos habitudes. Les applications dans la rangée du bas seront déplacées vers votre écran d\'accueil."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Accédez facilement aux applications que vous utilisez le plus, directement à l\'écran d\'accueil. Les suggestions changeront en fonction de vos habitudes. Les applications dans la rangée des favoris seront déplacées vers votre écran d\'accueil."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Accédez facilement aux applications que vous utilisez le plus, directement à l\'écran d\'accueil. Les suggestions changeront en fonction de vos habitudes. Les applications dans la rangée du bas seront déplacées vers un nouveau dossier."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Obtenir des suggestions d\'applications"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Non merci"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Paramètres"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"Étape <xliff:g id="CURRENT">%1$d</xliff:g> sur <xliff:g id="TOTAL">%2$d</xliff:g> du tutoriel"</string>
<string name="allset_title" msgid="5021126669778966707">"Tout est prêt!"</string>
<string name="allset_hint" msgid="2384632994739392447">"Balayez l\'écran vers le haut pour accéder à l\'écran d\'accueil"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"Vous êtes maintenant prêt à utiliser votre téléphone"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"Vous êtes maintenant prêt à utiliser votre tablette"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Paramètres de navigation du système"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Capture d\'écran"</string>
<string name="action_split" msgid="2098009717623550676">"Séparé"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Touchez une autre appli pour partager l\'écran"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"L\'appli n\'est pas compatible avec l\'écran partagé."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"L\'application ou votre organisation n\'autorise pas cette action"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Ignorer le tutoriel sur la navigation?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Vous trouverez le tutoriel dans l\'application <xliff:g id="NAME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-fr/strings.xml b/quickstep/res/values-fr/strings.xml
index 9ffa0cc..5eccc91 100644
--- a/quickstep/res/values-fr/strings.xml
+++ b/quickstep/res/values-fr/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Retrouvez des suggestions d\'applications dans la zone des favoris de votre écran d\'accueil"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Les suggestions d\'applications permettent d\'afficher vos applications favorites au bas de votre écran d\'accueil. Elles s\'adaptent à vos habitudes d\'utilisation. Les icônes auparavant affichées au bas de l\'écran seront déplacées vers le haut."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Accédez facilement aux applications dont vous vous servez le plus, directement depuis l\'écran d\'accueil. Ces suggestions peuvent varier en fonction de vos habitudes d\'utilisation. Les applications de la zone des favoris seront transférées sur votre écran d\'accueil."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Les suggestions d\'applications permettent d\'afficher vos applications favorites au bas de votre écran d\'accueil. Elles s\'adaptent à vos habitudes d\'utilisation. Les icônes auparavant affichées au bas de l\'écran seront placées dans un nouveau dossier."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Activer les suggestions"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Non, merci"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Paramètres"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"Tutoriel <xliff:g id="CURRENT">%1$d</xliff:g> sur <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"Tout est prêt !"</string>
<string name="allset_hint" msgid="2384632994739392447">"Balayez l\'écran vers le haut pour revenir à l\'accueil"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"Vous pouvez maintenant utiliser votre téléphone"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"Vous pouvez maintenant utiliser votre tablette"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Paramètres de navigation système"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Capture d\'écran"</string>
<string name="action_split" msgid="2098009717623550676">"Partager"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Appuyez sur autre appli pour utiliser écran partagé"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"Appli incompatible avec l\'écran partagé."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"Cette action n\'est pas autorisée par l\'application ou par votre organisation"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Ignorer le tutoriel de navigation ?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Vous le retrouverez dans l\'appli <xliff:g id="NAME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-gl/strings.xml b/quickstep/res/values-gl/strings.xml
index bea23b7..95c8761 100644
--- a/quickstep/res/values-gl/strings.xml
+++ b/quickstep/res/values-gl/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Recibe suxestións de aplicacións na fila de Favoritos da pantalla de inicio"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Accede facilmente desde a pantalla de inicio ás aplicacións que máis usas. As suxestións irán cambiando en función das túas rutinas. As aplicacións da fila inferior pasarán á pantalla de inicio."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Accede facilmente desde a pantalla de inicio ás aplicacións que máis usas. As suxestións irán cambiando en función das túas rutinas. As aplicacións da fila de Favoritos moveranse á túa pantalla de inicio."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Accede facilmente desde a pantalla de inicio ás aplicacións que máis usas. As suxestións irán cambiando en función das túas rutinas. As aplicacións da fila inferior pasarán a un cartafol novo."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Recibir suxestións de aplicacións"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Non, grazas"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Configuración"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"Titorial <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"Todo listo"</string>
<string name="allset_hint" msgid="2384632994739392447">"Pasa o dedo cara arriba para ir á pantalla de inicio"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"Xa podes comezar a utilizar o teléfono"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"Todo está listo para comezar a utilizar a tableta"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Configuración da navegación do sistema"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Facer captura"</string>
<string name="action_split" msgid="2098009717623550676">"Dividir"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Para usar a pantalla dividida, toca outra app"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"A app non admite a función de pantalla dividida."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"A aplicación ou a túa organización non permite realizar esta acción"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Queres omitir o titorial de navegación?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Podes atopalo máis tarde na aplicación <xliff:g id="NAME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-gu/strings.xml b/quickstep/res/values-gu/strings.xml
index a146b24..051077d 100644
--- a/quickstep/res/values-gu/strings.xml
+++ b/quickstep/res/values-gu/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"તમારી હોમ સ્ક્રીનની મનપસંદ પંક્તિમાં ઍપના સુઝાવો મેળવો"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"તમારી સૌથી વધુ વપરાતી ઍપને સીધી હોમ સ્ક્રીન પરથી જ સરળતાથી ઍક્સેસ કરો. સૂચનો તમારા રૂટિનના આધારે બદલાશે. નીચેની પંક્તિમાં રહેલી ઍપ તમારી હોમ સ્ક્રીન પર ખસેડાશે."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"તમારી સૌથી વધુ વપરાતી ઍપને સીધી હોમ સ્ક્રીન પરથી જ સરળતાથી ઍક્સેસ કરો. સૂચનો તમારા રૂટિનના આધારે બદલાશે. મનપસંદ પંક્તિમાં રહેલી ઍપ તમારી હોમ સ્ક્રીન પર ખસેડાશે."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"તમારી સૌથી વધુ વપરાતી ઍપને સીધી હોમ સ્ક્રીન પરથી જ સરળતાથી ઍક્સેસ કરો. સૂચનો તમારા રૂટિનના આધારે બદલાશે. નીચેની પંક્તિમાં રહેલી ઍપ નવા ફોલ્ડરમાં ખસેડાશે."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"ઍપ અંગેના સુઝાવો મેળવો"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"ના, આભાર"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"સેટિંગ"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"ટ્યૂટૉરિઅલ <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"બધું સેટ થઈ ગયું!"</string>
<string name="allset_hint" msgid="2384632994739392447">"હોમપેજ પર જવા માટે ઉપરની તરફ સ્વાઇપ કરો"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"તમે તમારા ફોનનો ઉપયોગ કરવા માટે તૈયાર છો"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"તમે તમારા ટૅબ્લેટનો ઉપયોગ કરવા માટે તૈયાર છો"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"સિસ્ટમના નૅવિગેશન સેટિંગ"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"સ્ક્રીનશૉટ"</string>
<string name="action_split" msgid="2098009717623550676">"વિભાજિત કરો"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"સ્પલિટસ્ક્રીનના વપરાશ માટે, કોઈ અન્ય ઍપ પર ટૅપ કરો"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"ઍપ સ્ક્રીન-વિભાજનને સપોર્ટ કરતી નથી."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"ઍપ કે તમારી સંસ્થા દ્વારા આ ક્રિયા કરવાની મંજૂરી નથી"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"નૅવિગેશન ટ્યૂટૉરિઅલ છોડી દઈએ?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"તમે આને પછીથી <xliff:g id="NAME">%1$s</xliff:g> ઍપમાં જોઈ શકો છો"</string>
diff --git a/quickstep/res/values-hi/strings.xml b/quickstep/res/values-hi/strings.xml
index c938e6b..434845f 100644
--- a/quickstep/res/values-hi/strings.xml
+++ b/quickstep/res/values-hi/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"अपनी होम स्क्रीन की सबसे नीचे वाली पंक्ति में पसंदीदा ऐप्लिकेशन के सुझाव पाएं"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"आपके ज़्यादातर इस्तेमाल किए जाने वाले ऐप्लिकेशन, सीधा अपनी होम स्क्रीन पर पाएं. ऐप्लिकेशन इस्तेमाल करने के आपके रूटीन के हिसाब से सुझाव बदलते रहते हैं. नीचे की पंक्ति के ऐप्लिकेशन होम स्क्रीन पर आ जाएंगे."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"सबसे ज़्यादा इस्तेमाल होने वाले ऐप्लिकेशन सीधे होम स्क्रीन पर देखें. आप ऐप्लिकेशन का कितना इस्तेमाल कर रहे हैं, उसके हिसाब से सुझाव बदलते रहते हैं. आपके पसंदीदा ऐप्लिकेशन, होम स्क्रीन पर नीचे की पंक्ति में दिखाई देंगे."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"सबसे ज़्यादा इस्तेमाल होने वाले ऐप्लिकेशन, सीधे होम स्क्रीन पर पाएं. आपके ऐप्लिकेशन इस्तेमाल करने के रूटीन के हिसाब से सुझाव बदलते रहते हैं. नीचे की पंक्ति के ऐप्लिकेशन एक नए फ़ोल्डर में चले जाएंगे."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"ऐप्लिकेशन के बारे में सुझाव पाएं"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"रहने दें"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"सेटिंग"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"ट्यूटोरियल <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"हो गया!"</string>
<string name="allset_hint" msgid="2384632994739392447">"होम स्क्रीन पर जाने के लिए, ऊपर की ओर स्वाइप करें"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"अब आपका फ़ोन, इस्तेमाल के लिए तैयार है"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"आप टैबलेट को इस्तेमाल करने के लिए तैयार हैं"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"सिस्टम नेविगेशन सेटिंग"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"स्क्रीनशॉट लें"</string>
<string name="action_split" msgid="2098009717623550676">"स्प्लिट स्क्रीन मोड"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"स्प्लिट स्क्रीन मोड के लिए, दूसरे ऐप पर टैप करें"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"यह ऐप्लिकेशन, स्प्लिट स्क्रीन पर काम नहीं करता है."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"ऐप्लिकेशन या आपका संगठन इस कार्रवाई की अनुमति नहीं देता"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"क्या आपको नेविगेशन ट्यूटोरियल छोड़ना है?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"इसे बाद में <xliff:g id="NAME">%1$s</xliff:g> ऐप्लिकेशन पर देखा जा सकता है"</string>
diff --git a/quickstep/res/values-hr/strings.xml b/quickstep/res/values-hr/strings.xml
index 640d7cc..681105e 100644
--- a/quickstep/res/values-hr/strings.xml
+++ b/quickstep/res/values-hr/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Primajte prijedloge aplikacija u retku omiljenih na početnom zaslonu"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Lako pristupite najčešće upotrebljavanim aplikacijama s početnog zaslona. Prijedlozi će se mijenjati na temelju vaših rutina. Aplikacije iz donjeg retka pomaknut će se na početni zaslon."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Lako pristupite najčešće upotrebljavanim aplikacijama s početnog zaslona. Prijedlozi će se mijenjati na temelju vaših rutina. Aplikacije koje se nalaze u retku omiljenih pomaknut će se na početni zaslon."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Lako pristupite najčešće upotrebljavanim aplikacijama s početnog zaslona. Prijedlozi će se mijenjati na temelju vaših rutina. Aplikacije iz donjeg retka pomaknut će se u novu mapu."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Predloži mi aplikacije"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Ne, hvala"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Postavke"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"Vodič <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"Sve je spremno!"</string>
<string name="allset_hint" msgid="2384632994739392447">"Prijeđite prstom prema gore da biste otvorili početni zaslon"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"Spremni ste za početak upotrebe telefona"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"Spremni ste za početak upotrebe tableta"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Postavke navigacije sustavom"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Snimka zaslona"</string>
<string name="action_split" msgid="2098009717623550676">"Podijeli"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Dodirnite drugu aplikaciju za podijeljeni zaslon"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"Aplikacija ne podržava podijeljeni zaslon."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"Aplikacija ili vaša organizacija ne dopuštaju ovu radnju"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Želite li preskočiti vodič za kretanje?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Kasnije ga možete pronaći u aplikaciji <xliff:g id="NAME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-hu/strings.xml b/quickstep/res/values-hu/strings.xml
index 6369293..46e4b2b 100644
--- a/quickstep/res/values-hu/strings.xml
+++ b/quickstep/res/values-hu/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Alkalmazásjavaslatokat kaphat a kezdőképernyőn megjelenő kedvencek sorában"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"A kezdőképernyőről könnyedén hozzáférhet a leggyakrabban használt alkalmazásokhoz. A javaslatok a rutinjai alapján változni fognak. Az alsó sorban lévő alkalmazások felkerülnek a kezdőképernyőre."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"A kezdőképernyőről könnyedén hozzáférhet a leggyakrabban használt alkalmazásokhoz. A javaslatok a rutinjai alapján változnak majd. A kedvencek sorában lévő alkalmazások a kezdőképernyőre kerülnek."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"A kezdőképernyőről könnyedén hozzáférhet a leggyakrabban használt alkalmazásokhoz. A javaslatok a rutinjai alapján változni fognak. Az alsó sorban lévő alkalmazások egy új mappába kerülnek."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Kérek javaslatokat"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Köszönöm, nem"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Beállítások"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"Útmutató (<xliff:g id="TOTAL">%2$d</xliff:g>/<xliff:g id="CURRENT">%1$d</xliff:g>.)"</string>
<string name="allset_title" msgid="5021126669778966707">"Kész is!"</string>
<string name="allset_hint" msgid="2384632994739392447">"Felfelé csúsztatva megjelenik a Kezdőképernyő"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"Készen áll a telefon használatára"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"Készen áll a táblagép használatára"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Rendszer-navigációs beállítások"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Képernyőkép"</string>
<string name="action_split" msgid="2098009717623550676">"Felosztás"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Koppintson másik appra a képernyőmegosztáshoz"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"Az alkalmazás nem támogatja az osztott képernyőt."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"Az alkalmazás vagy az Ön szervezete nem engedélyezi ezt a műveletet"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Kihagyja a navigáció bemutatóját?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Ezt később megtalálhatja a(z) <xliff:g id="NAME">%1$s</xliff:g> alkalmazásban"</string>
diff --git a/quickstep/res/values-hy/strings.xml b/quickstep/res/values-hy/strings.xml
index 9eeaa98..cc49954 100644
--- a/quickstep/res/values-hy/strings.xml
+++ b/quickstep/res/values-hy/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Ստացեք հավելվածների առաջարկներ հիմնական էկրանի «Ընտրանի» տողում"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Անմիջապես հիմնական էկրանից բացեք հաճախ օգտագործվող հավելվածները։ Առաջարկվող հավելվածները կփոփոխվեն՝ կախված ձեր գործողություններից։ Ներքևի տողի հավելվածները կտեղափոխվեն վերև հիմնական էկրանին։"</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Արագ բացեք հաճախ օգտագործվող հավելվածներն անմիջապես հիմնական էկրանից։ Առաջարկները կփոփոխվեն՝ կախված ձեր գործողություններից։ «Ընտրանի» տողի հավելվածները կտեղափոխվեն հիմնական էկրան։"</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Արագ բացեք հաճախ օգտագործվող հավելվածներն անմիջապես հիմնական էկրանից։ Առաջարկները կփոփոխվեն՝ կախված ձեր գործողություններից։ Ներքևում ցուցադրվող հավելվածները կտեղափոխվեն նոր պանակ։"</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Ստանալ առաջարկներ"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Ոչ, շնորհակալություն"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Կարգավորումներ"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"Ուղեցույց <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"Պատրաստ է"</string>
<string name="allset_hint" msgid="2384632994739392447">"Մատը սահեցրեք վերև՝ հիմնական էկրան անցնելու համար"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"Դուք արդեն կարող եք օգտագործել ձեր հեռախոսը"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"Դուք արդեն կարող եք օգտագործել ձեր պլանշետը"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Նավիգացիայի համակարգային կարգավորումներ"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Սքրինշոթ անել"</string>
<string name="action_split" msgid="2098009717623550676">"Տրոհել"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Հպեք այլ հավելվածի՝ էկրանը տրոհելու համար"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"Հավելվածը չի աջակցում էկրանի տրոհումը։"</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"Այս գործողությունն արգելված է հավելվածի կամ ձեր կազմակերպության կողմից"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Բաց թողնե՞լ նավիգացիայի ուղեցույցը"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Հետագայում սա կարող եք գտնել «<xliff:g id="NAME">%1$s</xliff:g>» հավելվածում"</string>
diff --git a/quickstep/res/values-in/strings.xml b/quickstep/res/values-in/strings.xml
index 14d8c3e..9012ba6 100644
--- a/quickstep/res/values-in/strings.xml
+++ b/quickstep/res/values-in/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Dapatkan saran aplikasi di baris favorit Layar utama"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Akses aplikasi yang paling sering digunakan dengan mudah, langsung di Layar utama. Saran akan berubah berdasarkan rutinitas Anda. Aplikasi di baris paling bawah akan berpindah naik ke Layar utama."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Mudah mengakses aplikasi yang paling sering digunakan, langsung di Layar utama. Saran akan berubah berdasarkan rutinitas Anda. Aplikasi di baris favorit akan berpindah ke Layar utama."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Akses aplikasi yang paling sering digunakan dengan mudah, langsung di Layar utama. Saran akan berubah berdasarkan rutinitas Anda. Aplikasi di baris paling bawah akan berpindah ke folder baru."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Dapatkan saran aplikasi"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Lain kali"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Setelan"</string>
@@ -78,6 +77,8 @@
<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_hint" msgid="2384632994739392447">"Geser ke atas untuk beralih ke Layar utama"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"Anda sudah siap untuk mulai menggunakan ponsel"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"Anda sudah siap untuk mulai menggunakan tablet"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Setelan navigasi sistem"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string>
<string name="action_split" msgid="2098009717623550676">"Pisahkan"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Ketuk aplikasi lain untuk menggunakan layar terpisah"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"Aplikasi tidak mendukung layar terpisah."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"Tindakan ini tidak diizinkan oleh aplikasi atau organisasi Anda"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Lewati tutorial gestur?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Anda dapat menemukan tutorial ini di lain waktu di aplikasi <xliff:g id="NAME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-is/strings.xml b/quickstep/res/values-is/strings.xml
index 1761203..d7f2075 100644
--- a/quickstep/res/values-is/strings.xml
+++ b/quickstep/res/values-is/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Fáðu tillögur að forritum á eftirlætissvæði heimaskjásins"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Nálgastu forritin sem þú notar mest auðveldlega á heimaskjánum. Tillögurnar breytast í samræmi við notkun þína. Forrit í neðstu röð færast upp á heimaskjáinn."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Nálgastu forritin sem þú notar mest á einfaldan hátt á heimaskjánum. Tillögurnar breytast í samræmi við notkun þína. Forrit á eftirlætissvæði færast á heimaskjáinn."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Nálgastu forritin sem þú notar mest auðveldlega á heimaskjánum. Tillögurnar breytast í samræmi við notkun þína. Forrit í neðstu röð færast í nýja möppu."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Fá tillögur að forritum"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Nei, takk"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Stillingar"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"Leiðsögn <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"Allt tilbúið!"</string>
<string name="allset_hint" msgid="2384632994739392447">"Strjúktu upp til að fara á heimaskjáinn"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"Þú getur byrjað að nota símann"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"Þú getur byrjað að nota spjaldtölvuna"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Stillingar kerfisstjórnunar"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Skjámynd"</string>
<string name="action_split" msgid="2098009717623550676">"Skipta"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Ýttu á annað forrit til að nota skjáskiptingu"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"Forritið styður ekki að skjánum sé skipt."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"Forritið eða fyrirtækið leyfir ekki þessa aðgerð"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Sleppa flettileiðsögn?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Þú getur fundið þetta síðar í forritinu <xliff:g id="NAME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-it/strings.xml b/quickstep/res/values-it/strings.xml
index 874d072..7df4dee 100644
--- a/quickstep/res/values-it/strings.xml
+++ b/quickstep/res/values-it/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Visualizza app suggerite nella riga dei Preferiti della schermata Home"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Accedi facilmente alle app più utilizzate direttamente dalla schermata Home. I suggerimenti varieranno in base alle tue routine. Le app nella riga inferiore verranno spostate più in alto sulla schermata Home."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Accedi facilmente alle app più utilizzate direttamente dalla schermata Home. I suggerimenti varieranno in base alle tue routine. Le app nella riga dei Preferiti verranno spostate nella schermata Home."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Accedi facilmente alle app più utilizzate direttamente dalla schermata Home. I suggerimenti varieranno in base alle tue routine. Le app nella riga inferiore verranno spostate in una nuova cartella."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Visualizza app suggerite"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"No, grazie"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Impostazioni"</string>
@@ -78,6 +77,8 @@
<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">"Finito."</string>
<string name="allset_hint" msgid="2384632994739392447">"Scorri verso l\'alto per andare alla schermata Home"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"Puoi iniziare a usare il tuo telefono"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"Puoi iniziare a usare il tuo tablet"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Impostazioni Navigazione del sistema"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string>
<string name="action_split" msgid="2098009717623550676">"Dividi"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Tocca un\'altra app per usare lo schermo diviso"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"L\'app non supporta la modalità Schermo diviso."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"Questa azione non è consentita dall\'app o dall\'organizzazione"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Saltare il tutorial di navigazione?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Puoi trovarlo in un secondo momento nell\'app <xliff:g id="NAME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-iw/strings.xml b/quickstep/res/values-iw/strings.xml
index 09d76f8..899011a 100644
--- a/quickstep/res/values-iw/strings.xml
+++ b/quickstep/res/values-iw/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"קבלת הצעות לאפליקציות בשורת המועדפות של מסך הבית"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"גישה נוחה לאפליקציות שנעשה בהן שימוש תכוף – ישירות ממסך הבית. ההצעות ישתנו בהתאם להרגלי השימוש שלך. אפליקציות שמופיעות בשורה התחתונה יעברו למעלה למסך הבית."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"גישה נוחה לאפליקציות שהשתמשת בהן הכי הרבה, ישירות ממסך הבית. ההצעות ישתנו בהתאם להרגלי השימוש שלך. אפליקציות בשורת המועדפות יועברו למסך הבית."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"גישה נוחה לאפליקציות שנעשה בהן שימוש תכוף – ישירות ממסך הבית. ההצעות ישתנו בהתאם להרגלי השימוש שלך. אפליקציות שמופיעות בשורה התחתונה יעברו לתיקייה חדשה."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"קבלת הצעות לאפליקציות"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"לא, תודה"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"הגדרות"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"מדריך <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"הכול מוכן!"</string>
<string name="allset_hint" msgid="2384632994739392447">"כדי לעבור לדף הבית, מחליקים כלפי מעלה"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"הכול מוכן ואפשר להתחיל להשתמש בטלפון"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"הכול מוכן ואפשר להתחיל להשתמש בטאבלט"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"הגדרות הניווט של המערכת"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"צילום מסך"</string>
<string name="action_split" msgid="2098009717623550676">"פיצול"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"צריך להקיש על אפליקציה אחרת כדי להשתמש במסך מפוצל"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"האפליקציה אינה תומכת במסך מפוצל."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"האפליקציה או הארגון שלך אינם מתירים את הפעולה הזאת"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"לדלג על המדריך לניווט?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"ניתן למצוא את המדריך מאוחר יותר באפליקציה <xliff:g id="NAME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-ja/strings.xml b/quickstep/res/values-ja/strings.xml
index 50c031e..3614e2d 100644
--- a/quickstep/res/values-ja/strings.xml
+++ b/quickstep/res/values-ja/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"ホーム画面のお気に入りの行でアプリの候補を利用できます"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"ホーム画面で、使用頻度の高いアプリに簡単にアクセスできるようになります。アプリの候補はルーティンに応じて変わります。ホーム画面で今一番下の行にあるアプリは、一行上に移動します。"</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"ホーム画面で、使用頻度の高いアプリに簡単にアクセスできるようになります。アプリの候補はルーティンに応じて変わります。お気に入りの行にあるアプリがホーム画面に移動します。"</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"ホーム画面で、使用頻度の高いアプリに簡単にアクセスできるようになります。アプリの候補はルーティンに応じて変わります。一番下の行にあるアプリが新しいフォルダに移動します。"</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"アプリの候補を利用"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"使用しない"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"設定"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"チュートリアル <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"設定完了"</string>
<string name="allset_hint" msgid="2384632994739392447">"ホームに移動するには上にスワイプします"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"スマートフォンを使用する準備ができました"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"これでタブレットが使えるようになりました"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"システム ナビゲーションの設定"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"スクリーンショット"</string>
<string name="action_split" msgid="2098009717623550676">"分割"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"分割画面を使用するには、他のアプリをタップします"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"アプリで分割画面がサポートされていません。"</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"この操作はアプリまたは組織で許可されていません"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"操作チュートリアルをスキップしますか?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"チュートリアルは後から <xliff:g id="NAME">%1$s</xliff:g> アプリで確認できます"</string>
diff --git a/quickstep/res/values-ka/strings.xml b/quickstep/res/values-ka/strings.xml
index 2bc0108..b862c6f 100644
--- a/quickstep/res/values-ka/strings.xml
+++ b/quickstep/res/values-ka/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"მიიღეთ აპების შემოთავაზებები მთავარი ეკრანის რჩეულების მწკრივში"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"მარტივად იქონიეთ ყველაზე ხშირად გამოყენებულ აპებზე წვდომა მთავარი ეკრანიდან. შეთავაზებები შეიცვლება თქვენი რუტინების მიხედვით. მოხდება ქვედა რიგში არსებული აპების მთავარ ეკრანზე გადატანა."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"მარტივად იქონიეთ წვდომა ყველაზე ხშირად გამოყენებულ აპებზე მთავარი ეკრანიდან. შეთავაზებები შეიცვლება თქვენი რუტინების მიხედვით. რჩეულების მწკრივში არსებული აპები თქვენს მთავარ ეკრანზე გადავა."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"მარტივად იქონიეთ ყველაზე ხშირად გამოყენებულ აპებზე წვდომა მთავარი ეკრანიდან. შეთავაზებები შეიცვლება თქვენი რუტინების მიხედვით. მოხდება ქვედა რიგში არსებული აპების ახალ საქაღალდეში გადატანა."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"აპის შეთავაზებების მიღება"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"არა, გმადლობთ"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"პარამეტრები"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"სახელმძღვანელო <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"მზადაა!"</string>
<string name="allset_hint" msgid="2384632994739392447">"მთავარ გვერდზე გადასასვლელად გადაფურცლეთ ზევით"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"მზად ხართ ტელეფონის გამოსაყენებლად"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"მზად ხართ ტაბლეტის გამოსაყენებლად"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"სისტემის ნავიგაციის პარამეტრები"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"ეკრანის ანაბეჭდი"</string>
<string name="action_split" msgid="2098009717623550676">"გაყოფა"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"შეეხეთ სხვა აპს ეკრანის გასაყოფად"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"ეკრანის გაყოფა არ არის მხარდაჭერილი აპის მიერ."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"ეს მოქმედება არ არის დაშვებული აპის ან თქვენი ორგანიზაციის მიერ"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"გსურთ, გამოტოვოთ ნავიგაციის სახელმძღვანელო?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"ამის მოგვიანებით პოვნა <xliff:g id="NAME">%1$s</xliff:g> აპში შეგიძლიათ"</string>
diff --git a/quickstep/res/values-kk/strings.xml b/quickstep/res/values-kk/strings.xml
index 7758125..8f42ff6 100644
--- a/quickstep/res/values-kk/strings.xml
+++ b/quickstep/res/values-kk/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Қолданба ұсыныстары негізгі экрандағы таңдаулылар жолында көрсетілетін болады"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Жиі пайдаланылатын қолданбаларға негізгі экраннан кіруге болады. Ұсыныстар күнделікті әрекеттеріңізге сәйкес өзгереді. Төменгі қатардағы қолданбалар негізгі экранға қарай жоғары жылжиды."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Жиі пайдаланылатын қолданбаларға негізгі экраннан оңай кіре аласыз. Ұсыныстар күнделікті әрекеттеріңізге сәйкес өзгереді. Таңдаулылар жолындағы қолданбалар негізгі экранға ауысады."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Жиі пайдаланылатын қолданбаларға негізгі экраннан кіруге болады. Ұсыныстар күнделікті әрекеттеріңізге сәйкес өзгереді. Төменгі қатардағы қолданбалар жаңа қалтаға жылжиды."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Қолданба ұсыныстарын алу"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Жоқ, рақмет"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Параметрлер"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"Оқулық: <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"Бәрі дайын!"</string>
<string name="allset_hint" msgid="2384632994739392447">"Негізгі экранға өту үшін жоғары қарай сырғытыңыз."</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"Телефоныңыз пайдалануға дайын."</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"Планшетіңіз пайдалануға дайын."</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Навигацияның жүйелік параметрлері"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Скриншот"</string>
<string name="action_split" msgid="2098009717623550676">"Бөлу"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Экранды бөлу режимін пайдалану үшін басқа қолданбаны түртіңіз."</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"Қолданбада экранды бөлу мүмкін емес."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"Бұл әрекетке қолданба не ұйым рұқсат етпейді."</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Қимылдар оқулығын өткізіп жіберу керек пе?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Мұны кейін <xliff:g id="NAME">%1$s</xliff:g> қолданбасынан таба аласыз."</string>
diff --git a/quickstep/res/values-km/strings.xml b/quickstep/res/values-km/strings.xml
index 93b2361..d1ed528 100644
--- a/quickstep/res/values-km/strings.xml
+++ b/quickstep/res/values-km/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"ទទួលបានការណែនាំកម្មវិធីនៅលើជួរដេកសំណព្វនៃអេក្រង់ដើមរបស់អ្នក"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"ចូលប្រើកម្មវិធីដែលអ្នកប្រើញឹកញាប់បំផុតបានយ៉ាងងាយស្រួលនៅលើអេក្រង់ដើមផ្ទាល់។ ការណែនាំនឹងប្រែប្រួលទៅតាមទម្លាប់របស់អ្នក។ កម្មវិធីនៅជួរខាងក្រោមនឹងផ្លាស់ទីឡើងទៅអេក្រង់ដើមរបស់អ្នក។"</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"ចូលប្រើកម្មវិធីដែលអ្នកប្រើញឹកញាប់បំផុតបានយ៉ាងងាយស្រួលនៅលើអេក្រង់ដើមដោយផ្ទាល់។ ការណែនាំនឹងប្រែប្រួលទៅតាមទម្លាប់របស់អ្នក។ កម្មវិធីនៅក្នុងជួរដេកសំណព្វនឹងផ្លាស់ទីទៅអេក្រង់ដើមរបស់អ្នក។"</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"ចូលប្រើកម្មវិធីដែលអ្នកប្រើញឹកញាប់បំផុតបានយ៉ាងងាយស្រួលនៅលើអេក្រង់ដើមផ្ទាល់។ ការណែនាំនឹងប្រែប្រួលទៅតាមទម្លាប់របស់អ្នក។ កម្មវិធីនៅជួរខាងក្រោមនឹងផ្លាស់ទីទៅថតថ្មី។"</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"ទទួលការណែនាំកម្មវិធី"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"ទេ អរគុណ"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"ការកំណត់"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"មេរៀនទី <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"រួចហើយ!"</string>
<string name="allset_hint" msgid="2384632994739392447">"អូសឡើងលើ ដើម្បីទៅកាន់អេក្រង់ដើម"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"អ្នកអាចចាប់ផ្ដើមប្រើទូរសព្ទរបស់អ្នកបានហើយ"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"អ្នកអាចចាប់ផ្ដើមប្រើថេប្លេតរបស់អ្នកបានហើយ"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"ការកំណត់ការរុករកប្រព័ន្ធ"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"រូបថតអេក្រង់"</string>
<string name="action_split" msgid="2098009717623550676">"បំបែក"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"ចុចកម្មវិធីផ្សេងទៀត ដើម្បីប្រើមុខងារបំបែកអេក្រង់"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"កម្មវិធីមិនអាចប្រើមុខងារបំបែកអេក្រង់បានទេ។"</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"សកម្មភាពនេះមិនត្រូវបានអនុញ្ញាតដោយកម្មវិធី ឬស្ថាប័នរបស់អ្នកទេ"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"រំលងមេរៀនអំពីការរុករកឬ?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"អ្នកអាចស្វែងរកមេរៀននេះនៅពេលក្រោយក្នុងកម្មវិធី <xliff:g id="NAME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-kn/strings.xml b/quickstep/res/values-kn/strings.xml
index 2c1f699..99fb8ba 100644
--- a/quickstep/res/values-kn/strings.xml
+++ b/quickstep/res/values-kn/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"ನಿಮ್ಮ ಹೋಮ್ ಸ್ಕ್ರೀನ್ನ ಮೆಚ್ಚಿನವುಗಳ ಸಾಲಿನಲ್ಲಿ ಆ್ಯಪ್ ಸಲಹೆಗಳನ್ನು ಪಡೆಯಿರಿ"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"ನೀವು ಹೆಚ್ಚು ಬಳಸಿದ ಆ್ಯಪ್ಗಳನ್ನು ಹೋಮ್ ಸ್ಕ್ರೀನ್ನಲ್ಲಿಯೇ ಸುಲಭವಾಗಿ ಪ್ರವೇಶಿಸಿ. ನಿಮ್ಮ ದಿನಚರಿಯನ್ನು ಆಧರಿಸಿ ಸಲಹೆಗಳು ಬದಲಾಗುತ್ತವೆ. ಕೆಳಭಾಗದ ಸಾಲಿನಲ್ಲಿನ ಆ್ಯಪ್ಗಳು ನಿಮ್ಮ ಹೋಮ್ ಸ್ಕ್ರೀನ್ ಕಡೆಗೆ ಚಲಿಸುತ್ತವೆ."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"ನೀವು ಹೆಚ್ಚು ಬಳಸಿದ ಆ್ಯಪ್ಗಳನ್ನು ಹೋಮ್ ಸ್ಕ್ರೀನ್ನಲ್ಲಿಯೇ ಸುಲಭವಾಗಿ ಪ್ರವೇಶಿಸಿ. ನಿಮ್ಮ ದಿನಚರಿಯನ್ನು ಆಧರಿಸಿ ಸಲಹೆಗಳು ಬದಲಾಗುತ್ತವೆ. ಮೆಚ್ಚಿನವುಗಳ ಸಾಲಿನಲ್ಲಿನ ಆ್ಯಪ್ಗಳು ನಿಮ್ಮ ಹೋಮ್ ಸ್ಕ್ರೀನ್ಗೆ ಚಲಿಸುತ್ತವೆ."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"ನೀವು ಹೆಚ್ಚು ಬಳಸಿದ ಆ್ಯಪ್ಗಳನ್ನು ಹೋಮ್ ಸ್ಕ್ರೀನ್ನಲ್ಲಿಯೇ ಸುಲಭವಾಗಿ ಪ್ರವೇಶಿಸಿ. ನಿಮ್ಮ ದಿನಚರಿಯನ್ನು ಆಧರಿಸಿ ಸಲಹೆಗಳು ಬದಲಾಗುತ್ತವೆ. ಕೆಳಭಾಗದ ಸಾಲಿನಲ್ಲಿನ ಆ್ಯಪ್ಗಳು ಹೊಸ ಫೋಲ್ಡರ್ಗೆ ಚಲಿಸುತ್ತವೆ."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"ಆ್ಯಪ್ ಸಲಹೆಗಳನ್ನು ಪಡೆಯಿರಿ"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"ಬೇಡ"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"ಟ್ಯುಟೋರಿಯಲ್ <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"ಎಲ್ಲವೂ ಸಿದ್ಧವಾಗಿದೆ!"</string>
<string name="allset_hint" msgid="2384632994739392447">"ಮುಖಪುಟಕ್ಕೆ ಹೋಗಲು ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"ನಿಮ್ಮ ಫೋನ್ ಬಳಸುವುದನ್ನು ಪ್ರಾರಂಭಿಸಲು ನೀವು ಸಿದ್ದರಾಗಿರುವಿರಿ"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"ನಿಮ್ಮ ಟ್ಯಾಬ್ಲೆಟ್ ಬಳಸುವುದನ್ನು ಪ್ರಾರಂಭಿಸಲು ನೀವು ಸಿದ್ದರಾಗಿರುವಿರಿ"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"ಸಿಸ್ಟಂ ನ್ಯಾವಿಗೇಶನ್ ಸೆಟ್ಟಿಂಗ್ಗಳು"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"ಸ್ಕ್ರೀನ್ಶಾಟ್"</string>
<string name="action_split" msgid="2098009717623550676">"ವಿಭಜಿಸಿ"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"ಸ್ಪ್ಲಿಟ್ ಸ್ಕ್ರೀನ್ ಅನ್ನು ಬಳಸಲು ಬೇರೊಂದು ಆ್ಯಪ್ ಮೇಲೆ ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"ಸ್ಪ್ಲಿಟ್ ಸ್ಕ್ರೀನ್ ಅನ್ನು ಆ್ಯಪ್ ಬೆಂಬಲಿಸುವುದಿಲ್ಲ."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"ಆ್ಯಪ್ ಅಥವಾ ನಿಮ್ಮ ಸಂಸ್ಥೆಯು ಈ ಕ್ರಿಯೆಯನ್ನು ಅನುಮತಿಸುವುದಿಲ್ಲ"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"ನ್ಯಾವಿಗೇಶನ್ ಟ್ಯುಟೋರಿಯಲ್ ಸ್ಕಿಪ್ ಮಾಡಬೇಕೇ?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"<xliff:g id="NAME">%1$s</xliff:g> ಆ್ಯಪ್ನಲ್ಲಿ ಇದನ್ನು ನಂತರ ಕಾಣಬಹುದು"</string>
diff --git a/quickstep/res/values-ko/strings.xml b/quickstep/res/values-ko/strings.xml
index 6626bc1..8af2ef7 100644
--- a/quickstep/res/values-ko/strings.xml
+++ b/quickstep/res/values-ko/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"홈 화면의 즐겨찾기 행에서 앱 제안 보기"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"홈 화면에서 자주 사용하는 앱에 바로 액세스할 수 있습니다. 제안은 사용 습관에 따라 바뀌며, 하단의 앱들은 홈 화면으로 이동합니다."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"홈 화면에서 가장 많이 사용한 앱에 바로 액세스할 수 있습니다. 제안은 루틴에 따라 달라집니다. 즐겨찾기 행의 앱이 홈 화면으로 이동합니다."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"홈 화면에서 자주 사용하는 앱에 바로 액세스할 수 있습니다. 제안은 사용 습관에 따라 바뀌며, 하단의 앱들은 새 폴더로 이동합니다."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"앱 제안받기"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"나중에"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"설정"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"튜토리얼 <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"설정 완료"</string>
<string name="allset_hint" msgid="2384632994739392447">"위로 스와이프하여 홈으로 이동"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"휴대전화를 사용할 준비가 되었습니다."</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"태블릿을 사용할 준비가 되었습니다."</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"시스템 탐색 설정"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"스크린샷"</string>
<string name="action_split" msgid="2098009717623550676">"분할"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"다른 앱을 탭하여 화면 분할 사용"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"앱이 화면 분할을 지원하지 않습니다."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"이 작업은 앱 또는 조직에서 허용되지 않습니다."</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"이동 방법 튜토리얼을 건너뛰시겠습니까?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"이 튜토리얼은 <xliff:g id="NAME">%1$s</xliff:g> 앱에서 다시 볼 수 있습니다."</string>
diff --git a/quickstep/res/values-ky/strings.xml b/quickstep/res/values-ky/strings.xml
index dac4d1d..b7721f4 100644
--- a/quickstep/res/values-ky/strings.xml
+++ b/quickstep/res/values-ky/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Сунушталган колдонмолор башкы экрандагы тандалмалардын катарында көрүнөт."</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Көп колдонулган колдонмолор башкы экранда жайгашып, алардын тизмеси маал-маалы менен өзгөрүп турат. Ылдый жакта жайгашкан тилкедеги колдонмолор башкы экранга жылдырылат."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Көп иштетилген колдонмолорго Башкы экрандан оңой кириңиз. Сунуштар тартиптин негизинде өзгөрөт. Тандалмалардын катарындагы колдонмолор башкы экраныңызга жылдырылат."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Көп колдонулган колдонмолор башкы экранда жайгашып, алардын тизмеси маал-маалы менен өзгөрүп турат. Ылдый жакта жайгашкан тилкедеги колдонмолор жаңы папкага жылдырылат."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Сунушталган колдонолорду алуу"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Жок, рахмат"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Жөндөөлөр"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"Үйрөткүч: <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"Бүттү!"</string>
<string name="allset_hint" msgid="2384632994739392447">"Башкы бетке өтүү үчүн экранды өйдө сүрүңүз"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"Телефонуңузду колдоно берсеңиз болот"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"Планшетиңизди колдоно берсеңиз болот"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Өтүү аракетинин системалык параметрлери"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Скриншот"</string>
<string name="action_split" msgid="2098009717623550676">"Бөлүү"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Экранды бөлүү үчүн башка колдонмону таптап коюңуз"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"Колдонмодо экран бөлүнбөйт."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"Бул аракетти аткарууга колдонмо же ишканаңыз тыюу салган"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Жаңсоолор үйрөткүчүн өткөрүп жибересизби?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Аны кийин <xliff:g id="NAME">%1$s</xliff:g> колдонмосунан табасыз"</string>
diff --git a/quickstep/res/values-land/dimens.xml b/quickstep/res/values-land/dimens.xml
index 905fbda..bc5d02a 100644
--- a/quickstep/res/values-land/dimens.xml
+++ b/quickstep/res/values-land/dimens.xml
@@ -80,4 +80,8 @@
<dimen name="taskbar_button_margin_6_5">219.6dp</dimen>
<dimen name="taskbar_button_margin_4_5">84dp</dimen>
<dimen name="taskbar_button_margin_4_4">79dp</dimen>
+ <dimen name="taskbar_contextual_button_margin">48dp</dimen>
+ <dimen name="taskbar_suw_frame">96dp</dimen>
+ <dimen name="taskbar_suw_insets">24dp</dimen>
+
</resources>
\ No newline at end of file
diff --git a/quickstep/res/values-lo/strings.xml b/quickstep/res/values-lo/strings.xml
index f3c3cbd..a1d8640 100644
--- a/quickstep/res/values-lo/strings.xml
+++ b/quickstep/res/values-lo/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"ຮັບການແນະນຳແອັບຢູ່ແຖວລາຍການທີ່ມັກຂອງໜ້າຈໍຫຼັກຂອງທ່ານ"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"ເຂົ້າເຖິງແອັບທີ່ທ່ານໃຊ້ຫຼາຍທີ່ສຸດໄດ້ຢ່າງງ່າຍດາຍທັນທີຈາກໜ້າຈໍຫຼັກ. ການແນະນຳຈະປ່ຽນແປງຕາມການນຳໃຊ້ປະຈຳຂອງທ່ານ. ແອັບຢູ່ແຖວລຸ່ມສຸດຈະຍ້າຍຂຶ້ນໄປໃສ່ໜ້າຈໍຫຼັກຂອງທ່ານ."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"ເຂົ້າເຖິງແອັບທີ່ທ່ານໃຊ້ຫຼາຍທີ່ສຸດໄດ້ຢ່າງງ່າຍດາຍທັນທີຈາກໜ້າຈໍຫຼັກ. ການແນະນຳຈະປ່ຽນແປງຕາມການນຳໃຊ້ປະຈຳຂອງທ່ານ. ຕອນນີ້ແອັບໃນລາຍການທີ່ມັກຈະຍ້າຍໄປໃສ່ໜ້າຈໍຫຼັກຂອງທ່ານ."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"ເຂົ້າເຖິງແອັບທີ່ທ່ານໃຊ້ຫຼາຍທີ່ສຸດໄດ້ຢ່າງງ່າຍດາຍທັນທີຈາກໜ້າຈໍຫຼັກ. ການແນະນຳຈະປ່ຽນແປງຕາມການນຳໃຊ້ປະຈຳຂອງທ່ານ. ແອັບຢູ່ແຖວລຸ່ມສຸດຈະຍ້າຍໄປໂຟນເດີໃໝ່."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"ຮັບການແນະນຳແອັບ"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"ບໍ່, ຂອບໃຈ"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"ການຕັ້ງຄ່າ"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"ການສອນການນຳໃຊ້ທີ <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"ຮຽບຮ້ອຍໝົດແລ້ວ!"</string>
<string name="allset_hint" msgid="2384632994739392447">"ປັດຂຶ້ນເພື່ອໄປຫາໜ້າຫຼັກ"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"ທ່ານພ້ອມເລີ່ມຕົ້ນໃຊ້ໂທລະສັບຂອງທ່ານແລ້ວ"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"ທ່ານພ້ອມເລີ່ມຕົ້ນໃຊ້ແທັບເລັດຂອງທ່ານແລ້ວ"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"ການຕັ້ງຄ່າການນຳທາງລະບົບ"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"ຮູບໜ້າຈໍ"</string>
<string name="action_split" msgid="2098009717623550676">"ແບ່ງ"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"ແຕະແອັບອື່ນເພື່ອໃຊ້ການແຍກໜ້າຈໍ"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"ແອັບບໍ່ຮອງຮັບການແບ່ງໜ້າຈໍ."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"ແອັບ ຫຼື ອົງການຂອງທ່ານບໍ່ອະນຸຍາດໃຫ້ໃຊ້ຄຳສັ່ງນີ້"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"ຂ້າມການສອນການນຳໃຊ້ການນຳທາງບໍ?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"ທ່ານສາມາດຊອກສ່ວນນີ້ພາຍຫຼັງໄດ້ໃນແອັບ <xliff:g id="NAME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-lt/strings.xml b/quickstep/res/values-lt/strings.xml
index 25a84a7..0ee9f0a 100644
--- a/quickstep/res/values-lt/strings.xml
+++ b/quickstep/res/values-lt/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Gaukite programų pasiūlymų pagrindinio ekrano eilutėje „Mėgstamiausios“"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Lengvai pasiekite dažniausiai naudojamas programas iškart pagrindiniame ekrane. Pasiūlymai keisis atsižvelgiant į tai, kaip jas naudojate. Apatinėje eilutėje esančios programos bus perkeltos į pagrindinį ekraną."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Lengvai pasiekite dažniausiai naudojamas programas iškart pagrindiniame ekrane. Pasiūlymai keisis atsižvelgiant į tai, kaip jas naudojate. Eilutėje „Mėgstamiausios“ rodomos programos bus perkeltos į pagrindinį ekraną."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Lengvai pasiekite dažniausiai naudojamas programas iškart pagrindiniame ekrane. Pasiūlymai keisis atsižvelgiant į tai, kaip jas naudojate. Apatinėje eilutėje esančios programos bus perkeltos į naują aplanką."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Gauti programų pasiūlymų"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Ne, ačiū"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Nustatymai"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"Mokymo programa: <xliff:g id="CURRENT">%1$d</xliff:g> iš <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"Paruošta!"</string>
<string name="allset_hint" msgid="2384632994739392447">"Perbraukite aukštyn, kad grįžtumėte į pagrindinį ekraną"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"Esate pasiruošę pradėti naudoti telefoną"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"Esate pasiruošę pradėti naudoti planšetinį kompiuterį"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Sistemos naršymo nustatymai"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Ekrano kopija"</string>
<string name="action_split" msgid="2098009717623550676">"Išskaidymo režimas"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Pal. kitą progr., kad gal. naud. išsk. ekr. rež."</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"Programoje nepalaikomas išskaidyto ekrano režimas."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"Jūsų organizacijoje arba naudojant šią programą neleidžiama atlikti šio veiksmo"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Praleisti naršymo mokymo programą?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Tai galėsite rasti vėliau programoje „<xliff:g id="NAME">%1$s</xliff:g>“"</string>
diff --git a/quickstep/res/values-lv/strings.xml b/quickstep/res/values-lv/strings.xml
index ee25cb5..27460f4 100644
--- a/quickstep/res/values-lv/strings.xml
+++ b/quickstep/res/values-lv/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Saņemiet lietotņu ieteikumus izlases rindā sākuma ekrānā"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Varat sākuma ekrānā ērti piekļūt savām visbiežāk izmantotajām lietotnēm. Ieteikumi mainīsies atkarībā no jūsu paradumiem. Apakšējā rindā esošās lietotnes tiks pārvietotas uz augšu — uz sākuma ekrānu."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Varat sākuma ekrānā ērti piekļūt savām visbiežāk izmantotajām lietotnēm. Ieteikumi mainīsies atkarībā no jūsu paradumiem. Lietotnes no izlases rindas tiks pārvietotas uz sākuma ekrānu."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Varat sākuma ekrānā ērti piekļūt savām visbiežāk izmantotajām lietotnēm. Ieteikumi mainīsies atkarībā no jūsu paradumiem. Apakšējā rindā esošās lietotnes tiks pārvietotas uz jaunu mapi."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Rādīt ieteicamās lietotnes"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Nē, paldies"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Iestatījumi"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"<xliff:g id="CURRENT">%1$d</xliff:g>. mācību darbība no <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"Gatavs!"</string>
<string name="allset_hint" msgid="2384632994739392447">"Velciet augšup, lai pārietu uz sākuma ekrānu."</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"Varat sākt izmantot savu tālruni"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"Varat sākt izmantot savu planšetdatoru"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Sistēmas navigācijas iestatījumi"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Veikt ekrānuzņēmumu"</string>
<string name="action_split" msgid="2098009717623550676">"Sadalīt"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Piesk. citai lietotnei, lai izm. ekrāna sadalīšanu"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"Lietotnē netiek atbalstīta ekrāna sadalīšana."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"Lietotne vai jūsu organizācija neatļauj veikt šo darbību."</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Vai izlaist navigācijas mācības?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Varēsiet to vēlāk atrast lietotnē <xliff:g id="NAME">%1$s</xliff:g>."</string>
diff --git a/quickstep/res/values-mk/strings.xml b/quickstep/res/values-mk/strings.xml
index 75f0933..38ec94b 100644
--- a/quickstep/res/values-mk/strings.xml
+++ b/quickstep/res/values-mk/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Добивајте предлози за апликации во редот со омилени на почетниот екран"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Лесно пристапувајте до најкористените апликации директно на почетниот екран. Предлозите ќе се менуваат според рутините. Апликациите од последниот ред ќе се поместуваат нагоре до почетниот екран."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Лесно пристапувајте до најкористените апликации на почетниот екран. Предлозите ќе се менуваат според рутините. Апликациите од редот со омилени ќе се преместат на почетниот екран."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Лесно пристапувајте до најкористените апликации директно на почетниот екран. Предлозите ќе се менуваат според рутините. Апликациите од последниот ред ќе се преместуваат во нова папка."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Добивајте предлози за апликации"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Не, фала"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Поставки"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"Упатство <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"Готово!"</string>
<string name="allset_hint" msgid="2384632994739392447">"Повлечете нагоре за да појдете на почетниот екран"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"Спремни сте да почнете да го користите телефонот"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"Спремни сте да почнете да го користите таблетот"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Поставки за системска навигација"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Слика од екранот"</string>
<string name="action_split" msgid="2098009717623550676">"Раздели"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Допрете друга апликација за да користите поделен екран"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"Апликацијата не поддржува поделен екран."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"Апликацијата или вашата организација не го дозволува дејствово"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Да се прескокне упатството за навигација?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Ова може да го најдете подоцна во апликацијата <xliff:g id="NAME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-ml/strings.xml b/quickstep/res/values-ml/strings.xml
index 9f00e91..bc7efb9 100644
--- a/quickstep/res/values-ml/strings.xml
+++ b/quickstep/res/values-ml/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"നിങ്ങളുടെ ഹോം സ്ക്രീനിന്റെ \'പ്രിയപ്പെട്ടവ\' വരിയിൽ ആപ്പ് നിർദ്ദേശങ്ങൾ നേടുക"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"നിങ്ങൾ ഏറ്റവും കൂടുതൽ ഉപയോഗിച്ച ആപ്പുകൾ ഹോം സ്ക്രീനിൽ നിന്ന് തന്നെ എളുപ്പത്തിൽ ആക്സസ് ചെയ്യൂ. നിങ്ങളുടെ ദിനചര്യകളുടെ അടിസ്ഥാനത്തിൽ നിർദ്ദേശങ്ങൾ മാറും. താഴത്തെ നിരയിലുള്ള ആപ്പുകൾ നിങ്ങളുടെ ഹോം സ്ക്രീനിലേക്ക് നീങ്ങും."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"നിങ്ങൾ ഏറ്റവും കൂടുതൽ ഉപയോഗിച്ച ആപ്പുകൾ ഹോം സ്ക്രീനിൽ നിന്ന് തന്നെ എളുപ്പത്തിൽ ആക്സസ് ചെയ്യൂ. നിങ്ങളുടെ ദിനചര്യകളുടെ അടിസ്ഥാനത്തിൽ നിർദ്ദേശങ്ങൾ മാറും. \'പ്രിയപ്പെട്ടവ\' വരിയിലുള്ള ആപ്പുകൾ നിങ്ങളുടെ ഹോം സ്ക്രീനിലേക്ക് നീങ്ങും."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"നിങ്ങൾ ഏറ്റവും കൂടുതൽ ഉപയോഗിച്ച ആപ്പുകൾ ഹോം സ്ക്രീനിൽ നിന്ന് തന്നെ എളുപ്പത്തിൽ ആക്സസ് ചെയ്യൂ. നിങ്ങളുടെ ദിനചര്യകളുടെ അടിസ്ഥാനത്തിൽ നിർദ്ദേശങ്ങൾ മാറും. താഴത്തെ നിരയിലുള്ള ആപ്പുകൾ പുതിയൊരു ഫോൾഡറിലേക്ക് നീങ്ങും."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"ആപ്പ് നിർദ്ദേശങ്ങൾ നേടുക"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"വേണ്ട"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"ക്രമീകരണം"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"ട്യൂട്ടോറിയൽ <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"എല്ലാം സജ്ജീകരിച്ചു!"</string>
<string name="allset_hint" msgid="2384632994739392447">"ഹോമിലേക്ക് പോകാൻ മുകളിലേക്ക് സ്വൈപ്പ് ചെയ്യുക"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"ഫോൺ ഉപയോഗിച്ച് തുടങ്ങാൻ നിങ്ങൾ തയ്യാറാണ്"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"ടാബ്ലെറ്റ് ഉപയോഗിച്ച് തുടങ്ങാൻ നിങ്ങൾ തയ്യാറാണ്"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"സിസ്റ്റം നാവിഗേഷൻ ക്രമീകരണം"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"സ്ക്രീൻഷോട്ട്"</string>
<string name="action_split" msgid="2098009717623550676">"വിഭജിക്കുക"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"സ്പ്ലിറ്റ് സ്ക്രീനിനായി മറ്റൊരു ആപ്പ് ടാപ്പുചെയ്യൂ"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"സ്പ്ലിറ്റ്-സ്ക്രീനിനെ ആപ്പ് പിന്തുണയ്ക്കുന്നില്ല."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"ഈ നടപടി എടുക്കുന്നത് ആപ്പോ നിങ്ങളുടെ സ്ഥാപനമോ അനുവദിക്കുന്നില്ല"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"നാവിഗേഷൻ ട്യൂട്ടോറിയൽ ഒഴിവാക്കണോ?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"<xliff:g id="NAME">%1$s</xliff:g> ആപ്പിൽ നിങ്ങൾക്ക് ഇത് പിന്നീട് കാണാനാകും"</string>
diff --git a/quickstep/res/values-mn/strings.xml b/quickstep/res/values-mn/strings.xml
index 59728a6..45e66e4 100644
--- a/quickstep/res/values-mn/strings.xml
+++ b/quickstep/res/values-mn/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Үндсэн нүүрний дуртай мөрнөөсөө санал болгож буй аппуудыг аваарай"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Хамгийн их ашигладаг аппууддаа Үндсэн нүүрнээс хялбархан хандаарай. Санал болгож буй аппуудыг таны хэвшлээс хамаарч өөрчилнө. Доод мөрд буй аппуудыг таны Үндсэн нүүр лүү дээш зөөнө."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Хамгийн их ашигладаг аппууддаа Үндсэн нүүрнээсээ хялбархан хандаарай. Санал болголтыг таны хэвшлээс хамааран өөрчилнө. Дуртай мөрөнд буй аппуудыг таны үндсэн нүүр лүү зөөнө."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Хамгийн их ашигладаг аппууддаа Үндсэн нүүрнээс хялбархан хандаарай. Санал болгож буй аппуудыг таны хэвшлээс хамаарч өөрчилнө. Доод мөрөнд буй аппуудыг шинэ фолдер луу зөөнө."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Санал болгож буй аппуудыг авах"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Үгүй, баярлалаа"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Тохиргоо"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"<xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g> практик хичээл"</string>
<string name="allset_title" msgid="5021126669778966707">"Тохируулж дууслаа!"</string>
<string name="allset_hint" msgid="2384632994739392447">"Нүүр хуудас руу очихын тулд дээш шударна уу"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"Та утсаа ашиглаж эхлэхэд бэлэн боллоо"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"Та таблетаа ашиглаж эхлэхэд бэлэн боллоо"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Системийн навигацын тохиргоо"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Дэлгэцийн агшин дарах"</string>
<string name="action_split" msgid="2098009717623550676">"Хуваах"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Дэлгэц хуваахыг ашиглах бол өөр аппыг товшино уу"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"Апп дэлгэцийг хуваах горимыг дэмждэггүй."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"Энэ үйлдлийг апп эсвэл танай байгууллага зөвшөөрдөггүй"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Навигацын практик хичээлийг алгасах уу?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Та үүнийг дараа нь <xliff:g id="NAME">%1$s</xliff:g> аппаас олох боломжтой"</string>
diff --git a/quickstep/res/values-mr/strings.xml b/quickstep/res/values-mr/strings.xml
index 5f2fb0c..f6f8c14 100644
--- a/quickstep/res/values-mr/strings.xml
+++ b/quickstep/res/values-mr/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"तुमच्या होम स्क्रीनच्या पसंतीच्या पंक्तीवर अॅप सूचना मिळवा"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"तुमची सर्वाधिक वापरली जाणारी अॅप्स होम स्क्रीनवरच सहजपणे अॅक्सेस करा. तुमच्या दिनक्रमानुसार तुम्हाला मिळणाऱ्या सूचना बदलतील. तळाशी असणारी अॅप्स तुमच्या होम स्क्रीनवर जातील."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"तुमची सर्वाधिक वापरली जाणारी अॅप्स होम स्क्रीनवर सहजपणे अॅक्सेस करा. सूचना तुमच्या दिनक्रमांनुसार बदलतील. पसंतीच्या पंक्तीमधील अॅप्स तुमच्या होम स्क्रीनवर हलवली जातील."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"तुमची सर्वाधिक वापरली जाणारी अॅप्स होम स्क्रीनवरच सहजपणे अॅक्सेस करा. सूचना तुमच्या दिनक्रमांच्या आधारावर बदलतील. तळाच्या पंक्तीवरील अॅप्स नवीन फोल्डरवर जातील."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"अॅप सूचना मिळवा"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"नाही, नको"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"सेटिंग्ज"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"ट्यूटोरियल <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"सर्व तयार आहे!"</string>
<string name="allset_hint" msgid="2384632994739392447">"होम वर जाण्यासाठी वरती स्वाइप करा"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"तुम्ही तुमचा फोन वापरण्यास सुरुवात करू शकता"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"तुम्ही तुमचा टॅबलेट वापरण्यास सुरुवात करू शकता"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"सिस्टीम नेव्हिगेशन सेटिंग्ज"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"स्क्रीनशॉट"</string>
<string name="action_split" msgid="2098009717623550676">"स्प्लिट"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"स्प्लिटस्क्रीन वापरण्यासाठी दुसऱ्या ॲपवर टॅप करा"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"अॅप हे स्प्लिट-स्क्रीनला सपोर्ट करत नाही."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"अॅप किंवा तुमच्या संस्थेद्वारे ही क्रिया करण्याची अनुमती नाही"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"नेव्हिगेशन ट्यूटोरियल वगळायचे आहे का?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"तुम्हाला हे नंतर <xliff:g id="NAME">%1$s</xliff:g> ॲपमध्ये मिळेल"</string>
diff --git a/quickstep/res/values-ms/strings.xml b/quickstep/res/values-ms/strings.xml
index 27dcc41..d173790 100644
--- a/quickstep/res/values-ms/strings.xml
+++ b/quickstep/res/values-ms/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Dapatkan cadangan apl di baris kegemaran Skrin utama anda"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Akses apl yang paling kerap anda gunakan dengan mudah pada Skrin utama. Cadangan akan berubah berdasarkan rutin anda. Apl di baris bawah akan beralih ke atas, ke Skrin utama anda."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Akses apl yang paling kerap anda gunakan dengan mudah pada Skrin utama. Cadangan akan berubah berdasarkan rutin anda. Apl di baris kegemaran akan beralih ke Skrin utama anda."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Akses apl yang paling kerap anda gunakan dengan mudah, terus pada Skrin utama. Cadangan akan berubah berdasarkan rutin anda. Apl di baris bawah akan beralih ke folder baharu."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Dapatkan cadangan apl"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Tidak perlu"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Tetapan"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"Tutorial <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"Siap!"</string>
<string name="allset_hint" msgid="2384632994739392447">"Leret ke atas untuk kembali ke Laman Utama"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"Anda sudah sedia untuk mula menggunakan telefon anda"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"Anda bersedia untuk mula menggunakan tablet anda"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Tetapan navigasi sistem"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Tangkapan skrin"</string>
<string name="action_split" msgid="2098009717623550676">"Pisah"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Ketik apl lain untuk menggunakan skrin pisah"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"Apl tidak menyokong skrin pisah."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"Tindakan ini tidak dibenarkan oleh apl atau organisasi anda"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Langkau tutorial navigasi?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Anda boleh mendapatkan tutorial ini kemudian dalam apl <xliff:g id="NAME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-my/strings.xml b/quickstep/res/values-my/strings.xml
index 825b6f2..20441d1 100644
--- a/quickstep/res/values-my/strings.xml
+++ b/quickstep/res/values-my/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"သင်၏ \'ပင်မစာမျက်နှာ\' ၏ အနှစ်သက်ဆုံးများအတန်းတွင် အက်ပ်အကြံပြုချက်များ ရယူခြင်း"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"အသုံးအများဆုံးအက်ပ်များကို \'ပင်မစာမျက်နှာ\' တွင် အလွယ်တကူ ဖွင့်နိုင်သည်။ သင်၏ ပုံမှန်လုပ်ဆောင်ချက်များပေါ် အခြေခံ၍ အကြံပြုချက်များ ပြောင်းလဲပါမည်။ အောက်ခြေအတန်းရှိ အက်ပ်များကို သင်၏ \'ပင်မစာမျက်နှာ\' သို့ရွှေ့လိုက်မည်။"</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"အသုံးအများဆုံးအက်ပ်များကို \'ပင်မစာမျက်နှာ\' တွင် အလွယ်တကူ သုံးနိုင်သည်။ သင်၏ ပုံမှန်အစီအစဉ်များပေါ် အခြေခံ၍ အကြံပြုချက်များ ပြောင်းလဲပါမည်။ အနှစ်သက်ဆုံးများအတန်းရှိ အက်ပ်များကို သင်၏ \'ပင်မစာမျက်နှာ\' သို့ရွှေ့လိုက်မည်။"</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"အသုံးအများဆုံးအက်ပ်များကို ပင်မစာမျက်နှာတွင် အလွယ်တကူ သုံးနိုင်သည်။ သင်၏ ပုံမှန်အစီအစဉ်များပေါ် အခြေခံ၍ အကြံပြုချက်များ ပြောင်းလဲပါမည်။ အောက်ခြေအတန်းရှိ အက်ပ်များကို ဖိုင်တွဲအသစ်သို့ ရွှေ့လိုက်မည်။"</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"အက်ပ်အကြံပြုချက်များ ရယူရန်"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"မလိုပါ"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"ဆက်တင်များ"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"ရှင်းလင်းပို့ချချက် <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"အားလုံး အဆင်သင့်ပါ။"</string>
<string name="allset_hint" msgid="2384632994739392447">"ပင်မစာမျက်နှာသို့သွားရန် အပေါ်သို့ ပွတ်ဆွဲပါ"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"သင့်ဖုန်း စသုံးရန် အသင့်ဖြစ်ပါပြီ"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"သင့်တက်ဘလက်ကို စသုံးရန် အသင့်ဖြစ်ပါပြီ"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"စနစ် လမ်းညွှန် ဆက်တင်များ"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"ဖန်သားပြင်ဓာတ်ပုံ"</string>
<string name="action_split" msgid="2098009717623550676">"ခွဲထုတ်ရန်"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"မျက်နှာပြင်ခွဲ၍ပြသရန် အက်ပ်နောက်တစ်ခုကို တို့ပါ"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"အက်ပ်တွင် မျက်နှာပြင် ခွဲ၍ပြသခြင်း သုံး၍မရပါ။"</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"ဤလုပ်ဆောင်ချက်ကို အက်ပ် သို့မဟုတ် သင်၏အဖွဲ့အစည်းက ခွင့်မပြုပါ"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"လမ်းညွှန်ခြင်း ရှင်းလင်းပို့ချချက်ကို ကျော်မလား။"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"၎င်းကို နောက်မှ <xliff:g id="NAME">%1$s</xliff:g> အက်ပ်တွင် ရှာနိုင်သည်"</string>
diff --git a/quickstep/res/values-nb/strings.xml b/quickstep/res/values-nb/strings.xml
index 250724e..4045c7c 100644
--- a/quickstep/res/values-nb/strings.xml
+++ b/quickstep/res/values-nb/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Få appforslag i favoritter-raden på startskjermen"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Få enkel tilgang til appene du bruker oftest, rett fra startskjermen. Forslagene endres basert på rutinene dine. Appene i den nederste raden flyttes opp til startskjermen."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Få enkel tilgang til appene du bruker oftest, rett fra startskjermen. Forslagene endres basert på rutinene dine. Apper i favoritter-raden blir flyttet til startskjermen."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Få enkel tilgang til appene du bruker oftest, rett fra startskjermen. Forslagene endres basert på rutinene dine. Appene i den nederste raden flyttes til en ny mappe."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Få appforslag"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Nei takk"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Innstillinger"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"Veiledning <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"Alt er klart!"</string>
<string name="allset_hint" msgid="2384632994739392447">"Sveip opp for å gå til startskjermen"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"Du er klar til å begynne å bruke telefonen"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"Du er klar til å begynne å bruke nettbrettet"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Innstillinger for systemnavigasjon"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Skjermdump"</string>
<string name="action_split" msgid="2098009717623550676">"Del opp"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Trykk på en annen app for å bruke delt skjerm"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"Appen støtter ikke delt skjerm."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"Appen eller organisasjonen din tillater ikke denne handlingen"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Vil du hoppe over navigeringsveiledningen?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Du kan finne dette i <xliff:g id="NAME">%1$s</xliff:g>-appen senere"</string>
diff --git a/quickstep/res/values-ne/strings.xml b/quickstep/res/values-ne/strings.xml
index f7214b0..fe081fe 100644
--- a/quickstep/res/values-ne/strings.xml
+++ b/quickstep/res/values-ne/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"आफ्नो होम स्क्रिनको मन पर्ने नामक पङ्क्तिमा एपसम्बन्धी सिफारिस प्राप्त गर्नुहोस्"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"आफूले सबैभन्दा बढी प्रयोग गर्ने एप होम स्क्रिनबाट सजिलै चलाउनुहोस्। सिफारिस गरिने एपहरूको क्रम तपाईंले एप प्रयोग गर्ने समयतालिकाअनुसार बदलिने छ। फेदको रोमा रहेका एपहरू तपाईंको होम स्क्रिनको सिरानमा सर्ने छन्।"</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"आफूले सबैभन्दा बढी प्रयोग गर्ने एपहरू गृह स्क्रिनबाटै सजिलैसँग खोल्नुहोस्। सिफारिस गरिने एपहरूको क्रम तपाईंको दिनचर्याअनुसार बदलिने छ। मन पर्ने नामक पङ्क्तिमा रहेका एपहरू सारेर होम स्क्रिनमा लगिने छन्।"</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"गृह स्क्रिनबाटै आफूले सबैभन्दा बढी प्रयोग गर्ने एप सजिलै चलाउनुहोस्। सिफारिस गरिने एपहरूको क्रम तपाईंले एप प्रयोग गर्ने समयतालिकाअनुसार बदलिने छ। फेदको पङ्क्तिमा रहेका एपहरू एउटा नयाँ फोल्डरमा सर्ने छन्।"</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"एपसम्बन्धी सिफारिस प्राप्त गर्नुहोस्"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"पर्दैन, धन्यवाद"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"सेटिङ"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"ट्युटोरियल <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"सबै तयार भयो!"</string>
<string name="allset_hint" msgid="2384632994739392447">"होममा जान माथितिर स्वाइप गर्नुहोस्"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"तपाईं आफ्नो फोन चलाउन थाल्न सक्नुहुन्छ"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"तपाईं अब आफ्नो ट्याब्लेट चलाउन थाल्न सक्नुहुन्छ"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"सिस्टम नेभिगेसनसम्बन्धी सेटिङ"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"स्क्रिनसट"</string>
<string name="action_split" msgid="2098009717623550676">"स्प्लिट गर्नुहोस्"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"स्प्लिटक्रिन प्रयोग गर्न अर्को एपमा ट्याप गर्नुहोस्"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"यो एपको स्क्रिन विभाजन गर्न मिल्दैन।"</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"यो एप वा तपाईंको सङ्गठनले यो कारबाही गर्ने अनुमति दिँदैन"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"नेभिगेसन ट्युटोरियल स्किप गर्ने हो?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"तपाईं पछि <xliff:g id="NAME">%1$s</xliff:g> नामक एपमा गई यो ट्युटोरियल भेट्टाउन सक्नुहुन्छ"</string>
diff --git a/quickstep/res/values-nl/strings.xml b/quickstep/res/values-nl/strings.xml
index ff5e45e..5439833 100644
--- a/quickstep/res/values-nl/strings.xml
+++ b/quickstep/res/values-nl/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"App-suggesties krijgen op de rij met favorieten op het startscherm"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Open je meestgebruikte apps makkelijk vanaf het startscherm. De suggesties veranderen op basis van je routines. Apps in de onderste rij worden naar je startscherm verplaatst."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Open je meestgebruikte apps makkelijk vanaf het startscherm. De suggesties veranderen op basis van je routines. Apps in de rij met favorieten worden naar het startscherm verplaatst."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Open je meestgebruikte apps makkelijk vanaf het startscherm. De suggesties veranderen op basis van je routines. Apps in de onderste rij worden naar een nieuwe map verplaatst."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"App-suggesties krijgen"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Nee, bedankt"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Instellingen"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"Tutorial <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"Klaar"</string>
<string name="allset_hint" msgid="2384632994739392447">"Swipe omhoog om naar het startscherm te gaan"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"Je bent klaar om je telefoon te gebruiken"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"Je bent klaar om je tablet te gebruiken"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Navigatie-instellingen van systeem"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string>
<string name="action_split" msgid="2098009717623550676">"Splitsen"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Tik op nog een app om je scherm te splitsen"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"App ondersteunt geen gesplitst scherm."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"Deze actie wordt niet toegestaan door de app of je organisatie"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Navigatietutorial overslaan?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Je vindt dit later terug in de app <xliff:g id="NAME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-or/strings.xml b/quickstep/res/values-or/strings.xml
index 6ab7c5e..aa998ba 100644
--- a/quickstep/res/values-or/strings.xml
+++ b/quickstep/res/values-or/strings.xml
@@ -31,11 +31,10 @@
<string name="time_left_for_app" msgid="3111996412933644358">"ଆଜି <xliff:g id="TIME">%1$s</xliff:g> ବାକି ଅଛି"</string>
<string name="title_app_suggestions" msgid="4185902664111965088">"ଆପ୍ ପରାମର୍ଶଗୁଡ଼ିକ"</string>
<string name="all_apps_prediction_tip" msgid="2672336544844936186">"ଆପଣ ପୂର୍ବାନୁମାନ କରିଥିବା ଆପ୍ସ"</string>
- <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"ଆପଣଙ୍କ ମୂଳ ସ୍କ୍ରିନର ତଳ ଧାଡ଼ିରେ ଆପ୍ ପରାମର୍ଶଗୁଡ଼ିକ ପାଆନ୍ତୁ"</string>
- <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"ଆପଣଙ୍କ ମୂଳ ସ୍କ୍ରିନର ପସନ୍ଦର ଧାଡ଼ିରେ ଆପ୍ ପରାମର୍ଶଗୁଡ଼ିକ ପାଆନ୍ତୁ"</string>
- <string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"ଆପଣଙ୍କର ସବୁଠାରୁ ଅଧିକ-ବ୍ୟବହୃତ ଆପଗୁଡ଼ିକୁ ସିଧା ମୂଳ ସ୍କ୍ରିନରେ ସହଜରେ ଆକ୍ସେସ୍ କରନ୍ତୁ। ଆପଣଙ୍କ ରୁଟିନଗୁଡ଼ିକ ଆଧାରରେ ପରାମର୍ଶଗୁଡ଼ିକ ପରିବର୍ତ୍ତିତ ହେବ। ତଳ ଧାଡ଼ିରେ ଥିବା ଆପଗୁଡ଼ିକ ଆପଣଙ୍କ ମୂଳ ସ୍କ୍ରିନକୁ ମୁଭ୍ କରିଯିବ।"</string>
- <string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"ମୂଳ ସ୍କ୍ରିନରେ ହିଁ ଆପଣଙ୍କର ସବୁଠାରୁ ଅଧିକ-ବ୍ୟବହୃତ ଆପଗୁଡ଼ିକୁ ସହଜରେ ଆକ୍ସେସ୍ କରନ୍ତୁ। ଆପଣଙ୍କ ରୁଟିନଗୁଡ଼ିକ ଆଧାରରେ ପରାମର୍ଶଗୁଡ଼ିକ ବଦଳିବ। ଆପଣଙ୍କ ମୂଳ ସ୍କ୍ରିନକୁ ପସନ୍ଦର ଧାଡ଼ିରେ ଥିବା ଆପଗୁଡ଼ିକ ମୁଭ୍ ହେବ।"</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"ଆପଣଙ୍କର ସବୁଠାରୁ ଅଧିକ-ବ୍ୟବହୃତ ଆପଗୁଡ଼ିକୁ, ସିଧା ମୂଳ ସ୍କ୍ରିନରେ ସହଜରେ ଆକ୍ସେସ୍ କରନ୍ତୁ। ଆପଣଙ୍କ ରୁଟିନଗୁଡ଼ିକ ଆଧାରରେ ପରାମର୍ଶଗୁଡ଼ିକ ପରିବର୍ତ୍ତିତ ହେବ। ତଳ ଧାଡ଼ିରେ ଥିବା ଆପଗୁଡ଼ିକ ଏକ ନୂଆ ଫୋଲ୍ଡରକୁ ମୁଭ୍ କରିଯିବ।"</string>
+ <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"ଆପଣଙ୍କ ହୋମ ସ୍କ୍ରିନର ତଳ ଧାଡ଼ିରେ ଆପ ପରାମର୍ଶଗୁଡ଼ିକ ପାଆନ୍ତୁ"</string>
+ <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"ଆପଣଙ୍କ ହୋମ ସ୍କ୍ରିନର ପସନ୍ଦର ଧାଡ଼ିରେ ଆପ ପରାମର୍ଶଗୁଡ଼ିକ ପାଆନ୍ତୁ"</string>
+ <string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"ଆପଣଙ୍କର ସବୁଠାରୁ ଅଧିକ-ବ୍ୟବହୃତ ଆପ୍ସକୁ ସିଧା ହୋମ ସ୍କ୍ରିନରେ ସହଜରେ ଆକ୍ସେସ କରନ୍ତୁ। ଆପଣଙ୍କ ରୁଟିନଗୁଡ଼ିକ ଆଧାରରେ ପରାମର୍ଶଗୁଡ଼ିକ ପରିବର୍ତ୍ତିତ ହେବ। ତଳ ଧାଡ଼ିରେ ଥିବା ଆପ୍ସ ଆପଣଙ୍କ ହୋମ ସ୍କ୍ରିନକୁ ମୁଭ ହୋଇଯିବ।"</string>
+ <string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"ହୋମ ସ୍କ୍ରିନରେ ହିଁ ଆପଣଙ୍କର ସବୁଠାରୁ ଅଧିକ-ବ୍ୟବହୃତ ଆପ୍ସକୁ ସହଜରେ ଆକ୍ସେସ କରନ୍ତୁ। ଆପଣଙ୍କ ରୁଟିନଗୁଡ଼ିକ ଆଧାରରେ ପରାମର୍ଶଗୁଡ଼ିକ ବଦଳିବ। ଆପଣଙ୍କ ହୋମ ସ୍କ୍ରିନକୁ ପସନ୍ଦର ଧାଡ଼ିରେ ଥିବା ଆପ୍ସ ମୁଭ ହୋଇଯିବ।"</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"ଆପ୍ ପରାମର୍ଶଗୁଡ଼ିକ ପାଆନ୍ତୁ"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"ନାହିଁ, ଥାଉ"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"ସେଟିଂସ"</string>
@@ -57,11 +56,11 @@
<string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"ଆପଣ ସ୍କ୍ରିନର ତଳ ଧାରରୁ ଉପରକୁ ସ୍ୱାଇପ୍ କରୁଥିବା ସୁନିଶ୍ଚିତ କରନ୍ତୁ।"</string>
<string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"ଆପଣ ଛାଡ଼ିବା ପୂର୍ବରୁ ବିରତ କରୁନଥିବା ସୁନିଶ୍ଚିତ କରନ୍ତୁ।"</string>
<string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"ଆପଣ ସିଧା ଉପରକୁ ସ୍ୱାଇପ୍ କରୁଥିବା ସୁନିଶ୍ଚିତ କରନ୍ତୁ।"</string>
- <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"ଆପଣ \'ମୂଳପୃଷ୍ଠାକୁ ଯାଆନ୍ତୁ\' ଜେଶ୍ଚର୍ ସମ୍ପୂର୍ଣ୍ଣ କରିଛନ୍ତି। ତା\'ପରେ, ପଛକୁ କିପରି ଫେରିବେ ତାହା ଜାଣନ୍ତୁ।"</string>
- <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"ଆପଣ \'ମୂଳପୃଷ୍ଠାକୁ ଯାଆନ୍ତୁ\' ଜେଶ୍ଚର୍ ସମ୍ପୂର୍ଣ୍ଣ କରିଛନ୍ତି।"</string>
- <string name="home_gesture_intro_title" msgid="836590312858441830">"ମୂଳପୃଷ୍ଠାକୁ ଯିବା ପାଇଁ ସ୍ୱାଇପ୍ କରନ୍ତୁ"</string>
- <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"ଆପଣଙ୍କ ସ୍କ୍ରିନର ତଳୁ ଉପରକୁ ସ୍ୱାଇପ୍ କରନ୍ତୁ। ଏହି ଜେଶ୍ଚର୍ ସର୍ବଦା ଆପଣଙ୍କୁ ମୂଳସ୍କ୍ରିନକୁ ନେଇଥାଏ।"</string>
- <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"2ଟି ଆଙ୍ଗୁଠିରେ ସ୍କ୍ରିନର ତଳୁ ଉପରକୁ ସ୍ୱାଇପ କରନ୍ତୁ। ଏହି ଜେଶ୍ଚର ସର୍ବଦା ଆପଣଙ୍କୁ ମୂଳସ୍କ୍ରିନକୁ ନେଇଥାଏ।"</string>
+ <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"ଆପଣ ହୋମ ଜେଶ୍ଚର ସମ୍ପୂର୍ଣ୍ଣ କରିଛନ୍ତି। ତା\'ପରେ, ପଛକୁ କିପରି ଫେରିବେ ତାହା ଜାଣନ୍ତୁ।"</string>
+ <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"ଆପଣ ହୋମ ଜେଶ୍ଚର ସମ୍ପୂର୍ଣ୍ଣ କରିଛନ୍ତି।"</string>
+ <string name="home_gesture_intro_title" msgid="836590312858441830">"ହୋମକୁ ଯିବା ପାଇଁ ସ୍ୱାଇପ କରନ୍ତୁ"</string>
+ <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"ଆପଣଙ୍କ ସ୍କ୍ରିନର ତଳୁ ଉପରକୁ ସ୍ୱାଇପ କରନ୍ତୁ। ଏହି ଜେଶ୍ଚର ସର୍ବଦା ଆପଣଙ୍କୁ ହୋମ ସ୍କ୍ରିନକୁ ନେଇଥାଏ।"</string>
+ <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"2ଟି ଆଙ୍ଗୁଠିରେ ସ୍କ୍ରିନର ତଳୁ ଉପରକୁ ସ୍ୱାଇପ କରନ୍ତୁ। ଏହି ଜେଶ୍ଚର ସର୍ବଦା ଆପଣଙ୍କୁ ହୋମ ସ୍କ୍ରିନକୁ ନେଇଥାଏ।"</string>
<string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"ଆପଣ ସ୍କ୍ରିନର ତଳ ଧାରରୁ ଉପରକୁ ସ୍ୱାଇପ୍ କରୁଥିବା ସୁନିଶ୍ଚିତ କରନ୍ତୁ।"</string>
<string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"ୱିଣ୍ଡୋକୁ ରିଲିଜ୍ କରିବା ପୂର୍ବରୁ ଅଧିକ ସମୟ ଧରି ରଖିବାକୁ ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
<string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"ଆପଣ ସିଧା ଉପରକୁ ସ୍ୱାଇପ୍ କରି ତା\'ପରେ ବିରତ କରୁଥିବା ସୁନିଶ୍ଚିତ କରନ୍ତୁ।"</string>
@@ -77,7 +76,9 @@
<string name="gesture_tutorial_nice" msgid="2936275692616928280">"ବଢ଼ିଆ!"</string>
<string name="gesture_tutorial_step" msgid="1279786122817620968">"ଟ୍ୟୁଟୋରିଆଲ୍ <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"ସମ୍ପୂର୍ଣ୍ଣ ଭାବେ ପ୍ରସ୍ତୁତ!"</string>
- <string name="allset_hint" msgid="2384632994739392447">"ମୂଳପୃଷ୍ଠାକୁ ଯିବା ପାଇଁ ଉପରକୁ ସ୍ୱାଇପ୍ କରନ୍ତୁ"</string>
+ <string name="allset_hint" msgid="2384632994739392447">"ହୋମକୁ ଯିବା ପାଇଁ ଉପରକୁ ସ୍ୱାଇପ କରନ୍ତୁ"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"ଆପଣ ଆପଣଙ୍କ ଫୋନ୍ ବ୍ୟବହାର କରିବା ପାଇଁ ପ୍ରସ୍ତୁତ ଅଛନ୍ତି"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"ଆପଣ ଆପଣଙ୍କ ଟାବଲେଟ ବ୍ୟବହାର କରିବା ଆରମ୍ଭ କରିବାକୁ ପ୍ରସ୍ତୁତ ଅଛନ୍ତି"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"ସିଷ୍ଟମ ନାଭିଗେସନ ସେଟିଂସ"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"ସ୍କ୍ରିନସଟ୍"</string>
<string name="action_split" msgid="2098009717623550676">"ସ୍ପ୍ଲିଟ୍"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"ସ୍ପ୍ଲିଟସ୍କ୍ରିନ ବ୍ୟବହାର କରିବାକୁ ଅନ୍ୟ ଏକ ଆପରେ ଟାପ କର"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"ସ୍ପ୍ଲିଟ-ସ୍କ୍ରିନକୁ ଆପ ସମର୍ଥନ କରେ ନାହିଁ।"</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"ଆପ୍ କିମ୍ବା ଆପଣଙ୍କ ସଂସ୍ଥା ଦ୍ୱାରା ଏହି କାର୍ଯ୍ୟକୁ ଅନୁମତି ଦିଆଯାଇ ନାହିଁ"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"ନାଭିଗେସନ୍ ଟ୍ୟୁଟୋରିଆଲକୁ ବାଦ୍ ଦେବେ?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"ଆପଣ ପରେ ଏହାକୁ <xliff:g id="NAME">%1$s</xliff:g> ଆପରେ ପାଇପାରିବେ"</string>
@@ -101,7 +103,7 @@
<string name="taskbar_edu_previous" msgid="459202320127201702">"ପଛକୁ ଫେରନ୍ତୁ"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"ବନ୍ଦ କରନ୍ତୁ"</string>
<string name="taskbar_edu_done" msgid="6880178093977704569">"ହୋଇଗଲା"</string>
- <string name="taskbar_button_home" msgid="2151398979630664652">"ମୂଳପୃଷ୍ଠା"</string>
+ <string name="taskbar_button_home" msgid="2151398979630664652">"ହୋମ"</string>
<string name="taskbar_button_a11y" msgid="5241161324875094465">"ଆକ୍ସେସିବିଲିଟୀ"</string>
<string name="taskbar_button_back" msgid="8558862226461164514">"ପଛକୁ ଫେରନ୍ତୁ"</string>
<string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"IME ସ୍ୱିଚର"</string>
diff --git a/quickstep/res/values-pa/strings.xml b/quickstep/res/values-pa/strings.xml
index 83def85..4bcf4d6 100644
--- a/quickstep/res/values-pa/strings.xml
+++ b/quickstep/res/values-pa/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"ਆਪਣੀ ਹੋਮ ਸਕ੍ਰੀਨ ਦੀ ਮਨਪਸੰਦ ਕਤਾਰ \'ਤੇ ਐਪ ਸੁਝਾਅ ਹਾਸਲ ਕਰੋ"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"ਹੋਮ ਸਕ੍ਰੀਨ \'ਤੇ ਆਪਣੀਆਂ ਸਭ ਤੋਂ ਵੱਧ ਵਰਤੀਆਂ ਗਈਆਂ ਐਪਾਂ ਤੱਕ ਆਸਾਨੀ ਨਾਲ ਪਹੁੰਚ ਕਰੋ। ਸੁਝਾਅ ਤੁਹਾਡੇ ਨਿਯਮਬੱਧ ਕੰਮਾਂ ਦੇ ਆਧਾਰ \'ਤੇ ਬਦਲਣਗੇ। ਹੇਠਲੀ ਕਤਾਰ ਵਾਲੀਆਂ ਐਪਾਂ ਤੁਹਾਡੀ ਹੋਮ ਸਕ੍ਰੀਨ ਵੱਲ ਉੱਪਰ ਆ ਜਾਣਗੀਆਂ।"</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"ਹੋਮ ਸਕ੍ਰੀਨ \'ਤੇ ਆਪਣੀਆਂ ਸਭ ਤੋਂ ਵੱਧ ਵਰਤੀਆਂ ਗਈਆਂ ਐਪਾਂ ਤੱਕ ਆਸਾਨੀ ਨਾਲ ਪਹੁੰਚ ਕਰੋ। ਸੁਝਾਅ ਤੁਹਾਡੇ ਨਿਯਮਬੱਧ ਕੰਮਾਂ ਦੇ ਆਧਾਰ \'ਤੇ ਬਦਲਣਗੇ। ਮਨਪਸੰਦ ਕਤਾਰ ਵਿਚਲੀਆਂ ਐਪਾਂ ਤੁਹਾਡੀ ਹੋਮ ਸਕ੍ਰੀਨ ਉੱਪਰ ਆ ਜਾਣਗੀਆਂ।"</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"ਹੋਮ ਸਕ੍ਰੀਨ \'ਤੇ ਆਪਣੀਆਂ ਸਭ ਤੋਂ ਵੱਧ ਵਰਤੀਆਂ ਗਈਆਂ ਐਪਾਂ ਤੱਕ ਆਸਾਨੀ ਨਾਲ ਪਹੁੰਚ ਕਰੋ। ਸੁਝਾਅ ਤੁਹਾਡੇ ਨਿਯਮਬੱਧ ਕੰਮਾਂ ਦੇ ਆਧਾਰ \'ਤੇ ਬਦਲਣਗੇ। ਹੇਠਲੀ ਕਤਾਰ ਵਾਲੀਆਂ ਐਪਾਂ ਇੱਕ ਨਵੇਂ ਫੋਲਡਰ ਵਿੱਚ ਚਲੀਆਂ ਜਾਣਗੀਆਂ।"</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"ਐਪ ਸੁਝਾਅ ਪ੍ਰਾਪਤ ਕਰੋ"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"ਨਹੀਂ ਧੰਨਵਾਦ"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"ਸੈਟਿੰਗਾਂ"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"ਟਿਊਟੋਰੀਅਲ <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"ਪੂਰੀ ਤਰ੍ਹਾਂ ਤਿਆਰ!"</string>
<string name="allset_hint" msgid="2384632994739392447">"ਹੋਮ \'ਤੇ ਜਾਣ ਲਈ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰੋ"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"ਤੁਸੀਂ ਆਪਣਾ ਫ਼ੋਨ ਵਰਤਣ ਲਈ ਤਿਆਰ ਹੋ"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"ਤੁਸੀਂ ਆਪਣਾ ਟੈਬਲੈੱਟ ਵਰਤਣ ਲਈ ਤਿਆਰ ਹੋ"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"ਸਿਸਟਮ ਨੈਵੀਗੇਸ਼ਨ ਸੈਟਿੰਗਾਂ"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"ਸਕ੍ਰੀਨਸ਼ਾਟ"</string>
<string name="action_split" msgid="2098009717623550676">"ਸਪਲਿਟ"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਵਰਤਣ ਲਈ ਕਿਸੇ ਹੋਰ ਐਪ \'ਤੇ ਟੈਪ ਕਰੋ"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"ਐਪ ਸਪਲਿਟ-ਸਕ੍ਰੀਨ ਦਾ ਸਮਰਥਨ ਨਹੀਂ ਕਰਦੀ।"</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"ਐਪ ਜਾਂ ਤੁਹਾਡੀ ਸੰਸਥਾ ਵੱਲੋਂ ਇਸ ਕਾਰਵਾਈ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਹੈ"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"ਕੀ ਨੈਵੀਗੇਸ਼ਨ ਟਿਊਟੋਰੀਅਲ ਨੂੰ ਛੱਡਣਾ ਹੈ?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"ਤੁਸੀਂ ਇਸਨੂੰ ਬਾਅਦ ਵਿੱਚ <xliff:g id="NAME">%1$s</xliff:g> ਐਪ ਵਿੱਚ ਲੱਭ ਸਕਦੇ ਹੋ"</string>
diff --git a/quickstep/res/values-pl/strings.xml b/quickstep/res/values-pl/strings.xml
index 077982a..0394bb7 100644
--- a/quickstep/res/values-pl/strings.xml
+++ b/quickstep/res/values-pl/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Otrzymuj sugestie aplikacji w wierszu ulubionych na ekranie głównym"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Łatwo uruchamiaj najczęściej używane aplikacje z ekranu głównego. Sugestie będą zmieniać się w zależności od Twoich nawyków. Aplikacje z dolnych wierszy będą przesuwane w górę, do ekranu głównego."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Zyskaj łatwy dostęp do najczęściej używanych aplikacji na ekranie głównym. Sugestie będą się zmieniać na podstawie Twojej rutyny. Aplikacje z wiersza ulubionych zostaną przeniesione na ekran główny."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Korzystaj z najczęściej używanych aplikacji na ekranie głównym w łatwy sposób. Sugestie będą się zmieniać na podstawie Twojej rutyny. Aplikacje z dolnych wierszy będą się przenosić do nowego folderu."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Otrzymuj sugestie aplikacji"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Nie, dziękuję"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Ustawienia"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"Samouczek <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"Wszystko gotowe"</string>
<string name="allset_hint" msgid="2384632994739392447">"Aby przejść na ekran główny, przesuń palcem w górę"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"Teraz możesz zacząć używać telefonu"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"Teraz możesz zacząć używać tabletu"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Ustawienia nawigacji w systemie"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Zrzut ekranu"</string>
<string name="action_split" msgid="2098009717623550676">"Podziel"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Kliknij drugą aplikację, aby podzielić ekran"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"Aplikacja nie obsługuje podzielonego ekranu."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"Nie możesz wykonać tego działania, bo nie zezwala na to aplikacja lub Twoja organizacja"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Pominąć samouczek nawigacji?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Znajdziesz to później w aplikacji <xliff:g id="NAME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-pt-rPT/strings.xml b/quickstep/res/values-pt-rPT/strings.xml
index 124b33b..47bc834 100644
--- a/quickstep/res/values-pt-rPT/strings.xml
+++ b/quickstep/res/values-pt-rPT/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Receba sugestões de apps na fila dos favoritos do ecrã principal"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Aceda facilmente às suas apps mais utilizadas, diretamente no ecrã principal. As sugestões mudam em função das suas rotinas. As apps na última fila passam para o ecrã principal."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Aceda facilmente às suas apps mais utilizadas no ecrã principal. As sugestões mudam em função das suas rotinas. As apps na fila dos favoritos passam para o ecrã principal."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Aceda facilmente às suas apps mais utilizadas, diretamente no ecrã principal. As sugestões mudam em função das suas rotinas. As apps na última fila passam para uma nova pasta."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Obter sugestões de apps"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Não, obrigado"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Definições"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"Tutorial <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"Tudo pronto!"</string>
<string name="allset_hint" msgid="2384632994739392447">"Deslize rapidamente para cima para aceder ao ecrã principal"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"Já pode começar a utilizar o seu telemóvel"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"Já pode começar a usar o seu tablet"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Definições de navegação do sistema"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Fazer captura de ecrã"</string>
<string name="action_split" msgid="2098009717623550676">"Dividir"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Toque noutra app para utilizar o ecrã dividido"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"A app não é compatível com o ecrã dividido."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"Esta ação não é permitida pela app ou a sua entidade."</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Ignorar o tutorial de navegação?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Pode encontrar isto mais tarde na app <xliff:g id="NAME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-pt/strings.xml b/quickstep/res/values-pt/strings.xml
index 199c2f4..6a36608 100644
--- a/quickstep/res/values-pt/strings.xml
+++ b/quickstep/res/values-pt/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Receba sugestões de apps na linha \"Favoritos\" da tela inicial"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Acesse diretamente na tela inicial os apps que você mais usa. As sugestões mudam de acordo com sua rotina, e os apps na linha inferior são movidos para a tela inicial."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Acesse os apps mais usados na tela inicial de forma simples. As sugestões mudam de acordo com sua rotina, e os apps na linha \"Favoritos\" são movidos para a tela inicial."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Acesse diretamente na tela inicial os apps que você mais usa. As sugestões mudam de acordo com sua rotina, e os apps na linha inferior são movidos para uma nova pasta."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Receber sugestões de apps"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Não"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Configurações"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"Tutorial <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"Tudo pronto!"</string>
<string name="allset_hint" msgid="2384632994739392447">"Deslize para cima para acessar a tela inicial"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"Você já pode começar a usar seu smartphone"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"Você já pode começar a usar seu tablet"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Configurações de navegação do sistema"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Capturar tela"</string>
<string name="action_split" msgid="2098009717623550676">"Dividir"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Toque em outro app para dividir a tela"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"O app não tem suporte para a divisão de tela."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"Essa ação não é permitida pelo app ou pela organização"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Pular o tutorial de navegação?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Veja o tutorial mais tarde no app <xliff:g id="NAME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-ro/strings.xml b/quickstep/res/values-ro/strings.xml
index ba4badf..9856a6c 100644
--- a/quickstep/res/values-ro/strings.xml
+++ b/quickstep/res/values-ro/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Primește sugestii de aplicații în rândul de preferințe al ecranului de pornire"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Accesează cu ușurință cele mai folosite aplicații direct din ecranul de pornire. Sugestiile se vor modifica în funcție de rutine. Aplicațiile din rândul de jos se vor muta în sus pe ecranul de pornire."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Accesează cu ușurință cele mai folosite aplicații direct din ecranul de pornire. Sugestiile se vor schimba în funcție de rutina ta. Aplicațiile din rândul de preferințe se vor muta în ecranul de pornire."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Accesează cu ușurință cele mai folosite aplicații, direct din ecranul de pornire. Sugestiile se vor modifica în funcție de rutine. Aplicațiile din rândul de jos se vor muta într-un dosar nou."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Primește sugestii de aplicații"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Nu, mulțumesc"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Setări"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"Tutorialul <xliff:g id="CURRENT">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"Gata!"</string>
<string name="allset_hint" msgid="2384632994739392447">"Glisează în sus pentru a accesa ecranul de pornire"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"Ești gata să folosești telefonul"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"Ești gata să folosești tableta"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Setările de navigare ale sistemului"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Captură de ecran"</string>
<string name="action_split" msgid="2098009717623550676">"Împărțit"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Atinge altă aplicație pentru ecranul împărțit"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"Aplicația nu acceptă ecranul împărțit."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"Această acțiune nu este permisă de aplicație sau de organizația ta"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Omiți tutorialul de navigare?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Îl poți găsi mai târziu în aplicația <xliff:g id="NAME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-ru/strings.xml b/quickstep/res/values-ru/strings.xml
index 44b72cf..de3343e 100644
--- a/quickstep/res/values-ru/strings.xml
+++ b/quickstep/res/values-ru/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Рекомендуемые приложения будут появляться в разделе избранных на главном экране"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Приложения, которыми вы часто пользуетесь, будут доступны прямо на главном экране. Их список может меняться с учетом ваших предпочтений. Приложения из нижнего ряда будут перемещены выше на главном экране."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Включите функцию для быстрого доступа к часто используемым приложениям на главном экране. Список меняется с учетом ваших действий. Приложения из раздела избранных будут перемещены на главный экран."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Приложения, которыми вы часто пользуетесь, будут доступны прямо на главном экране. Их список может меняться с учетом ваших предпочтений. Приложения из нижнего ряда будут перемещены в новую папку."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Показывать рекомендации"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Отмена"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Настройки"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"Руководство (шаг <xliff:g id="CURRENT">%1$d</xliff:g> из <xliff:g id="TOTAL">%2$d</xliff:g>)"</string>
<string name="allset_title" msgid="5021126669778966707">"Готово!"</string>
<string name="allset_hint" msgid="2384632994739392447">"Чтобы перейти на главный экран, проведите вверх."</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"Теперь вы можете использовать телефон."</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"Теперь вы можете использовать планшет."</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Системные настройки навигации"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Скриншот"</string>
<string name="action_split" msgid="2098009717623550676">"Разделить"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Для разделения экрана нажмите на другое приложение."</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"Приложение не поддерживает разделение экрана."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"Это действие заблокировано приложением или организацией."</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Пропустить руководство по жестам?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Его можно найти в приложении \"<xliff:g id="NAME">%1$s</xliff:g>\"."</string>
diff --git a/quickstep/res/values-si/strings.xml b/quickstep/res/values-si/strings.xml
index 31749cc..509490c 100644
--- a/quickstep/res/values-si/strings.xml
+++ b/quickstep/res/values-si/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"ඔබේ මුල් තිරයේ ප්රියතම පේළියේ යෙදුම් යෝජනා ලබා ගන්න"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"ඔබගේ වැඩිපුරම භාවිත කරන යෙදුම්වලට මුල් තිරයේ සිටම පහසුවෙන් ප්රවේශ වන්න. ඔබගේ දිනචරියා මත පදනම්ව යෝජනා වෙනස් වනු ඇත. පහළ පේළියේ යෙදුම් ඔබගේ මුල් තිරය දක්වා ගෙන යනු ඇත."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"ඔබගේ වැඩිපුරම භාවිත කරන යෙදුම්වලට මුල් තිරයේ සිටම පහසුවෙන් ප්රවේශ වන්න. ඔබගේ දිනචරියා මත පදනම්ව යෝජනා වෙනස් වනු ඇත. ප්රියතම තුළ යෙදුම් ඔබේ මුල් තිරය වෙත ගෙන යනු ඇත."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"ඔබගේ වැඩිපුරම භාවිත කරන යෙදුම්වලට මුල් තිරයේ සිටම පහසුවෙන් ප්රවේශ වන්න. ඔබගේ දිනචරියා මත පදනම්ව යෝජනා වෙනස් වනු ඇත. පහළ පේළියේ යෙදුම් නව ෆෝල්ඩරයකට ගෙන යනු ඇත."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"යෙදුම් යෝජනා ලබා ගන්න"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"එපා ස්තුතියි"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"සැකසීම්"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"නිබන්ධනය <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"සියල්ල සූදානම්!"</string>
<string name="allset_hint" msgid="2384632994739392447">"මුල් පිටුවට යාමට ඉහළට ස්වයිප් කරන්න"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"ඔබ ඔබගේ දුරකථනය භාවිත කිරීම පටන් ගැනීමට සූදානම්"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"ඔබ ඔබගේ ටැබ්ලටය භාවිත කිරීම පටන් ගැනීමට සූදානම්"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"පද්ධති සංචාලන සැකසීම්"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"තිර රුව"</string>
<string name="action_split" msgid="2098009717623550676">"බෙදන්න"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"බෙදුම් තිරය භාවිත කිරීමට තවත් යෙදුමක් තට්ටු කරන්න"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"යෙදුම බෙදුම් තිරය සඳහා සහාය නොදක්වයි."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"මෙම ක්රියාව යෙදුම හෝ ඔබේ සංවිධානය මගින් ඉඩ නොදේ"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"නිබන්ධනය සංචාලනය මඟ හරින්නද?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"ඔබට මෙය පසුව <xliff:g id="NAME">%1$s</xliff:g> යෙදුම තුළ සොයා ගත හැකිය"</string>
diff --git a/quickstep/res/values-sk/strings.xml b/quickstep/res/values-sk/strings.xml
index 5d51d45..f9372de 100644
--- a/quickstep/res/values-sk/strings.xml
+++ b/quickstep/res/values-sk/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Nechajte si na ploche na riadku obľúbených zobrazovať návrhy aplikácií"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Získajte jednoduchý prístup k najpoužívanejším aplikáciám priamo na ploche. Návrhy sa budú meniť podľa vašich zvyklostí. Aplikácie v spodnom riadku sa presunú nahor na plochu."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Získajte jednoduchý prístup k najpoužívanejším aplikáciám priamo na ploche. Návrhy sa budú meniť podľa vašich postupov. Aplikácie v riadku s obľúbenými sa presunú na plochu."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Získajte jednoduchý prístup k najpoužívanejším aplikáciám priamo na ploche. Návrhy sa budú meniť podľa vašich zvyklostí. Aplikácie v spodnom riadku sa presunú do nového priečinka."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Zobrazovať návrhy aplikácií"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Nie, ďakujem"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Nastavenia"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"Návod <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"Hotovo"</string>
<string name="allset_hint" msgid="2384632994739392447">"Potiahnutím nahor prejdete na plochu"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"Telefón môžete začať používať"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"Tablet môžete začať používať"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Nastavenia navigácie systémom"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Snímka obrazovky"</string>
<string name="action_split" msgid="2098009717623550676">"Rozdeliť"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Rozdel. obrazovku spustíte klepnutím na inú aplik."</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"Aplikácia nepodporuje rozdelenú obrazovku."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"Aplikácia alebo vaša organizácia túto akciu nepovoľuje"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Chcete preskočiť návod na navigáciu?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Tento návod nájdete v aplikácii <xliff:g id="NAME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-sl/strings.xml b/quickstep/res/values-sl/strings.xml
index e196e5d..0a5a271 100644
--- a/quickstep/res/values-sl/strings.xml
+++ b/quickstep/res/values-sl/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Prejemajte predloge aplikacij v vrstici s priljubljenimi na začetnem zaslonu"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Preprosto dostopajte do najpogosteje uporabljenih aplikacij kar na začetnem zaslonu. Predlogi se spreminjajo na podlagi dejanj, ki jih pogosto izvajate. Aplikacije iz spodnje vrstice se premaknejo na začetni zaslon."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Preprosto dostopajte do najpogosteje uporabljenih aplikacij kar na začetnem zaslonu. Predlogi se spreminjajo na podlagi dejanj, ki jih pogosto izvajate. Aplikacije v vrstici s priljubljenimi bodo premaknjene na začetni zaslon."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Preprosto dostopajte do najpogosteje uporabljenih aplikacij kar na začetnem zaslonu. Predlogi se spreminjajo na podlagi dejanj, ki jih pogosto izvajate. Aplikacije iz spodnje vrstice se premaknejo v novo mapo."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Prikaži predlagane aplikacije"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Ne, hvala"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Nastavitve"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"Vadnica <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"Končano"</string>
<string name="allset_hint" msgid="2384632994739392447">"Povlecite navzgor za začetni zaslon"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"Pripravljeni ste, da začnete uporabljati telefon"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"Pripravljeni ste, da začnete uporabljati tablični računalnik."</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Nastavitve krmarjenja po sistemu"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Posnetek zaslona"</string>
<string name="action_split" msgid="2098009717623550676">"Razdeli"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Za uporabo razdeljenega zaslona se dotaknite še ene aplikacije."</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"Aplikacija ne podpira načina razdeljenega zaslona."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"Aplikacija ali vaša organizacija ne dovoljuje tega dejanja"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Želite preskočiti vadnico za krmarjenje?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"To lahko pozneje najdete v aplikaciji <xliff:g id="NAME">%1$s</xliff:g>."</string>
diff --git a/quickstep/res/values-sq/strings.xml b/quickstep/res/values-sq/strings.xml
index 39771cb..f326693 100644
--- a/quickstep/res/values-sq/strings.xml
+++ b/quickstep/res/values-sq/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Merr aplikacione të sugjeruara në rreshtin e të preferuarave të ekranit tënd bazë"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Qasu me lehtësi në aplikacionet më të përdorura direkt në ekranin bazë. Sugjerimet do të ndryshojnë bazuar në rutinat e tua. Aplikacionet në rreshtin e poshtëm do të zhvendosen lart në ekranin tënd bazë."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Qasu me lehtësi në aplikacionet më të përdorura direkt në ekranin bazë. Sugjerimet do të ndryshojnë bazuar në rutinat e tua. Aplikacionet në rreshtin e të preferuarave do të zhvendosen në ekranin tënd bazë."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Qasu me lehtësi në aplikacionet më të përdorura, direkt në ekranin bazë. Sugjerimet do të ndryshojnë bazuar në rutinat e tua. Aplikacionet në rreshtin e poshtëm do të zhvendosen në një dosje tjetër."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Merr aplikacione të sugjeruara"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Jo, faleminderit"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Cilësimet"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"Udhëzuesi <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"Plotësisht gati!"</string>
<string name="allset_hint" msgid="2384632994739392447">"Rrëshqit shpejt lart për të shkuar tek \"Ekrani bazë\""</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"Je gati për të filluar përdorimin e telefonit tënd"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"Je gati që të fillosh të përdorësh tabletin"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Cilësimet e navigimit të sistemit"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Pamja e ekranit"</string>
<string name="action_split" msgid="2098009717623550676">"Ndaj"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Trokit aplikacion tjetër e përdor ekranin e ndarë"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"Aplikacioni nuk mbështet ekranin e ndarë."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"Ky veprim nuk lejohet nga aplikacioni ose organizata jote"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Të kapërcehet udhëzuesi i navigimit?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Këtë mund ta gjesh më vonë tek aplikacioni \"<xliff:g id="NAME">%1$s</xliff:g>\""</string>
diff --git a/quickstep/res/values-sr/strings.xml b/quickstep/res/values-sr/strings.xml
index 067a7fe..518023a 100644
--- a/quickstep/res/values-sr/strings.xml
+++ b/quickstep/res/values-sr/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Добијајте предлоге апликација у реду са омиљеним ставкама на почетном екрану"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Лако приступајте апликацијама које најчешће користите директно са почетног екрана. Предлози се мењају на основу употребе. Апликације из доњег реда се премештају нагоре на почетни екран."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Лако приступајте апликацијама које најчешће користите директно са почетног екрана. Предлози се мењају на основу ваших рутина. Апликације из реда са омиљеним ставкама се премештају на почетни екран."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Лако приступајте апликацијама које најчешће користите директно са почетног екрана. Предлози се мењају на основу употребе. Апликације из доњег реда се премештају у нов фолдер."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Приказуј предлоге апликација"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Не, хвала"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Подешавања"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"Водич <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"Готово!"</string>
<string name="allset_hint" msgid="2384632994739392447">"Превуците нагоре да бисте отворили почетни екран"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"Спремни сте да почнете да користите телефон"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"Спремни сте да почнете да користите таблет"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Подешавања кретања кроз систем"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Снимак екрана"</string>
<string name="action_split" msgid="2098009717623550676">"Подели"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Додирните другу апликацију за подељени екран"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"Апликација не подржава подељени екран."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"Апликација или организација не дозвољавају ову радњу"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Желите да прескочите водич за кретање?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Можете да пронађете ово касније у апликацији <xliff:g id="NAME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-sv/strings.xml b/quickstep/res/values-sv/strings.xml
index 3bb47c3..2d9db93 100644
--- a/quickstep/res/values-sv/strings.xml
+++ b/quickstep/res/values-sv/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Få appförslag på raden Favoriter på startskärmen"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Kom enkelt åt de appar du använder mest, direkt från startskärmen. Förslagen ändras efter dina rutiner. Appar på nedersta raden flyttas upp till startskärmen."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Kom enkelt åt de appar du använder mest, direkt från startskärmen. Förslagen ändras efter dina rutiner. Appar på raden Favoriter flyttas till startskärmen."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Kom enkelt åt de appar du använder mest, direkt från startskärmen. Förslagen ändras efter dina rutiner. Appar på nedersta raden flyttas till en ny mapp."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Få appförslag"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Nej tack"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Inställningar"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"Självstudie <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"Klart!"</string>
<string name="allset_hint" msgid="2384632994739392447">"Svep uppåt för att öppna startskärmen"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"Nu kan du börja använda telefonen"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"Nu kan du börja använda surfplattan"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Systemnavigeringsinställningar"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Skärmbild"</string>
<string name="action_split" msgid="2098009717623550676">"Delat"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Tryck på en annan app för att använda delad skärm"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"Appen har inte stöd för delad skärm."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"Appen eller organisationen tillåter inte den här åtgärden"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Vill du hoppa över självstudierna?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Du hittar det här igen i <xliff:g id="NAME">%1$s</xliff:g>-appen"</string>
diff --git a/quickstep/res/values-sw/strings.xml b/quickstep/res/values-sw/strings.xml
index 16891a8..aadf378 100644
--- a/quickstep/res/values-sw/strings.xml
+++ b/quickstep/res/values-sw/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Pata mapendekezo ya programu katika safu ya vipendwa ya Skrini yako ya kwanza"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Fikia kwa urahisi programu unazotumia sana moja kwa moja kwenye Skrini ya kwanza. Mapendekezo yatabadilika kulingana na ratiba zako. Programu zilizo kwenye sehemu ya chini zitahamishiwa kwenye Skrini yako ya kwanza."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Fikia kwa urahisi programu unazotumia sana moja kwa moja kwenye Skrini ya kwanza. Mapendekezo yatabadilika kulingana na utumiaji wako. Programu zilizo katika safu ya vipendwa zitahamishiwa kwenye Skrini yako ya kwanza."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Fikia kwa urahisi programu unazotumia zaidi, moja kwa moja kwenye Skrini ya kwanza. Mapendekezo yatabadilika kulingana na ratiba zako. Programu zilizo kwenye safu ya chini zitahamishiwa kwenye folda mpya."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Pata mapendekezo ya programu"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Hapana"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Mipangilio"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"Mafunzo ya <xliff:g id="CURRENT">%1$d</xliff:g> kati ya <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"Tayari!"</string>
<string name="allset_hint" msgid="2384632994739392447">"Telezesha kidole juu ili uende kwenye skrini ya kwanza"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"Uko tayari kuanza kutumia simu yako"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"Uko tayari kuanza kutumia kompyuta kibao yako"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Mipangilio ya usogezaji kwenye mfumo"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Picha ya skrini"</string>
<string name="action_split" msgid="2098009717623550676">"Iliyogawanywa"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Gusa programu nyingine ili utumie skrini iliyogawanywa"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"Programu haiwezi kutumia skrini iliyogawanywa."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"Kitendo hiki hakiruhusiwi na programu au shirika lako"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Ungependa kuruka mafunzo ya usogezaji?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Utapata mafunzo haya baadaye katika programu ya <xliff:g id="NAME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-sw600dp-land/dimens.xml b/quickstep/res/values-sw600dp-land/dimens.xml
index 0cd9b2b..dc10c24 100644
--- a/quickstep/res/values-sw600dp-land/dimens.xml
+++ b/quickstep/res/values-sw600dp-land/dimens.xml
@@ -17,4 +17,8 @@
<resources>
<!-- Overview actions -->
<dimen name="overview_actions_top_margin">12dp</dimen>
+
+ <!-- All Set page -->
+ <dimen name="allset_page_margin_horizontal">48dp</dimen>
+
</resources>
diff --git a/quickstep/res/values-sw600dp/dimens.xml b/quickstep/res/values-sw600dp/dimens.xml
index cfbbf8d..5899814 100644
--- a/quickstep/res/values-sw600dp/dimens.xml
+++ b/quickstep/res/values-sw600dp/dimens.xml
@@ -33,4 +33,10 @@
<dimen name="overview_page_spacing">36dp</dimen>
<!-- The space to the left and to the right of the "Clear all" button -->
<dimen name="overview_grid_side_margin">64dp</dimen>
+
+ <!-- All Set page -->
+ <dimen name="allset_page_margin_horizontal">120dp</dimen>
+ <dimen name="allset_page_allset_text_size">38sp</dimen>
+ <dimen name="allset_page_swipe_up_text_size">15sp</dimen>
+
</resources>
diff --git a/quickstep/res/values-sw720dp/dimens.xml b/quickstep/res/values-sw720dp/dimens.xml
index 284ce11..585f01e 100644
--- a/quickstep/res/values-sw720dp/dimens.xml
+++ b/quickstep/res/values-sw720dp/dimens.xml
@@ -33,4 +33,8 @@
<dimen name="overview_page_spacing">44dp</dimen>
<!-- The space to the left and to the right of the "Clear all" button -->
<dimen name="overview_grid_side_margin">64dp</dimen>
+
+ <!-- All Set page-->
+ <dimen name="allset_page_allset_text_size">42sp</dimen>
+ <dimen name="allset_page_swipe_up_text_size">16sp</dimen>
</resources>
diff --git a/quickstep/res/values-ta/strings.xml b/quickstep/res/values-ta/strings.xml
index 5c73734..77c3c37 100644
--- a/quickstep/res/values-ta/strings.xml
+++ b/quickstep/res/values-ta/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"உங்கள் முகப்புத் திரையின் \'பிடித்தவை\' வரிசையில் ஆப்ஸ் பரிந்துரைகளைப் பெறலாம்"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"அதிகமாகப் பயன்படுத்திய ஆப்ஸை முகப்புத் திரையிலேயே அணுகலாம். உங்கள் வழக்கங்களின் அடிப்படையில் பரிந்துரைகள் மாறும். கடைசி வரிசையிலுள்ள ஆப்ஸ் உங்கள் முகப்புத் திரைக்கு நகர்த்தப்படும்."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"அதிகமாகப் பயன்படுத்திய ஆப்ஸை முகப்புத் திரையிலேயே எளிதாக அணுகலாம். உங்கள் வழக்கங்களின் அடிப்படையில் பரிந்துரைகள் மாறும். பிடித்தவை வரிசையில் உள்ள ஆப்ஸ் உங்கள் முகப்புத் திரைக்கு நகர்த்தப்படும்."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"அதிகமாகப் பயன்படுத்திய ஆப்ஸை முகப்புத் திரையிலேயே அணுகலாம். உங்கள் வழக்கங்களின் அடிப்படையில் பரிந்துரைகள் மாறும். கடைசி வரிசையிலுள்ள ஆப்ஸ் புதிய ஃபோல்டருக்கு நகர்த்தப்படும்."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"ஆப்ஸ் பரிந்துரைகளைப் பெறுக"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"வேண்டாம்"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"அமைப்புகள்"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"பயிற்சி <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"அனைத்தையும் அமைத்துவிட்டீர்கள்!"</string>
<string name="allset_hint" msgid="2384632994739392447">"முகப்புத் திரைக்குச் செல்ல மேல்நோக்கி ஸ்வைப் செய்யுங்கள்"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"மொபைலைப் பயன்படுத்தத் தயாராகிவிட்டீர்கள்"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"உங்கள் டேப்லெட்டைப் பயன்படுத்தத் தயாராகிவிட்டீர்கள்"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"சிஸ்டம் வழிசெலுத்தல் அமைப்புகள்"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"ஸ்கிரீன்ஷாட்"</string>
<string name="action_split" msgid="2098009717623550676">"பிரி"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"ஸ்பிளிட் ஸ்கிரீனுக்கு மற்றொரு ஆப்ஸைத் தட்டவும்"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"திரைப் பிரிப்பு அம்சத்தை ஆப்ஸ் ஆதரிக்கவில்லை."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"ஆப்ஸோ உங்கள் நிறுவனமோ இந்த செயலை அனுமதிப்பதில்லை"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"வழிகாட்டுதல் பயிற்சியைத் தவிர்க்கவா?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"<xliff:g id="NAME">%1$s</xliff:g> ஆப்ஸில் பிறகு இதைக் கண்டறியலாம்"</string>
diff --git a/quickstep/res/values-te/strings.xml b/quickstep/res/values-te/strings.xml
index 332e95d..052ca5d 100644
--- a/quickstep/res/values-te/strings.xml
+++ b/quickstep/res/values-te/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"మీ హోమ్ స్క్రీన్లోని ఇష్టమైన వాటి వరుసలో యాప్ సూచనలు పొందండి"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"మీరు ఎక్కువగా ఉపయోగించే యాప్లను నేరుగా మొదటి స్క్రీన్లోనే సులభంగా యాక్సెస్ చేయండి. మీ రోజువారీ యాక్టివిటీలను బట్టి సూచనలు మారతాయి. దిగువ వరుసలోని యాప్లు మీ మొదటి స్క్రీన్ పైకి చేరుకుంటాయి."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"మీరు ఎక్కువగా ఉపయోగించే యాప్లను నేరుగా మొదటి స్క్రీన్లోనే సులభంగా యాక్సెస్ చేయండి. మీ రోజువారీ యాక్టివిటీలను బట్టి సూచనలు మారతాయి. ఇష్టమైన వాటి వరుసలోని యాప్లు మీ మొదటి స్క్రీన్కు చేరుకుంటాయి."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"మీరు ఎక్కువగా ఉపయోగించే యాప్లను నేరుగా మొదటి స్క్రీన్లోనే సులభంగా యాక్సెస్ చేయండి. మీ రోజువారీ యాక్టివిటీలను బట్టి సూచనలు మారతాయి. దిగువ వరుసలోని యాప్లు కొత్త ఫోల్డర్కు తరలించబడతాయి."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"యాప్ సూచనలను పొందండి"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"వద్దు"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"సెట్టింగ్లు"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"ట్యుటోరియల్ <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"అంతా సెట్ అయింది!"</string>
<string name="allset_hint" msgid="2384632994739392447">"మొదటి స్క్రీన్కు వెళ్లడానికి పైకి స్వైప్ చేయండి"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"మీరు మీ ఫోన్ను ఉపయోగించడానికి సిద్ధంగా ఉన్నారు"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"మీరు మీ టాబ్లెట్ను ఉపయోగించడానికి సిద్ధంగా ఉన్నారు"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"సిస్టమ్ నావిగేషన్ సెట్టింగ్లు"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"స్క్రీన్షాట్"</string>
<string name="action_split" msgid="2098009717623550676">"స్ప్లిట్ చేయండి"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"స్క్రీన్ విభజనను ఉపయోగించడానికి మరొక యాప్ నొక్కండి"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"యాప్లో స్ప్లిట్-స్క్రీన్ పని చేయదు."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"ఈ చర్యను యాప్ గానీ, మీ సంస్థ గానీ అనుమతించవు"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"నావిగేషన్ ట్యుటోరియల్ను స్కిప్ చేయాలా?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"<xliff:g id="NAME">%1$s</xliff:g> యాప్లో మీరు తర్వాత కనుగొనవచ్చు"</string>
diff --git a/quickstep/res/values-th/strings.xml b/quickstep/res/values-th/strings.xml
index 59a84ff..13f0e12 100644
--- a/quickstep/res/values-th/strings.xml
+++ b/quickstep/res/values-th/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"รับคำแนะนำเกี่ยวกับแอปในแถวรายการโปรดของหน้าจอหลัก"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"เข้าถึงแอปที่คุณใช้มากที่สุดได้อย่างง่ายดายจากหน้าจอหลัก การแนะนำจะเปลี่ยนไปตามแอปที่ใช้งานเป็นประจำ แอปในแถวล่างจะย้ายขึ้นมาอยู่ในหน้าจอหลัก"</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"เข้าถึงแอปที่คุณใช้มากที่สุดได้อย่างง่ายดายจากหน้าจอหลัก การแนะนำจะเปลี่ยนไปตามแอปที่ใช้งานเป็นประจำ แอปในแถวรายการโปรดจะย้ายไปอยู่ในหน้าจอหลัก"</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"เข้าถึงแอปที่คุณใช้มากที่สุดได้อย่างง่ายดายจากหน้าจอหลัก การแนะนำจะเปลี่ยนไปตามแอปที่ใช้งานเป็นประจำ แอปในแถวล่างจะย้ายไปอยู่ในโฟลเดอร์ใหม่"</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"ดูแอปแนะนำ"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"ไม่เป็นไร"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"การตั้งค่า"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"บทแนะนำ <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"เรียบร้อยแล้ว"</string>
<string name="allset_hint" msgid="2384632994739392447">"ปัดขึ้นเพื่อไปที่หน้าแรก"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"คุณเริ่มใช้โทรศัพท์ได้แล้ว"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"คุณเริ่มใช้แท็บเล็ตได้แล้ว"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"การตั้งค่าการนำทางของระบบ"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"ภาพหน้าจอ"</string>
<string name="action_split" msgid="2098009717623550676">"แยก"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"แตะที่แอปอื่นเพื่อใช้แบ่งหน้าจอ"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"แอปไม่รองรับการแบ่งหน้าจอ"</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"แอปหรือองค์กรของคุณไม่อนุญาตการดำเนินการนี้"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"ข้ามบทแนะนำการนำทางไหม"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"คุณดูบทแนะนำนี้ได้ภายหลังในแอป \"<xliff:g id="NAME">%1$s</xliff:g>\""</string>
diff --git a/quickstep/res/values-tl/strings.xml b/quickstep/res/values-tl/strings.xml
index b22d85d..68b3902 100644
--- a/quickstep/res/values-tl/strings.xml
+++ b/quickstep/res/values-tl/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Makakuha ng mga iminumungkahing app sa row ng mga paborito ng iyong Home screen"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Madaling ma-access ang mga pinakaginagamit mong app nang direkta sa Home screen. Magbabago ang mga suhestyon batay sa iyong mga routine. Mapupunta sa iyong Home screen ang mga app na nasa ibabang row."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Madaling ma-access ang mga pinakaginagamit mong app nang direkta sa Home screen. Magbabago ang mga suhestyon batay sa iyong mga routine. Mapupunta sa iyong Home screen ang mga app sa row ng mga paborito."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Madaling ma-access ang mga pinakaginagamit mong app, direkta sa Home screen. Magbabago ang mga suhestyon batay sa iyong mga routine. Mapupunta sa isang bagong folder ang mga app na nasa ibabang row."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Kumuha ng mga suhestiyon sa app"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Huwag na lang"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Mga Setting"</string>
@@ -78,6 +77,8 @@
<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">"Handa na ang lahat!"</string>
<string name="allset_hint" msgid="2384632994739392447">"Mag-swipe pataas para pumunta sa Home"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"Handa mo nang simulan ang paggamit sa iyong telepono"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"Handa mo nang simulan ang paggamit sa iyong tablet"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Mga setting ng navigation ng system"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string>
<string name="action_split" msgid="2098009717623550676">"Split"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Mag-tap ng ibang app para gamitin ang splitscreen"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"Hindi sinusuportahan ng app ang split-screen."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"Hindi pinapayagan ng app o ng iyong organisasyon ang pagkilos na ito"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Laktawan ang tutorial sa pag-navigate?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Makikita mo ito sa <xliff:g id="NAME">%1$s</xliff:g> app sa ibang pagkakataon"</string>
diff --git a/quickstep/res/values-tr/strings.xml b/quickstep/res/values-tr/strings.xml
index b3054f7..c7f7ac9 100644
--- a/quickstep/res/values-tr/strings.xml
+++ b/quickstep/res/values-tr/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Ana ekranınızın favoriler satırında uygulama önerileri alın"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"En çok kullanılan uygulamalarınıza Ana ekranda kolayca erişin. Öneriler, rutinlerinize dayalı olarak değişir. Alt satırdaki uygulamalar, yukarı taşınarak Ana ekranınıza alınır."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"En çok kullanılan uygulamalarınıza Ana ekrandan kolayca erişin. Öneriler rutinlerinize dayalı olarak değişir. Favoriler satırındaki uygulamalar Ana ekranınıza taşınır."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"En çok kullanılan uygulamalarınıza Ana ekranda kolayca erişin. Öneriler, rutinlerinize dayalı olarak değişir. Alt satırdaki uygulamalar yeni bir klasöre taşınır."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Uygulama önerileri al"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Hayır, teşekkürler"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Ayarlar"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"Eğitim <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"İşlem tamam!"</string>
<string name="allset_hint" msgid="2384632994739392447">"Ana ekrana gitmek için yukarı kaydırın"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"Telefonunuzu kullanmaya hazırsınız"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"Tabletinizi kullanmaya hazırsınız"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Sistem gezinme ayarları"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Ekran görüntüsü"</string>
<string name="action_split" msgid="2098009717623550676">"Böl"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Bölünmüş ekran için başka bir uygulamaya dokunun"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"Uygulama bölünmüş ekranı desteklemiyor."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"Uygulamanız veya kuruluşunuz bu işleme izin vermiyor"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Gezinme eğitimi atlansın mı?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Bunu daha sonra <xliff:g id="NAME">%1$s</xliff:g> uygulamasında bulabilirsiniz"</string>
diff --git a/quickstep/res/values-uk/strings.xml b/quickstep/res/values-uk/strings.xml
index 490ac2d..173f637 100644
--- a/quickstep/res/values-uk/strings.xml
+++ b/quickstep/res/values-uk/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Рекомендовані додатки з\'являтимуться в рядку \"Вибране\" на головному екрані"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"З легкістю відкривайте на головному екрані ті додатки, які використовуєте найчастіше. Рекомендації змінюватимуться залежно від ваших дій. Додатки в нижньому рядку перемістяться на головний екран."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"З легкістю відкривайте найпотрібніші додатки просто з головного екрана. Рекомендації змінюватимуться залежно від ваших дій. Додатки з рядка \"Вибране\" буде переміщено на головний екран."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"З легкістю відкривайте найвикористовуваніші додатки просто з головного екрана. Рекомендації змінюватимуться залежно від ваших дій. Додатки в нижньому рядку буде переміщено в нову папку."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Показувати рекомендації"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Не потрібно"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Налаштування"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"Навчальний посібник <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"Готово."</string>
<string name="allset_hint" msgid="2384632994739392447">"Щоб перейти на головний екран, проведіть пальцем угору"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"Тепер ви можете користуватися телефоном"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"Тепер ви можете користуватися планшетом"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Системні налаштування навігації"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Знімок екрана"</string>
<string name="action_split" msgid="2098009717623550676">"Розділити"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Щоб розділити екран, виберіть ще один додаток"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"Додаток не підтримує розділення екрана."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"Ця дія заборонена додатком або адміністратором організації"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Пропустити посібник із навігації?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Ви знайдете його пізніше в додатку <xliff:g id="NAME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-ur/strings.xml b/quickstep/res/values-ur/strings.xml
index 8f2da65..89e518b 100644
--- a/quickstep/res/values-ur/strings.xml
+++ b/quickstep/res/values-ur/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"اپنی ہوم اسکرین کی پسندیدہ قطار پر ایپ کی تجاویز حاصل کریں"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"ہوم اسکرین پر آسانی سے اپنی سب سے زیادہ مستعمل ایپس تک رسائی حاصل کریں۔ آپ کی روٹینز کی بنیاد پر تجاویز تبدیل ہوں گی۔ نچلی قطار میں موجود ایپس آپ کی ہوم اسکرین کے اوپر منتقل ہوں گی۔"</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"ہوم اسکرین پر آسانی سے اپنی سب سے زیادہ مستعمل ایپس تک رسائی حاصل کریں۔ آپ کی روٹینز کی بنیاد پر تجاویز تبدیل ہوں گی۔ پسندیدہ میں موجود ایپس آپ کی ہوم اسکرین کے اوپر منتقل ہوں گی۔"</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"ہوم اسکرین پر، آسانی سے اپنی سب سے زیادہ مستعمل ایپس تک رسائی حاصل کریں۔ آپ کی روٹینز کی بنیاد پر تجاویز تبدیل ہوں گی۔ نچلی قطار میں موجود ایپس نئے فولڈر میں منتقل ہوں گی۔"</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"ایپس کی تجاویز حاصل کریں"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"نہیں شکریہ"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"ترتیبات"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"ٹیوٹوریل <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"سب کچھ تیار ہے!"</string>
<string name="allset_hint" msgid="2384632994739392447">"ہوم پر جانے کے لیے اوپر سوائپ کریں"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"آپ اپنا فون استعمال شروع کرنے کے لیے تیار ہیں"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"آپ اپنے ٹیبلیٹ کا استعمال شروع کرنے کے لیے تیار ہیں"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"سسٹم نیویگیشن کی ترتیبات"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"اسکرین شاٹ"</string>
<string name="action_split" msgid="2098009717623550676">"اسپلٹ"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"اسپلٹ اسکرین کا استعمال کرنے کیلئے دوسری ایپ پر تھپتھپائیں"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"ایپ سپلٹ اسکرین کو سپورٹ نہیں کرتی۔"</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"ایپ یا آپ کی تنظیم کی جانب سے اس کارروائی کی اجازت نہیں ہے"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"نیویگیشن کا ٹیوٹوریل نظر انداز کریں؟"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"آپ اسے بعد میں <xliff:g id="NAME">%1$s</xliff:g> ایپ میں تلاش کر سکتے ہیں"</string>
diff --git a/quickstep/res/values-uz/strings.xml b/quickstep/res/values-uz/strings.xml
index 9c47cef..56a76d5 100644
--- a/quickstep/res/values-uz/strings.xml
+++ b/quickstep/res/values-uz/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Tavsiya etiladigan ilovalar bosh ekranning saralanganlar ruknida chiqadi"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Faol ishlatiladigan ilovalarga bosh ekrandan osongina kira olasiz. Tavsiyalar oxirgi faoliyatingiz asosida almashib boradi. Pastki qatordagi ilovalar bosh ekranga chiqadi."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Faol ishlatiladigan ilovalarga bosh ekrandan osongina kira olasiz. Tavsiyalar oxirgi faoliyatingiz asosida almashib boradi. Saralanganlar qatoridagi ilovalar bosh ekranga chiqadi."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Faol ishlatiladigan ilovalarga bosh ekrandan osongina kira olasiz. Tavsiyalar oxirgi faoliyatingiz asosida almashib boradi. Pastki qatordagi ilovalar yangi jildga chiqadi."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Tavsiyalarni chiqarish"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Kerak emas"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Sozlamalar"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"Darslik: <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"Hammasi tayyor!"</string>
<string name="allset_hint" msgid="2384632994739392447">"Boshiga qaytish uchun tepaga suring"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"Telefoningiz xizmatga tayyor"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"Planshetingiz xizmatga tayyor"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Tizim navigatsiya sozlamalari"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Skrinshot"</string>
<string name="action_split" msgid="2098009717623550676">"Ajratish"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Ekranni ikkiga ajratish uchun boshqa ilovani bosing"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"Bu ilovada ekranni ikkiga ajratish ishlamaydi."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"Bu amal ilova yoki tashkilotingiz tomonidan taqiqlangan"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Navigatsiya darsi yopilsinmi?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Bu darslar <xliff:g id="NAME">%1$s</xliff:g> ilovasida chiqadi"</string>
diff --git a/quickstep/res/values-vi/strings.xml b/quickstep/res/values-vi/strings.xml
index 2e5c3e7..3b17966 100644
--- a/quickstep/res/values-vi/strings.xml
+++ b/quickstep/res/values-vi/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Nhận các ứng dụng đề xuất trên hàng mục ưa thích của Màn hình chính"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Bạn có thể dễ dàng truy cập những ứng dụng mà mình dùng thường xuyên nhất ngay trên màn hình chính. Các ứng dụng đề xuất sẽ thay đổi dựa trên thói quen của bạn. Các ứng dụng ở hàng dưới cùng sẽ chuyển lên phía trên của Màn hình chính."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Bạn có thể dễ dàng mở những ứng dụng mà mình dùng thường xuyên nhất ngay trên màn hình chính. Các ứng dụng đề xuất sẽ thay đổi dựa trên thói quen của bạn. Các ứng dụng ở hàng mục ưa thích sẽ chuyển sang Màn hình chính."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Bạn có thể dễ dàng truy cập những ứng dụng mà mình dùng thường xuyên nhất ngay trên màn hình chính. Các ứng dụng đề xuất sẽ thay đổi dựa trên thói quen của bạn. Các ứng dụng ở hàng dưới cùng sẽ chuyển đến một thư mục mới."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Nhận ứng dụng đề xuất"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Không, cảm ơn"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Cài đặt"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"Hướng dẫn <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"Đã hoàn tất!"</string>
<string name="allset_hint" msgid="2384632994739392447">"Vuốt lên để chuyển đến Màn hình chính"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"Vậy là bạn đã sẵn sàng sử dụng điện thoại của mình"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"Bạn đã sẵn sàng sử dụng máy tính bảng"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Chế độ cài đặt di chuyển trên hệ thống"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Chụp ảnh màn hình"</string>
<string name="action_split" msgid="2098009717623550676">"Chia đôi màn hình"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Nhấn vào một ứng dụng khác để dùng màn hình chia đôi"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"Ứng dụng không hỗ trợ chia đôi màn hình."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"Ứng dụng hoặc tổ chức của bạn không cho phép thực hiện hành động này"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Bỏ qua phần hướng dẫn thao tác?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Bạn có thể tìm lại phần hướng dẫn này trong ứng dụng <xliff:g id="NAME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values-zh-rCN/strings.xml b/quickstep/res/values-zh-rCN/strings.xml
index 0b5193f..0ff369e 100644
--- a/quickstep/res/values-zh-rCN/strings.xml
+++ b/quickstep/res/values-zh-rCN/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"在主屏幕的收藏行获取应用建议"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"直接在主屏幕上轻松访问您最常用的应用。系统会根据您的日常安排提供不同的建议。最下面一排中的应用会向上移到主屏幕中。"</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"直接在主屏幕上轻松访问您最常用的应用。建议会因您的日常安排而变化,收藏行中的应用将移到主屏幕上。"</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"直接在主屏幕上轻松访问您最常用的应用。系统会根据您的日常安排提供不同的建议。最下面一排中的应用会移到新文件夹中。"</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"获取应用建议"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"不用了"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"设置"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"教程 <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"大功告成!"</string>
<string name="allset_hint" msgid="2384632994739392447">"向上滑动即可转到主屏幕"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"您可以开始使用手机了"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"您可以开始使用平板电脑了"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"系统导航设置"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"屏幕截图"</string>
<string name="action_split" msgid="2098009717623550676">"拆分"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"点按另一个应用即可使用分屏"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"应用不支持分屏。"</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"该应用或您所在的单位不允许执行此操作"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"要跳过导航教程吗?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"您之后可以在“<xliff:g id="NAME">%1$s</xliff:g>”应用中找到此教程"</string>
diff --git a/quickstep/res/values-zh-rHK/strings.xml b/quickstep/res/values-zh-rHK/strings.xml
index b700da0..ff648df 100644
--- a/quickstep/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-zh-rHK/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"在主畫面「我的最愛」列取得應用程式建議"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"在主畫面輕鬆存取常用的應用程式。系統會根據您的日常安排更改建議,並將底部的應用程式移到主畫面。"</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"在主畫面輕鬆存取最常用的應用程式。系統會根據您的日常安排變更建議,「我的最愛」列中的應用程式會移至主畫面。"</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"在主畫面輕鬆存取最常用的應用程式。系統會根據您的日常安排變更建議,並將底列的應用程式移至新資料夾。"</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"取得應用程式建議"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"不用了,謝謝"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"設定"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"教學課程 <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"設定完成!"</string>
<string name="allset_hint" msgid="2384632994739392447">"向上滑動即可前往主畫面"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"您可以開始使用手機了"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"您可以開始使用平板電腦了"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"系統導覽設定"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"螢幕截圖"</string>
<string name="action_split" msgid="2098009717623550676">"分割"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"輕按其他應用程式以使用分割螢幕"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"應用程式不支援分割螢幕。"</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"應用程式或您的機構不允許此操作"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"要略過手勢操作教學課程嗎?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"您之後可以在「<xliff:g id="NAME">%1$s</xliff:g>」應用程式找到這些說明"</string>
diff --git a/quickstep/res/values-zh-rTW/strings.xml b/quickstep/res/values-zh-rTW/strings.xml
index e7b74a4..23bee17 100644
--- a/quickstep/res/values-zh-rTW/strings.xml
+++ b/quickstep/res/values-zh-rTW/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"在主畫面的收藏列取得應用程式建議"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"你可以輕鬆地在主畫面上找到自己常用的應用程式。應用程式建議會依據你的日常使用習慣而有所不同。系統會將底部列出的應用程式上移到主畫面。"</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"你可以輕鬆地在主畫面上找到自己常用的應用程式。系統會根據你的日常使用習慣提供不同的應用程式建議,並在主畫面顯示收藏列中的應用程式。"</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"你可以輕鬆地在主畫面上找到自己常用的應用程式。應用程式建議會根據日常安排有所不同。系統會將底部列出的應用程式移到新的資料夾。"</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"取得應用程式建議"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"不用了,謝謝"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"設定"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"教學課程 <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"設定完成!"</string>
<string name="allset_hint" msgid="2384632994739392447">"向上滑動即可前往主畫面"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"你可以開始使用手機了"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"你可以開始使用平板電腦了"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"系統操作機制設定"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"螢幕截圖"</string>
<string name="action_split" msgid="2098009717623550676">"分割"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"輕觸另一個應用程式即可使用分割畫面"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"這個應用程式不支援分割畫面。"</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"這個應用程式或貴機構不允許執行這個動作"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"要略過手勢操作教學課程嗎?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"你之後可以在「<xliff:g id="NAME">%1$s</xliff:g>」應用程式找到這些說明"</string>
diff --git a/quickstep/res/values-zu/strings.xml b/quickstep/res/values-zu/strings.xml
index c69f63d..46ba929 100644
--- a/quickstep/res/values-zu/strings.xml
+++ b/quickstep/res/values-zu/strings.xml
@@ -35,7 +35,6 @@
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Thola iziphakamiso zohlelo lokusebenza kumugqa wezintandokazi Zesikrini sakho sasekhaya"</string>
<string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Finyelela kalula izinhlelo zakho zokusebenza ezisetshenziswa kakhulu khona kusikrini sasekhaya. Iziphakamiso zizoshintsha ngokususelwe kwimijikelezo yakho. Izinhlelo zokusebenza ezisemgqeni ongezansi zizoya phezulu kusikrini sakho sasekhaya."</string>
<string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Finyelela kalula izinhlelo zakho zokusebenza ezisetshenziswa kakhulu khona kusikrini sasekhaya. Iziphakamiso zizoshintsha ngokususelwe kwimijikelezo yakho. Izintandokazi zomugqa wezinhlelo zokusebenza zizoya Kusikrini sakho sasekhaya."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Finyelela kalula izinhlelo zakho zokusebenza ezisetshenziswa njalo, kusikrini sasekhaya. Iziphakamiso zizoshintsha ngokususelwe kwimijikelezo yakho. Izinhlelo zokusebenza ezisemgqeni ongezansi zizoya phezulu kufolda entsha."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"Thola iziphakamiso zohlelo lokusebenza"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Cha ngiyabonga"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"Amasethingi"</string>
@@ -78,6 +77,8 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"Okokufundisa <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"Konke kusethiwe!"</string>
<string name="allset_hint" msgid="2384632994739392447">"Swayiphela phezulu ukuze uye Ekhaya"</string>
+ <!-- no translation found for allset_button_hint (2395219947744706291) -->
+ <skip />
<string name="allset_description" msgid="6350320429953234580">"Usulungele ukuqala ukusebenzisa ifoni yakho"</string>
<string name="allset_description_tablet" msgid="7332070270570039247">"Usulungele ukuqala ukusebenzisa ithebulethi yakho"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Amasethingi wokuzulazula isistimu"</annotation></string>
@@ -85,7 +86,8 @@
<string name="action_screenshot" msgid="8171125848358142917">"Isithombe-skrini"</string>
<string name="action_split" msgid="2098009717623550676">"Hlukanisa"</string>
<string name="toast_split_select_app" msgid="5453865907322018352">"Thepha enye i-app ukuze usebenzise isikrini sokuhlukanisa"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"Uhlelo lokusebenza alusekeli isikrini esihlukanisiwe."</string>
+ <!-- no translation found for toast_split_app_unsupported (2360229567007828914) -->
+ <skip />
<string name="blocked_by_policy" msgid="2071401072261365546">"Lesi senzo asivunyelwanga uhlelo lokusebenza noma inhlangano yakho"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Yeqa isifundo sokuzulazula?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Lokhu ungakuthola kamuva ku-app ye-<xliff:g id="NAME">%1$s</xliff:g>"</string>
diff --git a/quickstep/res/values/config.xml b/quickstep/res/values/config.xml
index 3b4a28b..198a676 100644
--- a/quickstep/res/values/config.xml
+++ b/quickstep/res/values/config.xml
@@ -43,4 +43,9 @@
<!-- Accessibility actions -->
<item type="id" name="action_move_to_top_or_left" />
<item type="id" name="action_move_to_bottom_or_right" />
+
+ <!-- The max scale for the wallpaper when it's zoomed in -->
+ <item name="config_wallpaperMaxScale" format="float" type="dimen">
+ @*android:dimen/config_wallpaperMaxScale
+ </item>
</resources>
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index c85e71c..cd60879 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -202,6 +202,9 @@
<!-- All Set page -->
<dimen name="allset_page_margin_horizontal">40dp</dimen>
+ <dimen name="allset_page_allset_text_size">36sp</dimen>
+ <dimen name="allset_page_swipe_up_text_size">14sp</dimen>
+
<dimen name="allset_title_margin_top">24dp</dimen>
<dimen name="allset_title_icon_margin_top">32dp</dimen>
<dimen name="allset_subtitle_margin_top">24dp</dimen>
@@ -258,7 +261,10 @@
<dimen name="taskbar_contextual_button_padding">16dp</dimen>
<dimen name="taskbar_contextual_padding_top">8dp</dimen>
<dimen name="taskbar_nav_buttons_size">44dp</dimen>
- <dimen name="taskbar_contextual_button_margin">47dp</dimen>
+ <dimen name="taskbar_split_instructions_margin">48dp</dimen>
+ <dimen name="taskbar_contextual_button_margin">120dp</dimen>
+ <dimen name="taskbar_suw_insets">48dp</dimen>
+ <dimen name="taskbar_suw_frame">48dp</dimen>
<dimen name="taskbar_hotseat_nav_spacing">24dp</dimen>
<dimen name="taskbar_contextual_buttons_size">35dp</dimen>
<dimen name="taskbar_stashed_size">24dp</dimen>
@@ -268,6 +274,7 @@
<dimen name="taskbar_stashed_handle_height">4dp</dimen>
<dimen name="taskbar_edu_wave_anim_trans_y">25dp</dimen>
<dimen name="taskbar_edu_wave_anim_trans_y_return_overshoot">4dp</dimen>
+ <dimen name="taskbar_edu_horizontal_margin">112dp</dimen>
<dimen name="taskbar_nav_buttons_width_kids">88dp</dimen>
<dimen name="taskbar_nav_buttons_height_kids">40dp</dimen>
<dimen name="taskbar_nav_buttons_corner_radius_kids">40dp</dimen>
@@ -275,6 +282,19 @@
<dimen name="taskbar_home_button_left_margin_kids">48dp</dimen>
<dimen name="taskbar_icon_size_kids">32dp</dimen>
+ <!-- Transient taskbar -->
+ <dimen name="transient_taskbar_size">76dp</dimen>
+ <dimen name="transient_taskbar_margin">24dp</dimen>
+ <dimen name="transient_taskbar_shadow_blur">40dp</dimen>
+ <dimen name="transient_taskbar_key_shadow_distance">10dp</dimen>
+ <dimen name="transient_taskbar_stashed_size">32dp</dimen>
+ <dimen name="transient_taskbar_icon_spacing">10dp</dimen>
+ <!-- Taskbar swipe up thresholds -->
+ <dimen name="taskbar_app_window_threshold">150dp</dimen>
+ <dimen name="taskbar_home_overview_threshold">225dp</dimen>
+ <dimen name="taskbar_catch_up_threshold">300dp</dimen>
+ <dimen name="taskbar_nav_threshold">40dp</dimen>
+
<!-- Taskbar 3 button spacing -->
<dimen name="taskbar_button_space_inbetween">24dp</dimen>
<dimen name="taskbar_button_space_inbetween_phone">40dp</dimen>
@@ -283,4 +303,9 @@
<dimen name="taskbar_button_margin_4_5">47dp</dimen>
<dimen name="taskbar_button_margin_4_4">47dp</dimen>
<dimen name="taskbar_button_margin_default">47dp</dimen>
+
+ <!-- Launcher splash screen -->
+ <!-- Note: keep this value in sync with the WindowManager/Shell dimens.xml -->
+ <!-- starting_surface_exit_animation_window_shift_length -->
+ <dimen name="starting_surface_exit_animation_window_shift_length">20dp</dimen>
</resources>
diff --git a/quickstep/res/values/override.xml b/quickstep/res/values/override.xml
index 705ec9d..4f472f0 100644
--- a/quickstep/res/values/override.xml
+++ b/quickstep/res/values/override.xml
@@ -25,4 +25,6 @@
<string name="model_delegate_class" translatable="false">com.android.launcher3.model.QuickstepModelDelegate</string>
+ <string name="secondary_display_predictions_class" translatable="false">com.android.launcher3.secondarydisplay.SecondaryDisplayPredictionsImpl</string>
+
</resources>
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index 81b0dd2..1a801b5 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -68,7 +68,6 @@
<string name="hotseat_edu_message_migrate">Easily access your most-used apps right on the Home screen. Suggestions will change based on your routines. Apps on the bottom row will move up to your Home screen. </string>
<string name="hotseat_edu_message_migrate_landscape">Easily access your most-used apps right on the Home screen. Suggestions will change based on your routines. Apps in favorites row will move to your Home screen. </string>
- <string name="hotseat_edu_message_migrate_alt">Easily access your most-used apps, right on the Home screen. Suggestions will change based on your routines. Apps on the bottom row will move to a new folder.</string>
<!-- Button text to opt in for fully predicted hotseat -->
<string name="hotseat_edu_accept">Get app suggestions</string>
@@ -188,6 +187,8 @@
<string name="allset_title">All set!</string>
<!-- Hint string at the bottom of "All Set" page [CHAR LIMIT=NONE] -->
<string name="allset_hint">Swipe up to go Home</string>
+ <!-- Hint string at the bottom of "All Set" page for button navigation [CHAR LIMIT=NONE] -->
+ <string name="allset_button_hint">Tap the home button to go to your home screen</string>
<!-- Description of "All Set" page on phones [CHAR LIMIT=NONE] -->
<string name="allset_description">You\u2019re ready to start using your phone</string>
<!-- Description of "All Set" page on tablets [CHAR LIMIT=NONE] -->
@@ -205,7 +206,7 @@
<!-- Label for toast with instructions for split screen selection mode. [CHAR_LIMIT=50] -->
<string name="toast_split_select_app">Tap another app to use splitscreen</string>
<!-- Label for toast when app selected for split isn't supported. [CHAR_LIMIT=50] -->
- <string name="toast_split_app_unsupported">App does not support split-screen.</string>
+ <string name="toast_split_app_unsupported">Choose another app to use split screen</string>
<!-- Message shown when an action is blocked by a policy enforced by the app or the organization managing the device. [CHAR_LIMIT=NONE] -->
<string name="blocked_by_policy">This action isn\'t allowed by the app or your organization</string>
diff --git a/quickstep/res/values/styles.xml b/quickstep/res/values/styles.xml
index 7225220..4f0fdf1 100644
--- a/quickstep/res/values/styles.xml
+++ b/quickstep/res/values/styles.xml
@@ -51,6 +51,7 @@
parent="TextAppearance.GestureTutorial.Feedback.Title">
<item name="android:letterSpacing">0.03</item>
<item name="android:lineHeight">44sp</item>
+ <item name="android:textSize">@dimen/allset_page_allset_text_size</item>
</style>
<style name="TextAppearance.GestureTutorial.Dialog.Title"
@@ -151,6 +152,8 @@
<item name="android:background">@drawable/bg_overview_clear_all_button</item>
<item name="android:minWidth">96dp</item>
<item name="android:minHeight">48dp</item>
+ <item name="android:paddingStart">12dp</item>
+ <item name="android:paddingEnd">12dp</item>
<item name="android:stateListAnimator">@null</item>
</style>
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
deleted file mode 100644
index c2e8658..0000000
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ /dev/null
@@ -1,708 +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;
-
-import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
-import static com.android.launcher3.AbstractFloatingView.TYPE_HIDE_BACK_BUTTON;
-import static com.android.launcher3.LauncherState.FLAG_HIDE_BACK_BUTTON;
-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_SPLIT_SELECT;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE;
-import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID;
-import static com.android.launcher3.popup.QuickstepSystemShortcut.getSplitSelectShortcutByPosition;
-import static com.android.launcher3.taskbar.LauncherTaskbarUIController.ALL_APPS_PAGE_PROGRESS_INDEX;
-import static com.android.launcher3.taskbar.LauncherTaskbarUIController.MINUS_ONE_PAGE_PROGRESS_INDEX;
-import static com.android.launcher3.taskbar.LauncherTaskbarUIController.WIDGETS_PAGE_PROGRESS_INDEX;
-import static com.android.launcher3.util.DisplayController.CHANGE_ACTIVE_SCREEN;
-import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
-import static com.android.launcher3.util.DisplayController.NavigationMode.TWO_BUTTONS;
-import static com.android.launcher3.util.Executors.THREAD_POOL_EXECUTOR;
-import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
-import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY;
-
-import android.animation.AnimatorSet;
-import android.animation.ValueAnimator;
-import android.app.ActivityManager;
-import android.app.ActivityOptions;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentSender;
-import android.hardware.SensorManager;
-import android.hardware.devicestate.DeviceStateManager;
-import android.os.Bundle;
-import android.os.CancellationSignal;
-import android.os.IBinder;
-import android.view.Display;
-import android.view.View;
-import android.window.SplashScreen;
-
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.dragndrop.DragOptions;
-import com.android.launcher3.model.WellbeingModel;
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.popup.SystemShortcut;
-import com.android.launcher3.proxy.ProxyActivityStarter;
-import com.android.launcher3.proxy.StartActivityParams;
-import com.android.launcher3.statehandlers.BackButtonAlphaHandler;
-import com.android.launcher3.statehandlers.DepthController;
-import com.android.launcher3.statemanager.StateManager.StateHandler;
-import com.android.launcher3.taskbar.LauncherTaskbarUIController;
-import com.android.launcher3.taskbar.TaskbarManager;
-import com.android.launcher3.uioverrides.RecentsViewStateController;
-import com.android.launcher3.util.ActivityOptionsWrapper;
-import com.android.launcher3.util.DisplayController;
-import com.android.launcher3.util.DisplayController.NavigationMode;
-import com.android.launcher3.util.IntSet;
-import com.android.launcher3.util.ObjectWrapper;
-import com.android.launcher3.util.PendingSplitSelectInfo;
-import com.android.launcher3.util.RunnableList;
-import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
-import com.android.launcher3.util.UiThreadHelper;
-import com.android.quickstep.OverviewCommandHelper;
-import com.android.quickstep.RecentsModel;
-import com.android.quickstep.SystemUiProxy;
-import com.android.quickstep.TaskUtils;
-import com.android.quickstep.TouchInteractionService.TISBinder;
-import com.android.quickstep.util.LauncherUnfoldAnimationController;
-import com.android.quickstep.util.ProxyScreenStatusProvider;
-import com.android.quickstep.util.RemoteAnimationProvider;
-import com.android.quickstep.util.RemoteFadeOutAnimationListener;
-import com.android.quickstep.util.SplitSelectStateController;
-import com.android.quickstep.util.TISBindHelper;
-import com.android.quickstep.views.OverviewActionsView;
-import com.android.quickstep.views.RecentsView;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.ActivityOptionsCompat;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.unfold.UnfoldTransitionFactory;
-import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
-import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig;
-import com.android.systemui.unfold.config.UnfoldTransitionConfig;
-import com.android.systemui.unfold.system.ActivityManagerActivityTypeProvider;
-import com.android.systemui.unfold.system.DeviceStateManagerFoldProvider;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.stream.Stream;
-
-/**
- * Extension of Launcher activity to provide quickstep specific functionality
- */
-public abstract class BaseQuickstepLauncher extends Launcher {
-
- private DepthController mDepthController;
- private QuickstepTransitionManager mAppTransitionManager;
-
- /**
- * Reusable command for applying the back button alpha on the background thread.
- */
- public static final UiThreadHelper.AsyncCommand SET_BACK_BUTTON_ALPHA =
- (context, arg1, arg2) -> SystemUiProxy.INSTANCE.get(context).setNavBarButtonAlpha(
- Float.intBitsToFloat(arg1), arg2 != 0);
-
- private OverviewActionsView mActionsView;
-
- private TISBindHelper mTISBindHelper;
- private @Nullable TaskbarManager mTaskbarManager;
- private @Nullable OverviewCommandHelper mOverviewCommandHelper;
- private @Nullable LauncherTaskbarUIController mTaskbarUIController;
-
- // Will be updated when dragging from taskbar.
- private @Nullable DragOptions mNextWorkspaceDragOptions = null;
-
- private @Nullable UnfoldTransitionProgressProvider mUnfoldTransitionProgressProvider;
- private @Nullable LauncherUnfoldAnimationController mLauncherUnfoldAnimationController;
-
- /**
- * If Launcher restarted while in the middle of an Overview split select, it needs this data to
- * recover. In all other cases this will remain null.
- */
- private PendingSplitSelectInfo mPendingSplitSelectInfo = null;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- if (savedInstanceState != null) {
- mPendingSplitSelectInfo = ObjectWrapper.unwrap(
- savedInstanceState.getIBinder(PENDING_SPLIT_SELECT_INFO));
- }
- addMultiWindowModeChangedListener(mDepthController);
- initUnfoldTransitionProgressProvider();
- }
-
- @Override
- protected void onResume() {
- super.onResume();
-
- if (mLauncherUnfoldAnimationController != null) {
- mLauncherUnfoldAnimationController.onResume();
- }
- }
-
- @Override
- protected void onPause() {
- if (mLauncherUnfoldAnimationController != null) {
- mLauncherUnfoldAnimationController.onPause();
- }
-
- super.onPause();
- }
-
- @Override
- public void onDestroy() {
- mAppTransitionManager.onActivityDestroyed();
- if (mUnfoldTransitionProgressProvider != null) {
- mUnfoldTransitionProgressProvider.destroy();
- }
-
- mTISBindHelper.onDestroy();
- if (mTaskbarManager != null) {
- mTaskbarManager.clearActivity(this);
- }
-
- if (mLauncherUnfoldAnimationController != null) {
- mLauncherUnfoldAnimationController.onDestroy();
- }
-
- super.onDestroy();
- }
-
- @Override
- protected void onNewIntent(Intent intent) {
- super.onNewIntent(intent);
-
- if (mOverviewCommandHelper != null) {
- mOverviewCommandHelper.clearPendingCommands();
- }
- }
-
- public QuickstepTransitionManager getAppTransitionManager() {
- return mAppTransitionManager;
- }
-
- @Override
- public void onEnterAnimationComplete() {
- super.onEnterAnimationComplete();
- // After the transition to home, enable the high-res thumbnail loader if it wasn't enabled
- // as a part of quickstep, so that high-res thumbnails can load the next time we enter
- // overview
- RecentsModel.INSTANCE.get(this).getThumbnailCache()
- .getHighResLoadingState().setVisible(true);
- }
-
- @Override
- protected void handleGestureContract(Intent intent) {
- if (FeatureFlags.SEPARATE_RECENTS_ACTIVITY.get()) {
- super.handleGestureContract(intent);
- }
- }
-
- @Override
- public void onTrimMemory(int level) {
- super.onTrimMemory(level);
- RecentsModel.INSTANCE.get(this).onTrimMemory(level);
- }
-
- @Override
- public void onUiChangedWhileSleeping() {
- // Remove the snapshot because the content view may have obvious changes.
- UI_HELPER_EXECUTOR.execute(
- () -> ActivityManagerWrapper.getInstance().invalidateHomeTaskSnapshot(this));
- }
-
- @Override
- protected void onScreenOff() {
- super.onScreenOff();
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- RecentsView recentsView = getOverviewPanel();
- recentsView.finishRecentsAnimation(true /* toRecents */, null);
- }
- }
-
- /**
- * {@code LauncherOverlayCallbacks} scroll amount.
- * Indicates transition progress to -1 screen.
- * @param progress From 0 to 1.
- */
- @Override
- public void onScrollChanged(float progress) {
- super.onScrollChanged(progress);
- onTaskbarInAppDisplayProgressUpdate(progress, MINUS_ONE_PAGE_PROGRESS_INDEX);
- }
-
- @Override
- public void onAllAppsTransition(float progress) {
- super.onAllAppsTransition(progress);
- onTaskbarInAppDisplayProgressUpdate(progress, ALL_APPS_PAGE_PROGRESS_INDEX);
- }
-
- @Override
- public void onWidgetsTransition(float progress) {
- super.onWidgetsTransition(progress);
- onTaskbarInAppDisplayProgressUpdate(progress, WIDGETS_PAGE_PROGRESS_INDEX);
- }
-
- private void onTaskbarInAppDisplayProgressUpdate(float progress, int flag) {
- if (mTaskbarManager == null
- || mTaskbarManager.getCurrentActivityContext() == null
- || mTaskbarUIController == null) {
- return;
- }
- mTaskbarUIController.onTaskbarInAppDisplayProgressUpdate(progress, flag);
- }
-
- @Override
- public void startIntentSenderForResult(IntentSender intent, int requestCode,
- Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags, Bundle options) {
- if (requestCode != -1) {
- mPendingActivityRequestCode = requestCode;
- StartActivityParams params = new StartActivityParams(this, requestCode);
- params.intentSender = intent;
- params.fillInIntent = fillInIntent;
- params.flagsMask = flagsMask;
- params.flagsValues = flagsValues;
- params.extraFlags = extraFlags;
- params.options = options;
- startActivity(ProxyActivityStarter.getLaunchIntent(this, params));
- } else {
- super.startIntentSenderForResult(intent, requestCode, fillInIntent, flagsMask,
- flagsValues, extraFlags, options);
- }
- }
-
- @Override
- public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
- if (requestCode != -1) {
- mPendingActivityRequestCode = requestCode;
- StartActivityParams params = new StartActivityParams(this, requestCode);
- params.intent = intent;
- params.options = options;
- startActivity(ProxyActivityStarter.getLaunchIntent(this, params));
- } else {
- super.startActivityForResult(intent, requestCode, options);
- }
- }
-
- @Override
- protected void onDeferredResumed() {
- super.onDeferredResumed();
- handlePendingActivityRequest();
- }
-
- @Override
- public void onStateSetEnd(LauncherState state) {
- super.onStateSetEnd(state);
- handlePendingActivityRequest();
- }
-
- private void handlePendingActivityRequest() {
- if (mPendingActivityRequestCode != -1 && isInState(NORMAL)
- && ((getActivityFlags() & ACTIVITY_STATE_DEFERRED_RESUMED) != 0)) {
- // Remove any active ProxyActivityStarter task and send RESULT_CANCELED to Launcher.
- onActivityResult(mPendingActivityRequestCode, RESULT_CANCELED, null);
- // ProxyActivityStarter is started with clear task to reset the task after which it
- // removes the task itself.
- startActivity(ProxyActivityStarter.getLaunchIntent(this, null));
- }
- }
-
- @Override
- protected void setupViews() {
- super.setupViews();
-
- mActionsView = findViewById(R.id.overview_actions_view);
- RecentsView overviewPanel = (RecentsView) getOverviewPanel();
- SplitSelectStateController controller =
- new SplitSelectStateController(this, mHandler, getStateManager(),
- getDepthController());
- overviewPanel.init(mActionsView, controller);
- mActionsView.updateDimension(getDeviceProfile(), overviewPanel.getLastComputedTaskSize());
- mActionsView.updateVerticalMargin(DisplayController.getNavigationMode(this));
-
- mAppTransitionManager = new QuickstepTransitionManager(this);
- mAppTransitionManager.registerRemoteAnimations();
- mAppTransitionManager.registerRemoteTransitions();
-
- mTISBindHelper = new TISBindHelper(this, this::onTISConnected);
- mDepthController = new DepthController(this);
- }
-
- private void onTISConnected(TISBinder binder) {
- mTaskbarManager = binder.getTaskbarManager();
- mTaskbarManager.setActivity(this);
- mOverviewCommandHelper = binder.getOverviewCommandHelper();
- }
-
- @Override
- public void runOnBindToTouchInteractionService(Runnable r) {
- mTISBindHelper.runOnBindToTouchInteractionService(r);
- }
-
- private void initUnfoldTransitionProgressProvider() {
- final UnfoldTransitionConfig config = new ResourceUnfoldTransitionConfig();
- if (config.isEnabled()) {
- mUnfoldTransitionProgressProvider =
- UnfoldTransitionFactory.createUnfoldTransitionProgressProvider(
- /* context= */ this,
- config,
- ProxyScreenStatusProvider.INSTANCE,
- new DeviceStateManagerFoldProvider(
- getSystemService(DeviceStateManager.class), /* context */this),
- new ActivityManagerActivityTypeProvider(
- getSystemService(ActivityManager.class)),
- getSystemService(SensorManager.class),
- getMainThreadHandler(),
- getMainExecutor(),
- /* backgroundExecutor= */ THREAD_POOL_EXECUTOR,
- /* tracingTagPrefix= */ "launcher"
- );
-
- mLauncherUnfoldAnimationController = new LauncherUnfoldAnimationController(
- this,
- getWindowManager(),
- mUnfoldTransitionProgressProvider
- );
- }
- }
-
- public void setTaskbarUIController(LauncherTaskbarUIController taskbarUIController) {
- mTaskbarUIController = taskbarUIController;
- }
-
- public @Nullable LauncherTaskbarUIController getTaskbarUIController() {
- return mTaskbarUIController;
- }
-
- public <T extends OverviewActionsView> T getActionsView() {
- return (T) mActionsView;
- }
-
- @Override
- protected void closeOpenViews(boolean animate) {
- super.closeOpenViews(animate);
- TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY);
- }
-
- @Override
- protected void collectStateHandlers(List<StateHandler> out) {
- super.collectStateHandlers(out);
- out.add(getDepthController());
- out.add(new RecentsViewStateController(this));
- out.add(new BackButtonAlphaHandler(this));
- }
-
- public DepthController getDepthController() {
- return mDepthController;
- }
-
- @Nullable
- public UnfoldTransitionProgressProvider getUnfoldTransitionProgressProvider() {
- return mUnfoldTransitionProgressProvider;
- }
-
- @Override
- public boolean supportsAdaptiveIconAnimation(View clickedView) {
- return mAppTransitionManager.hasControlRemoteAppTransitionPermission();
- }
-
- @Override
- public DragOptions getDefaultWorkspaceDragOptions() {
- if (mNextWorkspaceDragOptions != null) {
- DragOptions options = mNextWorkspaceDragOptions;
- mNextWorkspaceDragOptions = null;
- return options;
- }
- return super.getDefaultWorkspaceDragOptions();
- }
-
- public void setNextWorkspaceDragOptions(DragOptions dragOptions) {
- mNextWorkspaceDragOptions = dragOptions;
- }
-
- @Override
- public void useFadeOutAnimationForLauncherStart(CancellationSignal signal) {
- QuickstepTransitionManager appTransitionManager = getAppTransitionManager();
- appTransitionManager.setRemoteAnimationProvider(new RemoteAnimationProvider() {
- @Override
- public AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets) {
-
- // On the first call clear the reference.
- signal.cancel();
-
- ValueAnimator fadeAnimation = ValueAnimator.ofFloat(1, 0);
- fadeAnimation.addUpdateListener(new RemoteFadeOutAnimationListener(appTargets,
- wallpaperTargets));
- AnimatorSet anim = new AnimatorSet();
- anim.play(fadeAnimation);
- return anim;
- }
- }, signal);
- }
-
- @Override
- public float[] getNormalOverviewScaleAndOffset() {
- return DisplayController.getNavigationMode(this).hasGestures
- ? new float[] {1, 1} : new float[] {1.1f, NO_OFFSET};
- }
-
- @Override
- public void onDragLayerHierarchyChanged() {
- onLauncherStateOrFocusChanged();
- }
-
- @Override
- protected void onActivityFlagsChanged(int changeBits) {
- if ((changeBits
- & (ACTIVITY_STATE_WINDOW_FOCUSED | ACTIVITY_STATE_TRANSITION_ACTIVE)) != 0) {
- onLauncherStateOrFocusChanged();
- }
-
- if ((changeBits & ACTIVITY_STATE_STARTED) != 0) {
- mDepthController.setActivityStarted(isStarted());
- }
-
- if ((changeBits & ACTIVITY_STATE_RESUMED) != 0) {
- if (mTaskbarUIController != null) {
- mTaskbarUIController.onLauncherResumedOrPaused(hasBeenResumed());
- }
- }
-
- super.onActivityFlagsChanged(changeBits);
- }
-
- public boolean shouldBackButtonBeHidden(LauncherState toState) {
- NavigationMode mode = DisplayController.getNavigationMode(this);
- boolean shouldBackButtonBeHidden = mode.hasGestures
- && toState.hasFlag(FLAG_HIDE_BACK_BUTTON)
- && hasWindowFocus()
- && (getActivityFlags() & ACTIVITY_STATE_TRANSITION_ACTIVE) == 0;
- if (shouldBackButtonBeHidden) {
- // Show the back button if there is a floating view visible.
- shouldBackButtonBeHidden = AbstractFloatingView.getTopOpenViewWithType(this,
- TYPE_ALL & ~TYPE_HIDE_BACK_BUTTON) == null;
- }
- return shouldBackButtonBeHidden;
- }
-
- /**
- * Sets the back button visibility based on the current state/window focus.
- */
- private void onLauncherStateOrFocusChanged() {
- boolean shouldBackButtonBeHidden = shouldBackButtonBeHidden(getStateManager().getState());
- if (DisplayController.getNavigationMode(this) == TWO_BUTTONS) {
- UiThreadHelper.setBackButtonAlphaAsync(this, SET_BACK_BUTTON_ALPHA,
- shouldBackButtonBeHidden ? 0f : 1f, true /* animate */);
- }
- if (getDragLayer() != null) {
- getRootView().setDisallowBackGesture(shouldBackButtonBeHidden);
- }
- }
-
- @Override
- public void finishBindingItems(IntSet pagesBoundFirst) {
- super.finishBindingItems(pagesBoundFirst);
- // Instantiate and initialize WellbeingModel now that its loading won't interfere with
- // populating workspace.
- // TODO: Find a better place for this
- WellbeingModel.INSTANCE.get(this);
- }
-
- @Override
- public void onInitialBindComplete(IntSet boundPages, RunnableList pendingTasks) {
- pendingTasks.add(() -> {
- // This is added in pending task as we need to wait for views to be positioned
- // correctly before registering them for the animation.
- if (mLauncherUnfoldAnimationController != null) {
- // This is needed in case items are rebound while the unfold animation is in
- // progress.
- mLauncherUnfoldAnimationController.updateRegisteredViewsIfNeeded();
- }
- });
- super.onInitialBindComplete(boundPages, pendingTasks);
- }
-
- @Override
- public Stream<SystemShortcut.Factory> getSupportedShortcuts() {
- Stream<SystemShortcut.Factory> base = Stream.of(WellbeingModel.SHORTCUT_FACTORY);
- if (ENABLE_SPLIT_FROM_WORKSPACE.get() && mDeviceProfile.isTablet) {
- RecentsView recentsView = getOverviewPanel();
- // TODO: Pull it out of PagedOrentationHandler for split from workspace.
- List<SplitPositionOption> positions =
- recentsView.getPagedOrientationHandler().getSplitPositionOptions(
- mDeviceProfile);
- List<SystemShortcut.Factory<BaseQuickstepLauncher>> splitShortcuts = new ArrayList<>();
- for (SplitPositionOption position : positions) {
- splitShortcuts.add(getSplitSelectShortcutByPosition(position));
- }
- base = Stream.concat(base, splitShortcuts.stream());
- }
- return Stream.concat(base, super.getSupportedShortcuts());
- }
-
- @Override
- public ActivityOptionsWrapper getActivityLaunchOptions(View v, @Nullable ItemInfo item) {
- ActivityOptionsWrapper activityOptions =
- mAppTransitionManager.hasControlRemoteAppTransitionPermission()
- ? mAppTransitionManager.getActivityLaunchOptions(v)
- : super.getActivityLaunchOptions(v, item);
- if (mLastTouchUpTime > 0) {
- ActivityOptionsCompat.setLauncherSourceInfo(
- activityOptions.options, mLastTouchUpTime);
- }
- activityOptions.options.setSplashScreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_ICON);
- activityOptions.options.setLaunchDisplayId(
- (v != null && v.getDisplay() != null) ? v.getDisplay().getDisplayId()
- : Display.DEFAULT_DISPLAY);
- addLaunchCookie(item, activityOptions.options);
- return activityOptions;
- }
-
- /**
- * Adds a new launch cookie for the activity launch if supported.
- *
- * @param info the item info for the launch
- * @param opts the options to set the launchCookie on.
- */
- public void addLaunchCookie(ItemInfo info, ActivityOptions opts) {
- IBinder launchCookie = getLaunchCookie(info);
- if (launchCookie != null) {
- opts.setLaunchCookie(launchCookie);
- }
- }
-
- /**
- * Return a new launch cookie for the activity launch if supported.
- *
- * @param info the item info for the launch
- */
- public IBinder getLaunchCookie(ItemInfo info) {
- if (info == null) {
- return null;
- }
- switch (info.container) {
- case LauncherSettings.Favorites.CONTAINER_DESKTOP:
- case LauncherSettings.Favorites.CONTAINER_HOTSEAT:
- // Fall through and continue it's on the workspace (we don't support swiping back
- // to other containers like all apps or the hotseat predictions (which can change)
- break;
- default:
- if (info.container >= 0) {
- // Also allow swiping to folders
- break;
- }
- // Reset any existing launch cookies associated with the cookie
- return ObjectWrapper.wrap(NO_MATCHING_ID);
- }
- switch (info.itemType) {
- case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
- case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
- case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
- case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
- // Fall through and continue if it's an app, shortcut, or widget
- break;
- default:
- // Reset any existing launch cookies associated with the cookie
- return ObjectWrapper.wrap(NO_MATCHING_ID);
- }
- return ObjectWrapper.wrap(new Integer(info.id));
- }
-
- public void setHintUserWillBeActive() {
- addActivityFlags(ACTIVITY_STATE_USER_WILL_BE_ACTIVE);
- }
-
- @Override
- public void onDisplayInfoChanged(Context context, DisplayController.Info info, int flags) {
- super.onDisplayInfoChanged(context, info, flags);
- // When changing screens, force moving to rest state similar to StatefulActivity.onStop, as
- // StatefulActivity isn't called consistently.
- if ((flags & CHANGE_ACTIVE_SCREEN) != 0) {
- getStateManager().moveToRestState();
- }
-
- if ((flags & CHANGE_NAVIGATION_MODE) != 0) {
- getDragLayer().recreateControllers();
- if (mActionsView != null) {
- mActionsView.updateVerticalMargin(info.navigationMode);
- }
- }
- }
-
- @Override
- public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
- super.dump(prefix, fd, writer, args);
- if (mDepthController != null) {
- mDepthController.dump(prefix, writer);
- }
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
-
- // If Launcher shuts downs during split select, we save some extra data in the recovery
- // bundle to allow graceful recovery. The normal LauncherState restore mechanism doesn't
- // work in this case because restoring straight to OverviewSplitSelect without staging data,
- // or before the tasks themselves have loaded into Overview, causes a crash. So we tell
- // Launcher to first restore into Overview state, wait for the relevant tasks and icons to
- // load in, and then proceed to OverviewSplitSelect.
- if (isInState(OVERVIEW_SPLIT_SELECT)) {
- SplitSelectStateController splitSelectStateController =
- ((RecentsView) getOverviewPanel()).getSplitPlaceholder();
- // Launcher will restart in Overview and then transition to OverviewSplitSelect.
- outState.putIBinder(PENDING_SPLIT_SELECT_INFO, ObjectWrapper.wrap(
- new PendingSplitSelectInfo(
- splitSelectStateController.getInitialTaskId(),
- splitSelectStateController.getActiveSplitStagePosition()
- )
- ));
- outState.putInt(RUNTIME_STATE, OVERVIEW.ordinal);
- }
- }
-
- /**
- * When Launcher restarts, it sometimes needs to recover to a split selection state.
- * This function checks if such a recovery is needed.
- * @return a boolean representing whether the launcher is waiting to recover to
- * OverviewSplitSelect state.
- */
- public boolean hasPendingSplitSelectInfo() {
- return mPendingSplitSelectInfo != null;
- }
-
- /**
- * See {@link #hasPendingSplitSelectInfo()}
- */
- public @Nullable PendingSplitSelectInfo getPendingSplitSelectInfo() {
- return mPendingSplitSelectInfo;
- }
-
- /**
- * When the launcher has successfully recovered to OverviewSplitSelect state, this function
- * deletes the recovery data, returning it to a null state.
- */
- public void finishSplitSelectRecovery() {
- mPendingSplitSelectInfo = null;
- }
-}
diff --git a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
index 62603e9..95a94ec 100644
--- a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
+++ b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
@@ -28,13 +28,13 @@
import android.content.Context;
import android.os.Build;
import android.os.Handler;
+import android.view.RemoteAnimationTarget;
import androidx.annotation.BinderThread;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.lang.ref.WeakReference;
@@ -55,7 +55,7 @@
* reference to the runner, leaving only the weak ref from the runner.
*/
@TargetApi(Build.VERSION_CODES.P)
-public class LauncherAnimationRunner implements RemoteAnimationRunnerCompat {
+public class LauncherAnimationRunner extends RemoteAnimationRunnerCompat {
private static final RemoteAnimationFactory DEFAULT_FACTORY =
(transit, appTargets, wallpaperTargets, nonAppTargets, result) ->
@@ -82,9 +82,9 @@
@BinderThread
public void onAnimationStart(
int transit,
- RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets,
- RemoteAnimationTargetCompat[] nonAppTargets,
+ RemoteAnimationTarget[] appTargets,
+ RemoteAnimationTarget[] wallpaperTargets,
+ RemoteAnimationTarget[] nonAppTargets,
Runnable runnable) {
Runnable r = () -> {
finishExistingAnimation();
@@ -99,22 +99,6 @@
}
}
- // Called only in R platform
- @BinderThread
- public void onAnimationStart(RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets, Runnable runnable) {
- onAnimationStart(0 /* transit */, appTargets, wallpaperTargets,
- new RemoteAnimationTargetCompat[0], runnable);
- }
-
- // Called only in Q platform
- @BinderThread
- @Deprecated
- public void onAnimationStart(RemoteAnimationTargetCompat[] appTargets, Runnable runnable) {
- onAnimationStart(appTargets, new RemoteAnimationTargetCompat[0], runnable);
- }
-
-
private RemoteAnimationFactory getFactory() {
RemoteAnimationFactory factory = mFactory.get();
return factory != null ? factory : DEFAULT_FACTORY;
@@ -133,7 +117,7 @@
*/
@BinderThread
@Override
- public void onAnimationCancelled() {
+ public void onAnimationCancelled(boolean isKeyguardOccluded) {
postAsyncCallback(mHandler, () -> {
finishExistingAnimation();
getFactory().onAnimationCancelled();
@@ -229,9 +213,9 @@
* call {@link AnimationResult#setAnimation} with the target animation to be run.
*/
void onCreateAnimation(int transit,
- RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets,
- RemoteAnimationTargetCompat[] nonAppTargets,
+ RemoteAnimationTarget[] appTargets,
+ RemoteAnimationTarget[] wallpaperTargets,
+ RemoteAnimationTarget[] nonAppTargets,
LauncherAnimationRunner.AnimationResult result);
/**
diff --git a/quickstep/src/com/android/launcher3/LauncherInitListener.java b/quickstep/src/com/android/launcher3/LauncherInitListener.java
index 35151f1..28bd701 100644
--- a/quickstep/src/com/android/launcher3/LauncherInitListener.java
+++ b/quickstep/src/com/android/launcher3/LauncherInitListener.java
@@ -19,10 +19,11 @@
import android.annotation.TargetApi;
import android.os.Build;
import android.os.CancellationSignal;
+import android.view.RemoteAnimationTarget;
+import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.RemoteAnimationProvider;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.util.function.BiPredicate;
@@ -44,15 +45,15 @@
public boolean handleInit(Launcher launcher, boolean alreadyOnHome) {
if (mRemoteAnimationProvider != null) {
QuickstepTransitionManager appTransitionManager =
- ((BaseQuickstepLauncher) launcher).getAppTransitionManager();
+ ((QuickstepLauncher) launcher).getAppTransitionManager();
// Set a one-time animation provider. After the first call, this will get cleared.
// TODO: Probably also check the intended target id.
CancellationSignal cancellationSignal = new CancellationSignal();
appTransitionManager.setRemoteAnimationProvider(new RemoteAnimationProvider() {
@Override
- public AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets) {
+ public AnimatorSet createWindowAnimation(RemoteAnimationTarget[] appTargets,
+ RemoteAnimationTarget[] wallpaperTargets) {
// On the first call clear the reference.
cancellationSignal.cancel();
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index d348cc3..dcad873 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -16,9 +16,19 @@
package com.android.launcher3;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.provider.Settings.Secure.LAUNCHER_TASKBAR_EDUCATION_SHOWING;
+import static android.view.RemoteAnimationTarget.MODE_CLOSING;
+import static android.view.RemoteAnimationTarget.MODE_OPENING;
+import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.view.WindowManager.TRANSIT_TO_BACK;
+import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_NONE;
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN;
+import static android.window.TransitionFilter.CONTAINER_ORDER_TOP;
import static com.android.launcher3.BaseActivity.INVISIBLE_ALL;
import static com.android.launcher3.BaseActivity.INVISIBLE_BY_APP_TRANSITIONS;
@@ -42,21 +52,21 @@
import static com.android.launcher3.config.FeatureFlags.KEYGUARD_ANIMATION;
import static com.android.launcher3.config.FeatureFlags.SEPARATE_RECENTS_ACTIVITY;
import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID;
-import static com.android.launcher3.statehandlers.DepthController.DEPTH;
+import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
import static com.android.launcher3.views.FloatingIconView.getFloatingIconView;
import static com.android.quickstep.TaskViewUtils.findTaskViewToLaunch;
import static com.android.systemui.shared.system.QuickStepContract.getWindowCornerRadius;
import static com.android.systemui.shared.system.QuickStepContract.supportsRoundedCornersOnWindows;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
+import android.app.ActivityOptions;
+import android.app.WindowConfiguration;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -79,13 +89,19 @@
import android.util.Pair;
import android.util.Size;
import android.view.CrossWindowBlurListeners;
+import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationDefinition;
+import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.view.View;
import android.view.ViewRootImpl;
import android.view.ViewTreeObserver;
+import android.view.WindowManager;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
+import android.window.RemoteTransition;
+import android.window.TransitionFilter;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -95,6 +111,7 @@
import com.android.launcher3.LauncherAnimationRunner.RemoteAnimationFactory;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorListeners;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.icons.FastBitmapDrawable;
import com.android.launcher3.shortcuts.DeepShortcutView;
@@ -102,7 +119,9 @@
import com.android.launcher3.taskbar.LauncherTaskbarUIController;
import com.android.launcher3.testing.shared.ResourceUtils;
import com.android.launcher3.touch.PagedOrientationHandler;
+import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.launcher3.util.ActivityOptionsWrapper;
+import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.DynamicResource;
import com.android.launcher3.util.ObjectWrapper;
import com.android.launcher3.util.RunnableList;
@@ -118,22 +137,16 @@
import com.android.quickstep.util.RectFSpringAnim;
import com.android.quickstep.util.RemoteAnimationProvider;
import com.android.quickstep.util.StaggeredWorkspaceAnim;
+import com.android.quickstep.util.SurfaceTransaction;
+import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties;
import com.android.quickstep.util.SurfaceTransactionApplier;
import com.android.quickstep.util.WorkspaceRevealAnim;
import com.android.quickstep.views.FloatingWidgetView;
import com.android.quickstep.views.RecentsView;
-import com.android.systemui.shared.system.ActivityCompat;
-import com.android.systemui.shared.system.ActivityOptionsCompat;
import com.android.systemui.shared.system.BlurUtils;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
-import com.android.systemui.shared.system.RemoteAnimationDefinitionCompat;
import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.RemoteTransitionCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
-import com.android.systemui.shared.system.WindowManagerWrapper;
import com.android.wm.shell.startingsurface.IStartingWindowListener;
import java.util.ArrayList;
@@ -181,6 +194,7 @@
public static final int SPLIT_DIVIDER_ANIM_DURATION = 100;
public static final int CONTENT_ALPHA_DURATION = 217;
+ public static final int TRANSIENT_TASKBAR_TRANSITION_DURATION = 417;
public static final int TASKBAR_TO_APP_DURATION = 600;
// TODO(b/236145847): Tune TASKBAR_TO_HOME_DURATION to 383 after conflict with unlock animation
// is solved.
@@ -193,7 +207,7 @@
// Cross-fade duration between App Widget and App
private static final int WIDGET_CROSSFADE_DURATION_MILLIS = 125;
- protected final BaseQuickstepLauncher mLauncher;
+ protected final QuickstepLauncher mLauncher;
private final DragLayer mDragLayer;
final Handler mHandler;
@@ -212,7 +226,7 @@
private RemoteAnimationFactory mKeyguardGoingAwayRunner;
private RemoteAnimationFactory mWallpaperOpenTransitionRunner;
- private RemoteTransitionCompat mLauncherOpenTransition;
+ private RemoteTransition mLauncherOpenTransition;
private LauncherBackAnimationController mBackAnimationController;
private final AnimatorListenerAdapter mForceInvisibleListener = new AnimatorListenerAdapter() {
@@ -289,11 +303,11 @@
long statusBarTransitionDelay = duration - STATUS_BAR_TRANSITION_DURATION
- STATUS_BAR_TRANSITION_PRE_DELAY;
- RemoteAnimationAdapterCompat adapterCompat =
- new RemoteAnimationAdapterCompat(runner, duration, statusBarTransitionDelay,
- mLauncher.getIApplicationThread());
- return new ActivityOptionsWrapper(
- ActivityOptionsCompat.makeRemoteAnimation(adapterCompat), onEndCallback);
+ ActivityOptions options = ActivityOptions.makeRemoteAnimation(
+ new RemoteAnimationAdapter(runner, duration, statusBarTransitionDelay),
+ new RemoteTransition(runner.toRemoteTransition(),
+ mLauncher.getIApplicationThread()));
+ return new ActivityOptionsWrapper(options, onEndCallback);
}
/**
@@ -307,7 +321,7 @@
* @return true if the app is launching from recents, false if it most likely is not
*/
protected boolean isLaunchingFromRecents(@NonNull View v,
- @Nullable RemoteAnimationTargetCompat[] targets) {
+ @Nullable RemoteAnimationTarget[] targets) {
return mLauncher.getStateManager().getState().overviewUi
&& findTaskViewToLaunch(mLauncher.getOverviewPanel(), v, targets) != null;
}
@@ -321,18 +335,18 @@
* @param launcherClosing true if the launcher app is closing
*/
protected void composeRecentsLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
- @NonNull RemoteAnimationTargetCompat[] appTargets,
- @NonNull RemoteAnimationTargetCompat[] wallpaperTargets,
- @NonNull RemoteAnimationTargetCompat[] nonAppTargets, boolean launcherClosing) {
+ @NonNull RemoteAnimationTarget[] appTargets,
+ @NonNull RemoteAnimationTarget[] wallpaperTargets,
+ @NonNull RemoteAnimationTarget[] nonAppTargets, boolean launcherClosing) {
TaskViewUtils.composeRecentsLaunchAnimator(anim, v, appTargets, wallpaperTargets,
nonAppTargets, launcherClosing, mLauncher.getStateManager(),
mLauncher.getOverviewPanel(), mLauncher.getDepthController());
}
- private boolean areAllTargetsTranslucent(@NonNull RemoteAnimationTargetCompat[] targets) {
+ private boolean areAllTargetsTranslucent(@NonNull RemoteAnimationTarget[] targets) {
boolean isAllOpeningTargetTrs = true;
for (int i = 0; i < targets.length; i++) {
- RemoteAnimationTargetCompat target = targets[i];
+ RemoteAnimationTarget target = targets[i];
if (target.mode == MODE_OPENING) {
isAllOpeningTargetTrs &= target.isTranslucent;
}
@@ -350,21 +364,18 @@
* @param launcherClosing true if launcher is closing
*/
private void composeIconLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
- @NonNull RemoteAnimationTargetCompat[] appTargets,
- @NonNull RemoteAnimationTargetCompat[] wallpaperTargets,
- @NonNull RemoteAnimationTargetCompat[] nonAppTargets,
+ @NonNull RemoteAnimationTarget[] appTargets,
+ @NonNull RemoteAnimationTarget[] wallpaperTargets,
+ @NonNull RemoteAnimationTarget[] nonAppTargets,
boolean launcherClosing) {
// Set the state animation first so that any state listeners are called
// before our internal listeners.
mLauncher.getStateManager().setCurrentAnimation(anim);
- final int rotationChange = getRotationChange(appTargets);
// Note: the targetBounds are relative to the launcher
int startDelay = getSingleFrameMs(mLauncher);
- Rect windowTargetBounds = getWindowTargetBounds(appTargets, rotationChange);
- Animator windowAnimator = getOpeningWindowAnimators(v, appTargets, wallpaperTargets,
- nonAppTargets, windowTargetBounds, areAllTargetsTranslucent(appTargets),
- rotationChange);
+ Animator windowAnimator = getOpeningWindowAnimators(
+ v, appTargets, wallpaperTargets, nonAppTargets, launcherClosing);
windowAnimator.setStartDelay(startDelay);
anim.play(windowAnimator);
if (launcherClosing) {
@@ -378,40 +389,19 @@
launcherContentAnimator.second.run();
}
});
- } else {
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- mLauncher.addOnResumeCallback(() ->
- ObjectAnimator.ofFloat(mLauncher.getDepthController(), DEPTH,
- mLauncher.getStateManager().getState().getDepth(
- mLauncher)).start());
- }
- });
}
}
private void composeWidgetLaunchAnimator(
@NonNull AnimatorSet anim,
@NonNull LauncherAppWidgetHostView v,
- @NonNull RemoteAnimationTargetCompat[] appTargets,
- @NonNull RemoteAnimationTargetCompat[] wallpaperTargets,
- @NonNull RemoteAnimationTargetCompat[] nonAppTargets) {
+ @NonNull RemoteAnimationTarget[] appTargets,
+ @NonNull RemoteAnimationTarget[] wallpaperTargets,
+ @NonNull RemoteAnimationTarget[] nonAppTargets,
+ boolean launcherClosing) {
mLauncher.getStateManager().setCurrentAnimation(anim);
-
- Rect windowTargetBounds = getWindowTargetBounds(appTargets, getRotationChange(appTargets));
- anim.play(getOpeningWindowAnimatorsForWidget(v, appTargets, wallpaperTargets, nonAppTargets,
- windowTargetBounds, areAllTargetsTranslucent(appTargets)));
-
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- mLauncher.addOnResumeCallback(() ->
- ObjectAnimator.ofFloat(mLauncher.getDepthController(), DEPTH,
- mLauncher.getStateManager().getState().getDepth(
- mLauncher)).start());
- }
- });
+ anim.play(getOpeningWindowAnimatorsForWidget(
+ v, appTargets, wallpaperTargets, nonAppTargets, launcherClosing));
}
/**
@@ -419,10 +409,10 @@
* In multiwindow mode, we need to get the final size of the opening app window target to help
* figure out where the floating view should animate to.
*/
- private Rect getWindowTargetBounds(@NonNull RemoteAnimationTargetCompat[] appTargets,
+ private Rect getWindowTargetBounds(@NonNull RemoteAnimationTarget[] appTargets,
int rotationChange) {
- RemoteAnimationTargetCompat target = null;
- for (RemoteAnimationTargetCompat t : appTargets) {
+ RemoteAnimationTarget target = null;
+ for (RemoteAnimationTarget t : appTargets) {
if (t.mode != MODE_OPENING) continue;
target = t;
break;
@@ -444,7 +434,9 @@
4 - rotationChange);
}
}
- if (mDeviceProfile.isTaskbarPresentInApps && !target.willShowImeOnTarget) {
+ if (mDeviceProfile.isTaskbarPresentInApps
+ && !target.willShowImeOnTarget
+ && !DisplayController.isTransientTaskbar(mLauncher)) {
// Animate to above the taskbar.
bounds.bottom -= target.contentInsets.bottom;
}
@@ -548,7 +540,8 @@
final boolean scrimEnabled = ENABLE_SCRIM_FOR_APP_LAUNCH.get();
if (scrimEnabled) {
- boolean useTaskbarColor = mDeviceProfile.isTaskbarPresentInApps;
+ boolean useTaskbarColor = mDeviceProfile.isTaskbarPresentInApps
+ && !FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get();
int scrimColor = useTaskbarColor
? mLauncher.getResources().getColor(R.color.taskbar_background)
: Themes.getAttrColor(mLauncher, R.attr.overviewScrimColor);
@@ -643,10 +636,14 @@
* @return Animator that controls the window of the opening targets from app icons.
*/
private Animator getOpeningWindowAnimators(View v,
- RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets,
- RemoteAnimationTargetCompat[] nonAppTargets,
- Rect windowTargetBounds, boolean appTargetsAreTranslucent, int rotationChange) {
+ RemoteAnimationTarget[] appTargets,
+ RemoteAnimationTarget[] wallpaperTargets,
+ RemoteAnimationTarget[] nonAppTargets,
+ boolean launcherClosing) {
+ int rotationChange = getRotationChange(appTargets);
+ Rect windowTargetBounds = getWindowTargetBounds(appTargets, rotationChange);
+ boolean appTargetsAreTranslucent = areAllTargetsTranslucent(appTargets);
+
RectF launcherIconBounds = new RectF();
FloatingIconView floatingView = FloatingIconView.getFloatingIconView(mLauncher, v,
!appTargetsAreTranslucent, launcherIconBounds, true /* isOpening */);
@@ -658,7 +655,7 @@
SurfaceTransactionApplier surfaceApplier =
new SurfaceTransactionApplier(floatingView);
openingTargets.addReleaseCheck(surfaceApplier);
- RemoteAnimationTargetCompat navBarTarget = openingTargets.getNavBarRemoteAnimationTarget();
+ RemoteAnimationTarget navBarTarget = openingTargets.getNavBarRemoteAnimationTarget();
int[] dragLayerBounds = new int[2];
mDragLayer.getLocationOnScreen(dragLayerBounds);
@@ -810,10 +807,11 @@
return;
}
- ArrayList<SurfaceParams> params = new ArrayList<>();
+ SurfaceTransaction transaction = new SurfaceTransaction();
+
for (int i = appTargets.length - 1; i >= 0; i--) {
- RemoteAnimationTargetCompat target = appTargets[i];
- SurfaceParams.Builder builder = new SurfaceParams.Builder(target.leash);
+ RemoteAnimationTarget target = appTargets[i];
+ SurfaceProperties builder = transaction.forSurface(target.leash);
if (target.mode == MODE_OPENING) {
matrix.setScale(scale, scale);
@@ -834,14 +832,13 @@
floatingView.update(mIconAlpha.value, 255, floatingIconBounds, percent, 0f,
mWindowRadius.value * scale, true /* isOpening */);
- builder.withMatrix(matrix)
- .withWindowCrop(crop)
- .withAlpha(1f - mIconAlpha.value)
- .withCornerRadius(mWindowRadius.value)
- .withShadowRadius(mShadowRadius.value);
+ builder.setMatrix(matrix)
+ .setWindowCrop(crop)
+ .setAlpha(1f - mIconAlpha.value)
+ .setCornerRadius(mWindowRadius.value)
+ .setShadowRadius(mShadowRadius.value);
} else if (target.mode == MODE_CLOSING) {
if (target.localBounds != null) {
- final Rect localBounds = target.localBounds;
tmpPos.set(target.localBounds.left, target.localBounds.top);
} else {
tmpPos.set(target.position.x, target.position.y);
@@ -858,29 +855,26 @@
tmpPos.y = tmp;
}
matrix.setTranslate(tmpPos.x, tmpPos.y);
- builder.withMatrix(matrix)
- .withWindowCrop(crop)
- .withAlpha(1f);
+ builder.setMatrix(matrix)
+ .setWindowCrop(crop)
+ .setAlpha(1f);
}
- params.add(builder.build());
}
if (navBarTarget != null) {
- final SurfaceParams.Builder navBuilder =
- new SurfaceParams.Builder(navBarTarget.leash);
+ SurfaceProperties navBuilder =
+ transaction.forSurface(navBarTarget.leash);
if (mNavFadeIn.value > mNavFadeIn.getStartValue()) {
matrix.setScale(scale, scale);
matrix.postTranslate(windowTransX0, windowTransY0);
- navBuilder.withMatrix(matrix)
- .withWindowCrop(crop)
- .withAlpha(mNavFadeIn.value);
+ navBuilder.setMatrix(matrix)
+ .setWindowCrop(crop)
+ .setAlpha(mNavFadeIn.value);
} else {
- navBuilder.withAlpha(mNavFadeOut.value);
+ navBuilder.setAlpha(mNavFadeOut.value);
}
- params.add(navBuilder.build());
}
-
- surfaceApplier.scheduleApply(params.toArray(new SurfaceParams[params.size()]));
+ surfaceApplier.scheduleApply(transaction);
}
};
appAnimator.addUpdateListener(listener);
@@ -889,7 +883,7 @@
// If app targets are translucent, do not animate the background as it causes a visible
// flicker when it resets itself at the end of its animation.
- if (appTargetsAreTranslucent) {
+ if (appTargetsAreTranslucent || !launcherClosing) {
animatorSet.play(appAnimator);
} else {
animatorSet.playTogether(appAnimator, getBackgroundAnimator());
@@ -898,17 +892,19 @@
}
private Animator getOpeningWindowAnimatorsForWidget(LauncherAppWidgetHostView v,
- RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets,
- RemoteAnimationTargetCompat[] nonAppTargets, Rect windowTargetBounds,
- boolean appTargetsAreTranslucent) {
+ RemoteAnimationTarget[] appTargets,
+ RemoteAnimationTarget[] wallpaperTargets,
+ RemoteAnimationTarget[] nonAppTargets, boolean launcherClosing) {
+ Rect windowTargetBounds = getWindowTargetBounds(appTargets, getRotationChange(appTargets));
+ boolean appTargetsAreTranslucent = areAllTargetsTranslucent(appTargets);
+
final RectF widgetBackgroundBounds = new RectF();
final Rect appWindowCrop = new Rect();
final Matrix matrix = new Matrix();
RemoteAnimationTargets openingTargets = new RemoteAnimationTargets(appTargets,
wallpaperTargets, nonAppTargets, MODE_OPENING);
- RemoteAnimationTargetCompat openingTarget = openingTargets.getFirstAppTarget();
+ RemoteAnimationTarget openingTarget = openingTargets.getFirstAppTarget();
int fallbackBackgroundColor = 0;
if (openingTarget != null && supportsSSplashScreen()) {
fallbackBackgroundColor = mTaskStartParams.containsKey(openingTarget.taskId)
@@ -932,7 +928,7 @@
SurfaceTransactionApplier surfaceApplier = new SurfaceTransactionApplier(floatingView);
openingTargets.addReleaseCheck(surfaceApplier);
- RemoteAnimationTargetCompat navBarTarget = openingTargets.getNavBarRemoteAnimationTarget();
+ RemoteAnimationTarget navBarTarget = openingTargets.getNavBarRemoteAnimationTarget();
AnimatorSet animatorSet = new AnimatorSet();
ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
@@ -996,43 +992,39 @@
matrix.postScale(mAppWindowScale, mAppWindowScale, widgetBackgroundBounds.left,
widgetBackgroundBounds.top);
- ArrayList<SurfaceParams> params = new ArrayList<>();
+ SurfaceTransaction transaction = new SurfaceTransaction();
float floatingViewAlpha = appTargetsAreTranslucent ? 1 - mPreviewAlpha.value : 1;
for (int i = appTargets.length - 1; i >= 0; i--) {
- RemoteAnimationTargetCompat target = appTargets[i];
- SurfaceParams.Builder builder = new SurfaceParams.Builder(target.leash);
+ RemoteAnimationTarget target = appTargets[i];
+ SurfaceProperties builder = transaction.forSurface(target.leash);
if (target.mode == MODE_OPENING) {
floatingView.update(widgetBackgroundBounds, floatingViewAlpha,
mWidgetForegroundAlpha.value, mWidgetFallbackBackgroundAlpha.value,
mCornerRadiusProgress.value);
- builder.withMatrix(matrix)
- .withWindowCrop(appWindowCrop)
- .withAlpha(mPreviewAlpha.value)
- .withCornerRadius(mWindowRadius.value / mAppWindowScale);
+ builder.setMatrix(matrix)
+ .setWindowCrop(appWindowCrop)
+ .setAlpha(mPreviewAlpha.value)
+ .setCornerRadius(mWindowRadius.value / mAppWindowScale);
}
- params.add(builder.build());
}
if (navBarTarget != null) {
- final SurfaceParams.Builder navBuilder =
- new SurfaceParams.Builder(navBarTarget.leash);
+ SurfaceProperties navBuilder = transaction.forSurface(navBarTarget.leash);
if (mNavFadeIn.value > mNavFadeIn.getStartValue()) {
- navBuilder.withMatrix(matrix)
- .withWindowCrop(appWindowCrop)
- .withAlpha(mNavFadeIn.value);
+ navBuilder.setMatrix(matrix)
+ .setWindowCrop(appWindowCrop)
+ .setAlpha(mNavFadeIn.value);
} else {
- navBuilder.withAlpha(mNavFadeOut.value);
+ navBuilder.setAlpha(mNavFadeOut.value);
}
- params.add(navBuilder.build());
}
-
- surfaceApplier.scheduleApply(params.toArray(new SurfaceParams[params.size()]));
+ surfaceApplier.scheduleApply(transaction);
}
});
// If app targets are translucent, do not animate the background as it causes a visible
// flicker when it resets itself at the end of its animation.
- if (appTargetsAreTranslucent) {
+ if (appTargetsAreTranslucent || !launcherClosing) {
animatorSet.play(appAnimator);
} else {
animatorSet.playTogether(appAnimator, getBackgroundAnimator());
@@ -1050,8 +1042,8 @@
&& BlurUtils.supportsBlursOnWindows();
MyDepthController depthController = new MyDepthController(mLauncher);
- ObjectAnimator backgroundRadiusAnim = ObjectAnimator.ofFloat(depthController, DEPTH,
- BACKGROUND_APP.getDepth(mLauncher))
+ ObjectAnimator backgroundRadiusAnim = ObjectAnimator.ofFloat(depthController.stateDepth,
+ MULTI_PROPERTY_VALUE, BACKGROUND_APP.getDepth(mLauncher))
.setDuration(APP_LAUNCH_DURATION);
if (allowBlurringLauncher) {
@@ -1090,28 +1082,26 @@
if (hasControlRemoteAppTransitionPermission()) {
mWallpaperOpenRunner = createWallpaperOpenRunner(false /* fromUnlock */);
- RemoteAnimationDefinitionCompat definition = new RemoteAnimationDefinitionCompat();
- definition.addRemoteAnimation(WindowManagerWrapper.TRANSIT_WALLPAPER_OPEN,
- WindowManagerWrapper.ACTIVITY_TYPE_STANDARD,
- new RemoteAnimationAdapterCompat(
+ RemoteAnimationDefinition definition = new RemoteAnimationDefinition();
+ definition.addRemoteAnimation(WindowManager.TRANSIT_OLD_WALLPAPER_OPEN,
+ WindowConfiguration.ACTIVITY_TYPE_STANDARD,
+ new RemoteAnimationAdapter(
new LauncherAnimationRunner(mHandler, mWallpaperOpenRunner,
false /* startAtFrontOfQueue */),
- CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */,
- mLauncher.getIApplicationThread()));
+ CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */));
if (KEYGUARD_ANIMATION.get()) {
mKeyguardGoingAwayRunner = createWallpaperOpenRunner(true /* fromUnlock */);
definition.addRemoteAnimation(
- WindowManagerWrapper.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER,
- new RemoteAnimationAdapterCompat(
+ WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER,
+ new RemoteAnimationAdapter(
new LauncherAnimationRunner(
mHandler, mKeyguardGoingAwayRunner,
true /* startAtFrontOfQueue */),
- CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */,
- mLauncher.getIApplicationThread()));
+ CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */));
}
- new ActivityCompat(mLauncher).registerRemoteAnimations(definition);
+ mLauncher.registerRemoteAnimations(definition);
}
}
@@ -1124,11 +1114,25 @@
}
if (hasControlRemoteAppTransitionPermission()) {
mWallpaperOpenTransitionRunner = createWallpaperOpenRunner(false /* fromUnlock */);
- mLauncherOpenTransition = RemoteAnimationAdapterCompat.buildRemoteTransition(
+ mLauncherOpenTransition = new RemoteTransition(
new LauncherAnimationRunner(mHandler, mWallpaperOpenTransitionRunner,
- false /* startAtFrontOfQueue */), mLauncher.getIApplicationThread());
- mLauncherOpenTransition.addHomeOpenCheck(mLauncher.getComponentName());
- SystemUiProxy.INSTANCE.get(mLauncher).registerRemoteTransition(mLauncherOpenTransition);
+ false /* startAtFrontOfQueue */).toRemoteTransition(),
+ mLauncher.getIApplicationThread());
+
+ TransitionFilter homeCheck = new TransitionFilter();
+ // No need to handle the transition that also dismisses keyguard.
+ homeCheck.mNotFlags = TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
+ homeCheck.mRequirements =
+ new TransitionFilter.Requirement[]{new TransitionFilter.Requirement(),
+ new TransitionFilter.Requirement()};
+ homeCheck.mRequirements[0].mActivityType = ACTIVITY_TYPE_HOME;
+ homeCheck.mRequirements[0].mTopActivity = mLauncher.getComponentName();
+ homeCheck.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT};
+ homeCheck.mRequirements[0].mOrder = CONTAINER_ORDER_TOP;
+ homeCheck.mRequirements[1].mActivityType = ACTIVITY_TYPE_STANDARD;
+ homeCheck.mRequirements[1].mModes = new int[]{TRANSIT_CLOSE, TRANSIT_TO_BACK};
+ SystemUiProxy.INSTANCE.get(mLauncher)
+ .registerRemoteTransition(mLauncherOpenTransition, homeCheck);
}
if (mBackAnimationController != null) {
mBackAnimationController.registerBackCallbacks(mHandler);
@@ -1147,7 +1151,7 @@
return;
}
if (hasControlRemoteAppTransitionPermission()) {
- new ActivityCompat(mLauncher).unregisterRemoteAnimations();
+ mLauncher.unregisterRemoteAnimations();
// Also clear strong references to the runners registered with the remote animation
// definition so we don't have to wait for the system gc
@@ -1174,8 +1178,8 @@
}
}
- private boolean launcherIsATargetWithMode(RemoteAnimationTargetCompat[] targets, int mode) {
- for (RemoteAnimationTargetCompat target : targets) {
+ private boolean launcherIsATargetWithMode(RemoteAnimationTarget[] targets, int mode) {
+ for (RemoteAnimationTarget target : targets) {
if (target.mode == mode && target.taskInfo != null
// Compare component name instead of task-id because transitions will promote
// the target up to the root task while getTaskId returns the leaf.
@@ -1187,9 +1191,9 @@
return false;
}
- private boolean hasMultipleTargetsWithMode(RemoteAnimationTargetCompat[] targets, int mode) {
+ private boolean hasMultipleTargetsWithMode(RemoteAnimationTarget[] targets, int mode) {
int numTargets = 0;
- for (RemoteAnimationTargetCompat target : targets) {
+ for (RemoteAnimationTarget target : targets) {
if (target.mode == mode) {
numTargets++;
}
@@ -1211,8 +1215,8 @@
/**
* Animator that controls the transformations of the windows when unlocking the device.
*/
- private Animator getUnlockWindowAnimator(RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets) {
+ private Animator getUnlockWindowAnimator(RemoteAnimationTarget[] appTargets,
+ RemoteAnimationTarget[] wallpaperTargets) {
SurfaceTransactionApplier surfaceApplier = new SurfaceTransactionApplier(mDragLayer);
ValueAnimator unlockAnimator = ValueAnimator.ofFloat(0, 1);
unlockAnimator.setDuration(CLOSING_TRANSITION_DURATION_MS);
@@ -1221,24 +1225,23 @@
unlockAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
- SurfaceParams[] params = new SurfaceParams[appTargets.length];
+ SurfaceTransaction transaction = new SurfaceTransaction();
for (int i = appTargets.length - 1; i >= 0; i--) {
- RemoteAnimationTargetCompat target = appTargets[i];
- params[i] = new SurfaceParams.Builder(target.leash)
- .withAlpha(1f)
- .withWindowCrop(target.screenSpaceBounds)
- .withCornerRadius(cornerRadius)
- .build();
+ RemoteAnimationTarget target = appTargets[i];
+ transaction.forSurface(target.leash)
+ .setAlpha(1f)
+ .setWindowCrop(target.screenSpaceBounds)
+ .setCornerRadius(cornerRadius);
}
- surfaceApplier.scheduleApply(params);
+ surfaceApplier.scheduleApply(transaction);
}
});
return unlockAnimator;
}
- private static int getRotationChange(RemoteAnimationTargetCompat[] appTargets) {
+ private static int getRotationChange(RemoteAnimationTarget[] appTargets) {
int rotationChange = 0;
- for (RemoteAnimationTargetCompat target : appTargets) {
+ for (RemoteAnimationTarget target : appTargets) {
if (Math.abs(target.rotationChange) > Math.abs(rotationChange)) {
rotationChange = target.rotationChange;
}
@@ -1249,8 +1252,8 @@
/**
* Returns view on launcher that corresponds to the closing app in the list of app targets
*/
- private @Nullable View findLauncherView(RemoteAnimationTargetCompat[] appTargets) {
- for (RemoteAnimationTargetCompat appTarget : appTargets) {
+ private @Nullable View findLauncherView(RemoteAnimationTarget[] appTargets) {
+ for (RemoteAnimationTarget appTarget : appTargets) {
if (appTarget.mode == MODE_CLOSING) {
View launcherView = findLauncherView(appTarget);
if (launcherView != null) {
@@ -1264,7 +1267,7 @@
/**
* Returns view on launcher that corresponds to the {@param runningTaskTarget}.
*/
- private @Nullable View findLauncherView(RemoteAnimationTargetCompat runningTaskTarget) {
+ private @Nullable View findLauncherView(RemoteAnimationTarget runningTaskTarget) {
if (runningTaskTarget == null || runningTaskTarget.taskInfo == null) {
return null;
}
@@ -1325,15 +1328,15 @@
* Closing animator that animates the window into its final location on the workspace.
*/
private RectFSpringAnim getClosingWindowAnimators(AnimatorSet animation,
- RemoteAnimationTargetCompat[] targets, View launcherView, PointF velocityPxPerS,
+ RemoteAnimationTarget[] targets, View launcherView, PointF velocityPxPerS,
RectF closingWindowStartRect, float startWindowCornerRadius) {
FloatingIconView floatingIconView = null;
FloatingWidgetView floatingWidget = null;
RectF targetRect = new RectF();
- RemoteAnimationTargetCompat runningTaskTarget = null;
+ RemoteAnimationTarget runningTaskTarget = null;
boolean isTransluscent = false;
- for (RemoteAnimationTargetCompat target : targets) {
+ for (RemoteAnimationTarget target : targets) {
if (target.mode == MODE_CLOSING) {
runningTaskTarget = target;
isTransluscent = runningTaskTarget.isTranslucent;
@@ -1427,7 +1430,7 @@
/**
* Closing window animator that moves the window down and offscreen.
*/
- private Animator getFallbackClosingWindowAnimators(RemoteAnimationTargetCompat[] appTargets) {
+ private Animator getFallbackClosingWindowAnimators(RemoteAnimationTarget[] appTargets) {
final int rotationChange = getRotationChange(appTargets);
SurfaceTransactionApplier surfaceApplier = new SurfaceTransactionApplier(mDragLayer);
Matrix matrix = new Matrix();
@@ -1448,10 +1451,10 @@
@Override
public void onUpdate(float percent, boolean initOnly) {
- SurfaceParams[] params = new SurfaceParams[appTargets.length];
+ SurfaceTransaction transaction = new SurfaceTransaction();
for (int i = appTargets.length - 1; i >= 0; i--) {
- RemoteAnimationTargetCompat target = appTargets[i];
- SurfaceParams.Builder builder = new SurfaceParams.Builder(target.leash);
+ RemoteAnimationTarget target = appTargets[i];
+ SurfaceProperties builder = transaction.forSurface(target.leash);
if (target.localBounds != null) {
tmpPos.set(target.localBounds.left, target.localBounds.top);
@@ -1473,20 +1476,19 @@
tmpRect.centerY());
matrix.postTranslate(0, mDy.value);
matrix.postTranslate(tmpPos.x, tmpPos.y);
- builder.withMatrix(matrix)
- .withWindowCrop(crop)
- .withAlpha(mAlpha.value)
- .withCornerRadius(windowCornerRadius)
- .withShadowRadius(mShadowRadius.value);
+ builder.setMatrix(matrix)
+ .setWindowCrop(crop)
+ .setAlpha(mAlpha.value)
+ .setCornerRadius(windowCornerRadius)
+ .setShadowRadius(mShadowRadius.value);
} else if (target.mode == MODE_OPENING) {
matrix.setTranslate(tmpPos.x, tmpPos.y);
- builder.withMatrix(matrix)
- .withWindowCrop(crop)
- .withAlpha(1f);
+ builder.setMatrix(matrix)
+ .setWindowCrop(crop)
+ .setAlpha(1f);
}
- params[i] = builder.build();
}
- surfaceApplier.scheduleApply(params);
+ surfaceApplier.scheduleApply(transaction);
}
});
@@ -1550,8 +1552,8 @@
* the transition.
*/
public Pair<RectFSpringAnim, AnimatorSet> createWallpaperOpenAnimations(
- RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets,
+ RemoteAnimationTarget[] appTargets,
+ RemoteAnimationTarget[] wallpaperTargets,
boolean fromUnlock,
RectF startRect,
float startWindowCornerRadius) {
@@ -1593,7 +1595,8 @@
true /* animateOverviewScrim */, launcherView).getAnimators());
if (!areAllTargetsTranslucent(appTargets)) {
- anim.play(ObjectAnimator.ofFloat(mLauncher.getDepthController(), DEPTH,
+ anim.play(ObjectAnimator.ofFloat(mLauncher.getDepthController().stateDepth,
+ MULTI_PROPERTY_VALUE,
BACKGROUND_APP.getDepth(mLauncher), NORMAL.getDepth(mLauncher)));
}
@@ -1659,9 +1662,9 @@
@Override
public void onCreateAnimation(int transit,
- RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets,
- RemoteAnimationTargetCompat[] nonAppTargets,
+ RemoteAnimationTarget[] appTargets,
+ RemoteAnimationTarget[] wallpaperTargets,
+ RemoteAnimationTarget[] nonAppTargets,
LauncherAnimationRunner.AnimationResult result) {
if (mLauncher.isDestroyed()) {
AnimatorSet anim = new AnimatorSet();
@@ -1700,9 +1703,9 @@
@Override
public void onCreateAnimation(int transit,
- RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets,
- RemoteAnimationTargetCompat[] nonAppTargets,
+ RemoteAnimationTarget[] appTargets,
+ RemoteAnimationTarget[] wallpaperTargets,
+ RemoteAnimationTarget[] nonAppTargets,
LauncherAnimationRunner.AnimationResult result) {
AnimatorSet anim = new AnimatorSet();
boolean launcherClosing =
@@ -1713,7 +1716,7 @@
final boolean skipFirstFrame;
if (launchingFromWidget) {
composeWidgetLaunchAnimator(anim, (LauncherAppWidgetHostView) mV, appTargets,
- wallpaperTargets, nonAppTargets);
+ wallpaperTargets, nonAppTargets, launcherClosing);
addCujInstrumentation(
anim, InteractionJankMonitorWrapper.CUJ_APP_LAUNCH_FROM_WIDGET);
skipFirstFrame = true;
@@ -1829,7 +1832,7 @@
* RectFSpringAnim update listener to be used for app to home animation.
*/
private class SpringAnimRunner implements RectFSpringAnim.OnUpdateListener {
- private final RemoteAnimationTargetCompat[] mAppTargets;
+ private final RemoteAnimationTarget[] mAppTargets;
private final Matrix mMatrix = new Matrix();
private final Point mTmpPos = new Point();
private final Rect mCurrentRect = new Rect();
@@ -1840,7 +1843,7 @@
private final Rect mTmpRect = new Rect();
- SpringAnimRunner(RemoteAnimationTargetCompat[] appTargets, RectF targetRect,
+ SpringAnimRunner(RemoteAnimationTarget[] appTargets, RectF targetRect,
Rect windowTargetBounds, float startWindowCornerRadius) {
mAppTargets = appTargets;
mStartRadius = startWindowCornerRadius;
@@ -1855,10 +1858,10 @@
@Override
public void onUpdate(RectF currentRectF, float progress) {
- SurfaceParams[] params = new SurfaceParams[mAppTargets.length];
+ SurfaceTransaction transaction = new SurfaceTransaction();
for (int i = mAppTargets.length - 1; i >= 0; i--) {
- RemoteAnimationTargetCompat target = mAppTargets[i];
- SurfaceParams.Builder builder = new SurfaceParams.Builder(target.leash);
+ RemoteAnimationTarget target = mAppTargets[i];
+ SurfaceProperties builder = transaction.forSurface(target.leash);
if (target.localBounds != null) {
mTmpPos.set(target.localBounds.left, target.localBounds.top);
@@ -1893,18 +1896,17 @@
mMatrix.setScale(scale, scale);
mMatrix.postTranslate(mCurrentRect.left, mCurrentRect.top);
- builder.withMatrix(mMatrix)
- .withWindowCrop(mTmpRect)
- .withAlpha(getWindowAlpha(progress))
- .withCornerRadius(getCornerRadius(progress) / scale);
+ builder.setMatrix(mMatrix)
+ .setWindowCrop(mTmpRect)
+ .setAlpha(getWindowAlpha(progress))
+ .setCornerRadius(getCornerRadius(progress) / scale);
} else if (target.mode == MODE_OPENING) {
mMatrix.setTranslate(mTmpPos.x, mTmpPos.y);
- builder.withMatrix(mMatrix)
- .withAlpha(1f);
+ builder.setMatrix(mMatrix)
+ .setAlpha(1f);
}
- params[i] = builder.build();
}
- mSurfaceApplier.scheduleApply(params);
+ mSurfaceApplier.scheduleApply(transaction);
}
protected float getWindowAlpha(float progress) {
diff --git a/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java b/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java
index f42b39f..e8374b8 100644
--- a/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java
+++ b/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java
@@ -35,6 +35,7 @@
import com.android.launcher3.R;
import com.android.launcher3.allapps.FloatingHeaderRow;
import com.android.launcher3.allapps.FloatingHeaderView;
+import com.android.launcher3.util.OnboardingPrefs;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ActivityContext;
@@ -92,8 +93,10 @@
? R.color.all_apps_label_text_dark
: R.color.all_apps_label_text);
- mShowAllAppsLabel = !ActivityContext.lookupContext(
- getContext()).getOnboardingPrefs().hasReachedMaxCount(ALL_APPS_VISITED_COUNT);
+ OnboardingPrefs<?> onboardingPrefs = ActivityContext.lookupContext(
+ getContext()).getOnboardingPrefs();
+ mShowAllAppsLabel = onboardingPrefs == null || !onboardingPrefs.hasReachedMaxCount(
+ ALL_APPS_VISITED_COUNT);
}
public void setup(FloatingHeaderView parent, FloatingHeaderRow[] rows, boolean tabsHidden) {
@@ -216,8 +219,8 @@
CharSequence allAppsLabelText = getResources().getText(R.string.all_apps_label);
mAllAppsLabelLayout = StaticLayout.Builder.obtain(
- allAppsLabelText, 0, allAppsLabelText.length(), mPaint,
- Math.round(mPaint.measureText(allAppsLabelText.toString())))
+ allAppsLabelText, 0, allAppsLabelText.length(), mPaint,
+ Math.round(mPaint.measureText(allAppsLabelText.toString())))
.setAlignment(Layout.Alignment.ALIGN_CENTER)
.setMaxLines(1)
.setIncludePad(true)
diff --git a/quickstep/src/com/android/launcher3/appprediction/InstantAppItemInfo.java b/quickstep/src/com/android/launcher3/appprediction/InstantAppItemInfo.java
index 9c3b881..8baee00 100644
--- a/quickstep/src/com/android/launcher3/appprediction/InstantAppItemInfo.java
+++ b/quickstep/src/com/android/launcher3/appprediction/InstantAppItemInfo.java
@@ -22,6 +22,8 @@
import android.content.Context;
import android.content.Intent;
+import androidx.annotation.NonNull;
+
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -33,11 +35,13 @@
this.componentName = new ComponentName(packageName, COMPONENT_CLASS_MARKER);
}
+ @NonNull
@Override
public ComponentName getTargetComponent() {
return componentName;
}
+ @NonNull
@Override
public WorkspaceItemInfo makeWorkspaceItem(Context context) {
WorkspaceItemInfo workspaceItemInfo = super.makeWorkspaceItem(context);
diff --git a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
index 351a3bc..c54d119 100644
--- a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
+++ b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
@@ -33,6 +33,7 @@
import com.android.launcher3.DeviceProfile.DeviceProfileListenable;
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.FloatingHeaderRow;
import com.android.launcher3.allapps.FloatingHeaderView;
import com.android.launcher3.anim.AlphaUpdateListener;
@@ -117,9 +118,14 @@
@Override
public int getExpectedHeight() {
- return getVisibility() == GONE ? 0
- : mActivityContext.getDeviceProfile().allAppsCellHeightPx + getPaddingTop()
- + getPaddingBottom();
+ DeviceProfile deviceProfile = mActivityContext.getDeviceProfile();
+ int iconHeight = deviceProfile.allAppsIconSizePx;
+ int iconPadding = deviceProfile.allAppsIconDrawablePaddingPx;
+ int textHeight = Utilities.calculateTextHeight(deviceProfile.allAppsIconTextSizePx);
+ int verticalPadding = getResources().getDimensionPixelSize(
+ R.dimen.all_apps_predicted_icon_vertical_padding);
+ int totalHeight = iconHeight + iconPadding + textHeight + verticalPadding * 2;
+ return getVisibility() == GONE ? 0 : totalHeight + getPaddingTop() + getPaddingBottom();
}
@Override
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
index d63bc18..048243e 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
@@ -26,22 +26,17 @@
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.CellLayout;
import com.android.launcher3.Hotseat;
-import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
-import com.android.launcher3.util.GridOccupancy;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.views.ArrowTipView;
import com.android.launcher3.views.Snackbar;
-import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.IntStream;
@@ -74,102 +69,13 @@
*/
void migrate() {
HotseatRestoreHelper.createBackup(mLauncher);
- if (FeatureFlags.HOTSEAT_MIGRATE_TO_FOLDER.get()) {
- migrateToFolder();
- } else {
- migrateHotseatWhole();
- }
+ migrateHotseatWhole();
Snackbar.show(mLauncher, R.string.hotsaet_tip_prediction_enabled,
R.string.hotseat_prediction_settings, null,
() -> mLauncher.startActivity(getSettingsIntent()));
}
/**
- * This migration places all non folder items in the hotseat into a folder and then moves
- * all folders in the hotseat to a workspace page that has enough empty spots.
- *
- * @return pageId that has accepted the items.
- */
- private int migrateToFolder() {
- ArrayDeque<FolderInfo> folders = new ArrayDeque<>();
- ArrayList<WorkspaceItemInfo> putIntoFolder = new ArrayList<>();
-
- //separate folders and items that can get in folders
- for (int i = 0; i < mLauncher.getDeviceProfile().numShownHotseatIcons; i++) {
- View view = mHotseat.getChildAt(i, 0);
- if (view == null) continue;
- ItemInfo info = (ItemInfo) view.getTag();
- if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
- folders.add((FolderInfo) info);
- } else if (info instanceof WorkspaceItemInfo && info.container == LauncherSettings
- .Favorites.CONTAINER_HOTSEAT) {
- putIntoFolder.add((WorkspaceItemInfo) info);
- }
- }
-
- // create a temp folder and add non folder items to it
- if (!putIntoFolder.isEmpty()) {
- ItemInfo firstItem = putIntoFolder.get(0);
- FolderInfo folderInfo = new FolderInfo();
- mLauncher.getModelWriter().addItemToDatabase(folderInfo, firstItem.container,
- firstItem.screenId, firstItem.cellX, firstItem.cellY);
- folderInfo.setTitle("", mLauncher.getModelWriter());
- folderInfo.contents.addAll(putIntoFolder);
- for (int i = 0; i < folderInfo.contents.size(); i++) {
- ItemInfo item = folderInfo.contents.get(i);
- item.rank = i;
- mLauncher.getModelWriter().moveItemInDatabase(item, folderInfo.id, 0,
- item.cellX, item.cellY);
- }
- folders.add(folderInfo);
- }
- mNewItems.addAll(folders);
-
- return placeFoldersInWorkspace(folders);
- }
-
- private int placeFoldersInWorkspace(ArrayDeque<FolderInfo> folders) {
- if (folders.isEmpty()) return 0;
-
- Workspace<?> workspace = mLauncher.getWorkspace();
- InvariantDeviceProfile idp = mLauncher.getDeviceProfile().inv;
-
- GridOccupancy[] occupancyList = new GridOccupancy[workspace.getChildCount()];
- for (int i = 0; i < occupancyList.length; i++) {
- occupancyList[i] = ((CellLayout) workspace.getChildAt(i)).cloneGridOccupancy();
- }
- //scan every screen to find available spots to place folders
- int occupancyIndex = 0;
- int[] itemXY = new int[2];
- while (occupancyIndex < occupancyList.length && !folders.isEmpty()) {
- GridOccupancy occupancy = occupancyList[occupancyIndex];
- if (occupancy.findVacantCell(itemXY, 1, 1)) {
- FolderInfo info = folders.poll();
- mLauncher.getModelWriter().moveItemInDatabase(info,
- LauncherSettings.Favorites.CONTAINER_DESKTOP,
- workspace.getScreenIdForPageIndex(occupancyIndex), itemXY[0], itemXY[1]);
- occupancy.markCells(info, true);
- } else {
- occupancyIndex++;
- }
- }
- if (folders.isEmpty()) return workspace.getScreenIdForPageIndex(occupancyIndex);
- int screenId = LauncherSettings.Settings.call(mLauncher.getContentResolver(),
- LauncherSettings.Settings.METHOD_NEW_SCREEN_ID)
- .getInt(LauncherSettings.Settings.EXTRA_VALUE);
- // if all screens are full and we still have folders left, put those on a new page
- FolderInfo folderInfo;
- int col = 0;
- while ((folderInfo = folders.poll()) != null) {
- mLauncher.getModelWriter().moveItemInDatabase(folderInfo,
- LauncherSettings.Favorites.CONTAINER_DESKTOP, screenId, col++,
- idp.numRows - 1);
- }
- mNewScreens = IntArray.wrap(screenId);
- return workspace.getPageCount();
- }
-
- /**
* This migration option attempts to move the entire hotseat up to the first workspace that
* has space to host items. If no such page is found, it moves items to a new page.
*
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
index 7b48332..80bdb6f 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
@@ -15,8 +15,7 @@
*/
package com.android.launcher3.hybridhotseat;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent
- .LAUNCHER_HOTSEAT_EDU_ACCEPT;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_EDU_ACCEPT;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_EDU_DENY;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_EDU_SEEN;
@@ -39,7 +38,7 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.uioverrides.PredictedAppIcon;
import com.android.launcher3.views.AbstractSlideInView;
@@ -112,12 +111,6 @@
((LinearLayout.LayoutParams) buttonContainer.getLayoutParams()).setMarginEnd(
adjustedMarginEnd);
}
-
- // update ui to reflect which migration method is going to be used
- if (FeatureFlags.HOTSEAT_MIGRATE_TO_FOLDER.get()) {
- ((TextView) findViewById(R.id.hotseat_edu_content)).setText(
- R.string.hotseat_edu_message_migrate_alt);
- }
}
private void onAccept(View v) {
@@ -200,7 +193,7 @@
icon.setEnabled(false);
icon.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
icon.verifyHighRes();
- CellLayout.LayoutParams lp = new CellLayout.LayoutParams(i, 0, 1, 1);
+ CellLayoutLayoutParams lp = new CellLayoutLayoutParams(i, 0, 1, 1);
mSampleHotseat.addViewToCellLayout(icon, i, info.getViewId(), lp, true);
}
}
diff --git a/quickstep/src/com/android/launcher3/model/PredictionUpdateTask.java b/quickstep/src/com/android/launcher3/model/PredictionUpdateTask.java
index 7e3ee7d..bc3253f 100644
--- a/quickstep/src/com/android/launcher3/model/PredictionUpdateTask.java
+++ b/quickstep/src/com/android/launcher3/model/PredictionUpdateTask.java
@@ -27,6 +27,8 @@
import android.content.pm.ShortcutInfo;
import android.os.UserHandle;
+import androidx.annotation.NonNull;
+
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.Utilities;
import com.android.launcher3.model.BgDataModel.FixedContainerItems;
@@ -52,7 +54,8 @@
}
@Override
- public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+ public void execute(@NonNull final LauncherAppState app, @NonNull final BgDataModel dataModel,
+ @NonNull final AllAppsList apps) {
Context context = app.getContext();
// TODO: remove this
diff --git a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
index 770dfb2..de0b14d 100644
--- a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
+++ b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
@@ -117,14 +117,15 @@
// TODO: Implement caching and preloading
super.loadItems(ums, pinnedShortcuts);
- WorkspaceItemFactory allAppsFactory = new WorkspaceItemFactory(
- mApp, ums, pinnedShortcuts, mIDP.numDatabaseAllAppsColumns);
- FixedContainerItems allAppsItems = new FixedContainerItems(mAllAppsState.containerId,
- mAllAppsState.storage.read(mApp.getContext(), allAppsFactory, ums.allUsers::get));
- mDataModel.extraItems.put(mAllAppsState.containerId, allAppsItems);
+ WorkspaceItemFactory allAppsFactory = new WorkspaceItemFactory(mApp, ums, pinnedShortcuts,
+ mIDP.numDatabaseAllAppsColumns, mAllAppsState.containerId);
+ FixedContainerItems allAppsPredictionItems = new FixedContainerItems(
+ mAllAppsState.containerId, mAllAppsState.storage.read(mApp.getContext(),
+ allAppsFactory, ums.allUsers::get));
+ mDataModel.extraItems.put(mAllAppsState.containerId, allAppsPredictionItems);
- WorkspaceItemFactory hotseatFactory =
- new WorkspaceItemFactory(mApp, ums, pinnedShortcuts, mIDP.numDatabaseHotseatIcons);
+ WorkspaceItemFactory hotseatFactory = new WorkspaceItemFactory(mApp, ums, pinnedShortcuts,
+ mIDP.numDatabaseHotseatIcons, mHotseatState.containerId);
FixedContainerItems hotseatItems = new FixedContainerItems(mHotseatState.containerId,
mHotseatState.storage.read(mApp.getContext(), hotseatFactory, ums.allUsers::get));
mDataModel.extraItems.put(mHotseatState.containerId, hotseatItems);
@@ -432,15 +433,17 @@
private final UserManagerState mUMS;
private final Map<ShortcutKey, ShortcutInfo> mPinnedShortcuts;
private final int mMaxCount;
+ private final int mContainer;
private int mReadCount = 0;
protected WorkspaceItemFactory(LauncherAppState appState, UserManagerState ums,
- Map<ShortcutKey, ShortcutInfo> pinnedShortcuts, int maxCount) {
+ Map<ShortcutKey, ShortcutInfo> pinnedShortcuts, int maxCount, int container) {
mAppState = appState;
mUMS = ums;
mPinnedShortcuts = pinnedShortcuts;
mMaxCount = maxCount;
+ mContainer = container;
}
@Nullable
@@ -458,6 +461,7 @@
return null;
}
AppInfo info = new AppInfo(lai, user, mUMS.isUserQuiet(user));
+ info.container = mContainer;
mAppState.getIconCache().getTitleAndIcon(info, lai, false);
mReadCount++;
return info.makeWorkspaceItem(mAppState.getContext());
@@ -472,6 +476,7 @@
return null;
}
WorkspaceItemInfo wii = new WorkspaceItemInfo(si, mAppState.getContext());
+ wii.container = mContainer;
mAppState.getIconCache().getShortcutIcon(wii, si);
mReadCount++;
return wii;
diff --git a/quickstep/src/com/android/launcher3/model/WellbeingModel.java b/quickstep/src/com/android/launcher3/model/WellbeingModel.java
index 68ed682..fb2d0dc 100644
--- a/quickstep/src/com/android/launcher3/model/WellbeingModel.java
+++ b/quickstep/src/com/android/launcher3/model/WellbeingModel.java
@@ -18,8 +18,6 @@
import static android.content.ContentResolver.SCHEME_CONTENT;
-import static com.android.launcher3.Utilities.newContentObserver;
-
import android.annotation.TargetApi;
import android.app.RemoteAction;
import android.content.ContentProviderClient;
diff --git a/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java b/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
index 9cd9d85..1beabf1 100644
--- a/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
+++ b/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
@@ -18,20 +18,22 @@
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION;
import android.app.prediction.AppTarget;
-import android.content.ComponentName;
import android.text.TextUtils;
+import androidx.annotation.NonNull;
+
import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.BgDataModel.FixedContainerItems;
import com.android.launcher3.model.QuickstepModelDelegate.PredictorState;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.widget.PendingAddWidgetInfo;
+import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.function.Predicate;
import java.util.stream.Collectors;
/** Task to update model as a result of predicted widgets update */
@@ -52,54 +54,48 @@
* workspace.
*/
@Override
- public void execute(LauncherAppState appState, BgDataModel dataModel, AllAppsList apps) {
+ public void execute(@NonNull final LauncherAppState appState,
+ @NonNull final BgDataModel dataModel, @NonNull final AllAppsList apps) {
Set<ComponentKey> widgetsInWorkspace = dataModel.appWidgets.stream().map(
widget -> new ComponentKey(widget.providerName, widget.user)).collect(
Collectors.toSet());
+ Predicate<WidgetItem> notOnWorkspace = w -> !widgetsInWorkspace.contains(w);
Map<PackageUserKey, List<WidgetItem>> allWidgets =
dataModel.widgetsModel.getAllWidgetsWithoutShortcuts();
- FixedContainerItems fixedContainerItems =
- new FixedContainerItems(mPredictorState.containerId);
+ List<WidgetItem> servicePredictedItems = new ArrayList<>();
+ List<WidgetItem> localFilteredWidgets = new ArrayList<>();
- if (FeatureFlags.ENABLE_LOCAL_RECOMMENDED_WIDGETS_FILTER.get()) {
- for (AppTarget app : mTargets) {
- PackageUserKey packageUserKey = new PackageUserKey(app.getPackageName(),
- app.getUser());
- if (allWidgets.containsKey(packageUserKey)) {
- List<WidgetItem> notAddedWidgets = allWidgets.get(packageUserKey).stream()
- .filter(item ->
- !widgetsInWorkspace.contains(
- new ComponentKey(item.componentName, item.user)))
- .collect(Collectors.toList());
- if (notAddedWidgets.size() > 0) {
- // Even an apps have more than one widgets, we only include one widget.
- fixedContainerItems.items.add(
- new PendingAddWidgetInfo(
- notAddedWidgets.get(0).widgetInfo,
- CONTAINER_WIDGETS_PREDICTION));
- }
- }
+ for (AppTarget app : mTargets) {
+ PackageUserKey packageUserKey = new PackageUserKey(app.getPackageName(), app.getUser());
+ List<WidgetItem> widgets = allWidgets.get(packageUserKey);
+ if (widgets == null || widgets.isEmpty()) {
+ continue;
}
- } else {
- Map<ComponentKey, WidgetItem> widgetItems =
- allWidgets.values().stream().flatMap(List::stream).distinct()
- .collect(Collectors.toMap(widget -> (ComponentKey) widget,
- widget -> widget));
- for (AppTarget app : mTargets) {
- if (TextUtils.isEmpty(app.getClassName())) {
+ String className = app.getClassName();
+ if (!TextUtils.isEmpty(className)) {
+ WidgetItem item = widgets.stream()
+ .filter(w -> className.equals(w.componentName.getClassName()))
+ .filter(notOnWorkspace)
+ .findFirst()
+ .orElse(null);
+ if (item != null) {
+ servicePredictedItems.add(item);
continue;
}
- ComponentKey targetWidget = new ComponentKey(
- new ComponentName(app.getPackageName(), app.getClassName()), app.getUser());
- if (widgetItems.containsKey(targetWidget)) {
- fixedContainerItems.items.add(
- new PendingAddWidgetInfo(widgetItems.get(
- targetWidget).widgetInfo,
- CONTAINER_WIDGETS_PREDICTION));
- }
}
+ // No widget was added by the service, try local filtering
+ widgets.stream().filter(notOnWorkspace).findFirst()
+ .ifPresent(localFilteredWidgets::add);
}
+ if (servicePredictedItems.isEmpty()) {
+ servicePredictedItems.addAll(localFilteredWidgets);
+ }
+ FixedContainerItems fixedContainerItems =
+ new FixedContainerItems(mPredictorState.containerId);
+ servicePredictedItems.forEach(w -> fixedContainerItems.items.add(
+ new PendingAddWidgetInfo(w.widgetInfo, CONTAINER_WIDGETS_PREDICTION)));
+
dataModel.extraItems.put(mPredictorState.containerId, fixedContainerItems);
bindExtraContainerItems(fixedContainerItems);
diff --git a/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java b/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java
index 4e59790..7cf8201 100644
--- a/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java
+++ b/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java
@@ -15,43 +15,65 @@
*/
package com.android.launcher3.popup;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE;
+import static com.android.launcher3.util.SplitConfigurationOptions.getLogEventForPosition;
+import static com.android.quickstep.util.SplitAnimationTimings.TABLET_HOME_TO_SPLIT;
+
import android.content.Intent;
import android.graphics.Bitmap;
+import android.graphics.Rect;
+import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.Log;
import android.view.View;
-import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.R;
+import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
+import com.android.quickstep.util.SplitSelectStateController;
+import com.android.quickstep.views.FloatingTaskView;
import com.android.quickstep.views.RecentsView;
public interface QuickstepSystemShortcut {
String TAG = QuickstepSystemShortcut.class.getSimpleName();
- static SystemShortcut.Factory<BaseQuickstepLauncher> getSplitSelectShortcutByPosition(
+ static SystemShortcut.Factory<QuickstepLauncher> getSplitSelectShortcutByPosition(
SplitPositionOption position) {
return (activity, itemInfo, originalView) ->
new QuickstepSystemShortcut.SplitSelectSystemShortcut(activity, itemInfo,
originalView, position);
}
- class SplitSelectSystemShortcut extends SystemShortcut<BaseQuickstepLauncher> {
+ class SplitSelectSystemShortcut extends SystemShortcut<QuickstepLauncher> {
+ private final int mSplitPlaceholderSize;
+ private final int mSplitPlaceholderInset;
+
+ private final Rect mTempRect = new Rect();
private final SplitPositionOption mPosition;
- public SplitSelectSystemShortcut(BaseQuickstepLauncher launcher, ItemInfo itemInfo,
+ public SplitSelectSystemShortcut(QuickstepLauncher launcher, ItemInfo itemInfo,
View originalView, SplitPositionOption position) {
super(position.iconResId, position.textResId, launcher, itemInfo, originalView);
mPosition = position;
+
+ mSplitPlaceholderSize = launcher.getResources().getDimensionPixelSize(
+ R.dimen.split_placeholder_size);
+ mSplitPlaceholderInset = launcher.getResources().getDimensionPixelSize(
+ R.dimen.split_placeholder_inset);
}
@Override
public void onClick(View view) {
+ // Initiate splitscreen from the Home screen or Home All Apps
Bitmap bitmap;
Intent intent;
if (mItemInfo instanceof WorkspaceItemInfo) {
@@ -68,10 +90,39 @@
return;
}
+ StatsLogManager.EventEnum splitEvent = getLogEventForPosition(mPosition.stagePosition);
+ SplitSelectSource source = new SplitSelectSource(mOriginalView,
+ new BitmapDrawable(bitmap), intent, mPosition, mItemInfo, splitEvent);
+ if (ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
+ startSplitToHome(source);
+ } else {
+ RecentsView recentsView = mTarget.getOverviewPanel();
+ recentsView.initiateSplitSelect(source);
+ }
+ }
+
+ private void startSplitToHome(SplitSelectSource source) {
+ AbstractFloatingView.closeAllOpenViews(mTarget);
+
+ SplitSelectStateController controller = mTarget.getSplitSelectStateController();
+ controller.setInitialTaskSelect(source.intent, source.position.stagePosition,
+ source.itemInfo, source.splitEvent);
+
RecentsView recentsView = mTarget.getOverviewPanel();
- recentsView.initiateSplitSelect(
- new SplitSelectSource(mOriginalView, new BitmapDrawable(bitmap), intent,
- mPosition));
+ recentsView.getPagedOrientationHandler().getInitialSplitPlaceholderBounds(
+ mSplitPlaceholderSize, mSplitPlaceholderInset, mTarget.getDeviceProfile(),
+ controller.getActiveSplitStagePosition(), mTempRect);
+
+ PendingAnimation anim = new PendingAnimation(TABLET_HOME_TO_SPLIT.getDuration());
+ RectF startingTaskRect = new RectF();
+ FloatingTaskView floatingTaskView = FloatingTaskView.getFloatingTaskView(mTarget,
+ source.view, null /* thumbnail */,
+ source.drawable, startingTaskRect);
+ floatingTaskView.setAlpha(1);
+ floatingTaskView.addStagingAnimation(anim, startingTaskRect, mTempRect,
+ false /* fadeWithThumbnail */, true /* isStagedTask */);
+ controller.setFirstFloatingTaskView(floatingTaskView);
+ anim.buildAnim().start();
}
}
@@ -81,13 +132,18 @@
public final Drawable drawable;
public final Intent intent;
public final SplitPositionOption position;
+ public final ItemInfo itemInfo;
+ public final StatsLogManager.EventEnum splitEvent;
public SplitSelectSource(View view, Drawable drawable, Intent intent,
- SplitPositionOption position) {
+ SplitPositionOption position, ItemInfo itemInfo,
+ StatsLogManager.EventEnum splitEvent) {
this.view = view;
this.drawable = drawable;
this.intent = intent;
this.position = position;
+ this.itemInfo = itemInfo;
+ this.splitEvent = splitEvent;
}
}
}
diff --git a/quickstep/src/com/android/launcher3/secondarydisplay/SecondaryDisplayPredictionsImpl.java b/quickstep/src/com/android/launcher3/secondarydisplay/SecondaryDisplayPredictionsImpl.java
new file mode 100644
index 0000000..8720bd8
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/secondarydisplay/SecondaryDisplayPredictionsImpl.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.secondarydisplay;
+
+import static com.android.launcher3.util.OnboardingPrefs.ALL_APPS_VISITED_COUNT;
+
+import android.content.Context;
+import android.view.View;
+
+import com.android.launcher3.allapps.ActivityAllAppsContainerView;
+import com.android.launcher3.appprediction.AppsDividerView;
+import com.android.launcher3.appprediction.PredictionRowView;
+import com.android.launcher3.model.BgDataModel;
+import com.android.launcher3.util.OnboardingPrefs;
+import com.android.launcher3.views.ActivityContext;
+
+/**
+ * Implementation of SecondaryDisplayPredictions.
+ */
+@SuppressWarnings("unused")
+public final class SecondaryDisplayPredictionsImpl extends SecondaryDisplayPredictions {
+ private final ActivityContext mActivityContext;
+
+ public SecondaryDisplayPredictionsImpl(Context context) {
+ mActivityContext = ActivityContext.lookupContext(context);
+ }
+
+ @Override
+ void updateAppDivider() {
+ OnboardingPrefs<?> onboardingPrefs = mActivityContext.getOnboardingPrefs();
+ if (onboardingPrefs != null) {
+ mActivityContext.getAppsView().getFloatingHeaderView()
+ .findFixedRowByType(AppsDividerView.class)
+ .setShowAllAppsLabel(
+ !onboardingPrefs.hasReachedMaxCount(ALL_APPS_VISITED_COUNT));
+ onboardingPrefs.incrementEventCount(ALL_APPS_VISITED_COUNT);
+ }
+ }
+
+ @Override
+ public void setPredictedApps(BgDataModel.FixedContainerItems item) {
+ mActivityContext.getAppsView().getFloatingHeaderView()
+ .findFixedRowByType(PredictionRowView.class)
+ .setPredictedApps(item.items);
+ }
+
+ @Override
+ public void setLongClickListener(ActivityAllAppsContainerView<?> appsView,
+ View.OnLongClickListener onIconLongClickListener) {
+ appsView.getFloatingHeaderView()
+ .findFixedRowByType(PredictionRowView.class)
+ .setOnIconLongClickListener(onIconLongClickListener);
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/statehandlers/BackButtonAlphaHandler.java b/quickstep/src/com/android/launcher3/statehandlers/BackButtonAlphaHandler.java
deleted file mode 100644
index 07d3a51..0000000
--- a/quickstep/src/com/android/launcher3/statehandlers/BackButtonAlphaHandler.java
+++ /dev/null
@@ -1,64 +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.statehandlers;
-
-import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.util.DisplayController.NavigationMode.TWO_BUTTONS;
-import static com.android.quickstep.AnimatedFloat.VALUE;
-
-import com.android.launcher3.BaseQuickstepLauncher;
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.anim.PendingAnimation;
-import com.android.launcher3.statemanager.StateManager.StateHandler;
-import com.android.launcher3.states.StateAnimationConfig;
-import com.android.launcher3.util.DisplayController;
-import com.android.launcher3.util.UiThreadHelper;
-import com.android.quickstep.AnimatedFloat;
-import com.android.quickstep.SystemUiProxy;
-
-/**
- * State handler for animating back button alpha in two-button nav mode.
- */
-public class BackButtonAlphaHandler implements StateHandler<LauncherState> {
-
- private final BaseQuickstepLauncher mLauncher;
- private final AnimatedFloat mBackAlpha = new AnimatedFloat(this::updateBackAlpha);
-
- public BackButtonAlphaHandler(BaseQuickstepLauncher launcher) {
- mLauncher = launcher;
- }
-
- @Override
- public void setState(LauncherState state) { }
-
- @Override
- public void setStateWithAnimation(LauncherState toState, StateAnimationConfig config,
- PendingAnimation animation) {
- if (DisplayController.getNavigationMode(mLauncher) != TWO_BUTTONS) {
- return;
- }
-
- mBackAlpha.value = SystemUiProxy.INSTANCE.get(mLauncher).getLastNavButtonAlpha();
- animation.setFloat(mBackAlpha, VALUE,
- mLauncher.shouldBackButtonBeHidden(toState) ? 0 : 1, LINEAR);
- }
-
- private void updateBackAlpha() {
- UiThreadHelper.setBackButtonAlphaAsync(mLauncher,
- BaseQuickstepLauncher.SET_BACK_BUTTON_ALPHA, mBackAlpha.value, false /* animate */);
- }
-}
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
index 1311b1d..867e168 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
@@ -19,11 +19,11 @@
import static com.android.launcher3.anim.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;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
-import android.util.FloatProperty;
import android.view.CrossWindowBlurListeners;
import android.view.View;
import android.view.ViewRootImpl;
@@ -32,7 +32,6 @@
import com.android.launcher3.BaseActivity;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.Utilities;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.statemanager.StateManager.StateHandler;
import com.android.launcher3.states.StateAnimationConfig;
@@ -47,43 +46,12 @@
public class DepthController extends BaseDepthController implements StateHandler<LauncherState>,
BaseActivity.MultiWindowModeChangedListener {
- /**
- * A property that updates the background blur within a given range of values (ie. even if the
- * animator goes beyond 0..1, the interpolated value will still be bounded).
- */
- public static class ClampedDepthProperty extends FloatProperty<DepthController> {
- private final float mMinValue;
- private final float mMaxValue;
-
- public ClampedDepthProperty(float minValue, float maxValue) {
- super("depthClamped");
- mMinValue = minValue;
- mMaxValue = maxValue;
- }
-
- @Override
- public void setValue(DepthController depthController, float depth) {
- depthController.setDepth(Utilities.boundToRange(depth, mMinValue, mMaxValue));
- }
-
- @Override
- public Float get(DepthController depthController) {
- return depthController.mDepth;
- }
- }
-
private final ViewTreeObserver.OnDrawListener mOnDrawListener = this::onLauncherDraw;
private final Consumer<Boolean> mCrossWindowBlurListener = this::setCrossWindowBlursEnabled;
private final Runnable mOpaquenessListener = this::applyDepthAndBlur;
- /**
- * If we're launching and app and should not be blurring the screen for performance reasons.
- */
- private boolean mBlurDisabledForAppLaunch;
-
-
// Workaround for animating the depth when multiwindow mode changes.
private boolean mIgnoreStateChangesDuringMultiWindowAnimation = false;
@@ -141,16 +109,12 @@
@Override
public void setState(LauncherState toState) {
- if (mSurface == null || mIgnoreStateChangesDuringMultiWindowAnimation) {
+ if (mIgnoreStateChangesDuringMultiWindowAnimation) {
return;
}
- float toDepth = toState.getDepth(mLauncher);
- if (Float.compare(mDepth, toDepth) != 0) {
- setDepth(toDepth);
- } else if (toState == LauncherState.OVERVIEW) {
- applyDepthAndBlur();
- } else if (toState == LauncherState.BACKGROUND_APP) {
+ stateDepth.setValue(toState.getDepth(mLauncher));
+ if (toState == LauncherState.BACKGROUND_APP) {
mLauncher.getDragLayer().getViewTreeObserver().addOnDrawListener(mOnDrawListener);
}
}
@@ -164,9 +128,8 @@
}
float toDepth = toState.getDepth(mLauncher);
- if (Float.compare(mDepth, toDepth) != 0) {
- animation.setFloat(this, DEPTH, toDepth, config.getInterpolator(ANIM_DEPTH, LINEAR));
- }
+ animation.setFloat(stateDepth, MULTI_PROPERTY_VALUE, toDepth,
+ config.getInterpolator(ANIM_DEPTH, LINEAR));
}
@Override
@@ -179,7 +142,7 @@
public void onMultiWindowModeChanged(boolean isInMultiWindowMode) {
mIgnoreStateChangesDuringMultiWindowAnimation = true;
- ObjectAnimator mwAnimation = ObjectAnimator.ofFloat(this, DEPTH,
+ ObjectAnimator mwAnimation = ObjectAnimator.ofFloat(stateDepth, MULTI_PROPERTY_VALUE,
mLauncher.getStateManager().getState().getDepth(mLauncher, isInMultiWindowMode))
.setDuration(300);
mwAnimation.addListener(new AnimatorListenerAdapter() {
@@ -197,9 +160,9 @@
writer.println(prefix + "\tmMaxBlurRadius=" + mMaxBlurRadius);
writer.println(prefix + "\tmCrossWindowBlursEnabled=" + mCrossWindowBlursEnabled);
writer.println(prefix + "\tmSurface=" + mSurface);
- writer.println(prefix + "\tmDepth=" + mDepth);
+ writer.println(prefix + "\tmStateDepth=" + stateDepth.getValue());
+ writer.println(prefix + "\tmWidgetDepth=" + widgetDepth.getValue());
writer.println(prefix + "\tmCurrentBlur=" + mCurrentBlur);
- writer.println(prefix + "\tmBlurDisabledForAppLaunch=" + mBlurDisabledForAppLaunch);
writer.println(prefix + "\tmInEarlyWakeUp=" + mInEarlyWakeUp);
writer.println(prefix + "\tmIgnoreStateChangesDuringMultiWindowAnimation="
+ mIgnoreStateChangesDuringMultiWindowAnimation);
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
new file mode 100644
index 0000000..0c8952d
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.statehandlers;
+
+import android.os.SystemProperties;
+import android.view.View;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.launcher3.uioverrides.QuickstepLauncher;
+
+/**
+ * Controls the visibility of the workspace and the resumed / paused state when desktop mode
+ * is enabled.
+ */
+public class DesktopVisibilityController {
+
+ private final Launcher mLauncher;
+
+ private boolean mFreeformTasksVisible;
+ private boolean mInOverviewState;
+
+ public DesktopVisibilityController(Launcher launcher) {
+ mLauncher = launcher;
+ }
+
+ /**
+ * Whether desktop mode is supported.
+ */
+ private boolean isDesktopModeSupported() {
+ return SystemProperties.getBoolean("persist.wm.debug.desktop_mode", false);
+ }
+
+ /**
+ * Whether freeform windows are visible in desktop mode.
+ */
+ public boolean areFreeformTasksVisible() {
+ return mFreeformTasksVisible;
+ }
+
+ /**
+ * Sets whether freeform windows are visible and updates launcher visibility based on that.
+ */
+ public void setFreeformTasksVisible(boolean freeformTasksVisible) {
+ if (freeformTasksVisible != mFreeformTasksVisible) {
+ mFreeformTasksVisible = freeformTasksVisible;
+ updateLauncherVisibility();
+ }
+ }
+
+ /**
+ * Sets whether the overview is visible and updates launcher visibility based on that.
+ */
+ public void setOverviewStateEnabled(boolean overviewStateEnabled) {
+ if (overviewStateEnabled != mInOverviewState) {
+ mInOverviewState = overviewStateEnabled;
+ updateLauncherVisibility();
+ }
+ }
+
+ /**
+ * Updates launcher visibility and state to look like it is paused or resumed depending on
+ * whether freeform windows are showing in desktop mode.
+ */
+ private void updateLauncherVisibility() {
+ StatefulActivity<LauncherState> activity =
+ QuickstepLauncher.ACTIVITY_TRACKER.getCreatedActivity();
+ View workspaceView = mLauncher.getWorkspace();
+ if (activity == null || workspaceView == null || !isDesktopModeSupported()) return;
+
+ if (mFreeformTasksVisible) {
+ workspaceView.setVisibility(View.INVISIBLE);
+ if (!mInOverviewState) {
+ // When freeform is visible & we're not in overview, we want launcher to appear
+ // paused, this ensures that taskbar displays.
+ activity.setPaused();
+ }
+ } else {
+ workspaceView.setVisibility(View.VISIBLE);
+ // If freeform isn't visible ensure that launcher appears resumed to behave normally.
+ activity.setResumed();
+ }
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/DesktopNavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/DesktopNavbarButtonsViewController.java
index 0ab3cfd5..48481d8 100644
--- a/quickstep/src/com/android/launcher3/taskbar/DesktopNavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/DesktopNavbarButtonsViewController.java
@@ -18,6 +18,8 @@
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_NOTIFICATIONS;
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_QUICK_SETTINGS;
+import android.view.LayoutInflater;
+import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
@@ -31,6 +33,8 @@
private final TaskbarActivityContext mContext;
private final FrameLayout mNavButtonsView;
private final ViewGroup mNavButtonContainer;
+ private final ViewGroup mStartContextualContainer;
+ private final View mAllAppsButton;
private TaskbarControllers mControllers;
@@ -40,6 +44,12 @@
mContext = context;
mNavButtonsView = navButtonsView;
mNavButtonContainer = mNavButtonsView.findViewById(R.id.end_nav_buttons);
+ mStartContextualContainer = mNavButtonsView.findViewById(R.id.start_contextual_buttons);
+ mAllAppsButton = LayoutInflater.from(context)
+ .inflate(R.layout.taskbar_all_apps_button, mStartContextualContainer, false);
+ mAllAppsButton.setOnClickListener((View v) -> {
+ mControllers.taskbarAllAppsController.show();
+ });
}
/**
@@ -57,6 +67,8 @@
addButton(R.drawable.ic_sysbar_notifications, BUTTON_NOTIFICATIONS,
mNavButtonContainer, mControllers.navButtonController,
R.id.notifications_button);
+ // All apps button
+ mStartContextualContainer.addView(mAllAppsButton);
}
/** Cleans up on destroy */
diff --git a/quickstep/src/com/android/launcher3/taskbar/DesktopTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/DesktopTaskbarUIController.java
index 3c76e8e..9393b0f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/DesktopTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/DesktopTaskbarUIController.java
@@ -16,7 +16,7 @@
package com.android.launcher3.taskbar;
-import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.launcher3.uioverrides.QuickstepLauncher;
/**
* A data source which integrates with a Launcher instance, used specifically for a
@@ -24,9 +24,9 @@
*/
public class DesktopTaskbarUIController extends TaskbarUIController {
- private final BaseQuickstepLauncher mLauncher;
+ private final QuickstepLauncher mLauncher;
- public DesktopTaskbarUIController(BaseQuickstepLauncher launcher) {
+ public DesktopTaskbarUIController(QuickstepLauncher launcher) {
mLauncher = launcher;
}
@@ -43,8 +43,8 @@
mLauncher.getHotseat().setIconsAlpha(1f);
}
- @Override
/** Disable taskbar stashing in desktop environment. */
+ @Override
public boolean supportsVisualStashing() {
return false;
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java
index f1e6747..df867cb 100644
--- a/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java
@@ -21,6 +21,7 @@
import android.animation.Animator;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.statemanager.StateManager;
import com.android.quickstep.RecentsActivity;
import com.android.quickstep.fallback.RecentsState;
@@ -40,8 +41,7 @@
animateToRecentsState(toState);
// Handle tapping on live tile.
- RecentsView recentsView = mRecentsActivity.getOverviewPanel();
- recentsView.setTaskLaunchListener(toState == RecentsState.DEFAULT
+ getRecentsView().setTaskLaunchListener(toState == RecentsState.DEFAULT
? (() -> animateToRecentsState(RecentsState.BACKGROUND_APP)) : null);
}
};
@@ -70,12 +70,14 @@
* Currently this animation just force stashes the taskbar in Overview.
*/
public Animator createAnimToRecentsState(RecentsState toState, long duration) {
- boolean forceStashed = toState.hasOverviewActions();
+ boolean useStashedLauncherState = toState.hasOverviewActions();
+ boolean stashedLauncherState =
+ useStashedLauncherState && !FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get();
TaskbarStashController controller = mControllers.taskbarStashController;
// Set both FLAG_IN_STASHED_LAUNCHER_STATE and FLAG_IN_APP to ensure the state is respected.
// For all other states, just use the current stashed-in-app setting (e.g. if long clicked).
- controller.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE, forceStashed);
- controller.updateStateForFlag(FLAG_IN_APP, !forceStashed);
+ controller.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE, stashedLauncherState);
+ controller.updateStateForFlag(FLAG_IN_APP, !useStashedLauncherState);
return controller.applyStateWithoutStart(duration);
}
@@ -85,4 +87,9 @@
anim.start();
}
}
+
+ @Override
+ public RecentsView getRecentsView() {
+ return mRecentsActivity.getOverviewPanel();
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index 62a11d4..0945bf2 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -15,8 +15,11 @@
*/
package com.android.launcher3.taskbar;
+import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
+
+import static com.android.launcher3.QuickstepTransitionManager.TRANSIENT_TASKBAR_TRANSITION_DURATION;
import static com.android.launcher3.taskbar.TaskbarLauncherStateController.FLAG_RESUMED;
-import static com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_EXTRA_NAVIGATION_BAR;
+import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS;
import android.animation.Animator;
import android.animation.AnimatorSet;
@@ -24,27 +27,28 @@
import android.os.RemoteException;
import android.util.Log;
import android.util.SparseArray;
-import android.view.MotionEvent;
import android.view.TaskTransitionSpec;
import android.view.WindowManagerGlobal;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
-import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.LauncherState;
import com.android.launcher3.QuickstepTransitionManager;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.logging.InstanceId;
import com.android.launcher3.logging.InstanceIdSequence;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
+import com.android.launcher3.uioverrides.QuickstepLauncher;
+import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.OnboardingPrefs;
import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.RecentsAnimationCallbacks;
+import com.android.quickstep.views.RecentsView;
import java.io.PrintWriter;
import java.util.Arrays;
@@ -65,7 +69,7 @@
private final SparseArray<Float> mTaskbarInAppDisplayProgress = new SparseArray<>(4);
- private final BaseQuickstepLauncher mLauncher;
+ private final QuickstepLauncher mLauncher;
private final DeviceProfile.OnDeviceProfileChangeListener mOnDeviceProfileChangeListener =
dp -> {
@@ -81,7 +85,7 @@
private final TaskbarLauncherStateController
mTaskbarLauncherStateController = new TaskbarLauncherStateController();
- public LauncherTaskbarUIController(BaseQuickstepLauncher launcher) {
+ public LauncherTaskbarUIController(QuickstepLauncher launcher) {
mLauncher = launcher;
}
@@ -115,7 +119,8 @@
@Override
protected boolean isTaskbarTouchable() {
- return !mTaskbarLauncherStateController.isAnimatingToLauncher();
+ return !(mTaskbarLauncherStateController.isAnimatingToLauncher()
+ && mTaskbarLauncherStateController.goingToAlignedLauncherState());
}
public void setShouldDelayLauncherStateAnim(boolean shouldDelayLauncherStateAnim) {
@@ -124,24 +129,6 @@
}
/**
- * Enables manual taskbar stashing. This method should only be used for tests that need to
- * stash/unstash the taskbar.
- */
- @VisibleForTesting
- public void enableManualStashingForTests(boolean enableManualStashing) {
- mControllers.taskbarStashController.enableManualStashingForTests(enableManualStashing);
- }
-
- /**
- * Unstashes the Taskbar if it is stashed. This method should only be used to unstash the
- * taskbar at the end of a test.
- */
- @VisibleForTesting
- public void unstashTaskbarIfStashed() {
- mControllers.taskbarStashController.onLongPressToUnstashTaskbar();
- }
-
- /**
* Adds the Launcher resume animator to the given animator set.
*
* This should be used to run a Launcher resume animation whose progress matches a
@@ -171,9 +158,11 @@
isResumed,
fromInit,
/* startAnimation= */ true,
- !isResumed
- ? QuickstepTransitionManager.TASKBAR_TO_APP_DURATION
- : QuickstepTransitionManager.TASKBAR_TO_HOME_DURATION);
+ DisplayController.isTransientTaskbar(mLauncher)
+ ? TRANSIENT_TASKBAR_TRANSITION_DURATION
+ : (!isResumed
+ ? QuickstepTransitionManager.TASKBAR_TO_APP_DURATION
+ : QuickstepTransitionManager.TASKBAR_TO_HOME_DURATION));
}
@Nullable
@@ -188,6 +177,13 @@
}
}
+ if (ENABLE_SHELL_TRANSITIONS && isResumed
+ && !mLauncher.getStateManager().getState().isTaskbarAlignedWithHotseat(mLauncher)) {
+ // Launcher is resumed, but in a state where taskbar is still independent, so
+ // ignore the state change.
+ return null;
+ }
+
mTaskbarLauncherStateController.updateStateForFlag(FLAG_RESUMED, isResumed);
return mTaskbarLauncherStateController.applyState(fromInit ? 0 : duration, startAnimation);
}
@@ -200,16 +196,15 @@
*/
public Animator createAnimToLauncher(@NonNull LauncherState toState,
@NonNull RecentsAnimationCallbacks callbacks, long duration) {
- return mTaskbarLauncherStateController.createAnimToLauncher(toState, callbacks, duration);
- }
+ AnimatorSet set = new AnimatorSet();
+ Animator taskbarState = mTaskbarLauncherStateController
+ .createAnimToLauncher(toState, callbacks, duration);
+ long halfDuration = Math.round(duration * 0.5f);
+ Animator translation =
+ mControllers.taskbarTranslationController.createAnimToLauncher(halfDuration);
- /**
- * @param ev MotionEvent in screen coordinates.
- * @return Whether any Taskbar item could handle the given MotionEvent if given the chance.
- */
- public boolean isEventOverAnyTaskbarItem(MotionEvent ev) {
- return mControllers.taskbarViewController.isEventOverAnyItem(ev)
- || mControllers.navbarButtonsViewController.isEventOverAnyItem(ev);
+ set.playTogether(taskbarState, translation);
+ return set;
}
public boolean isDraggingItem() {
@@ -235,7 +230,9 @@
} else {
// Adjust task transition spec to account for taskbar being visible
@ColorInt int taskAnimationBackgroundColor =
- mLauncher.getColor(R.color.taskbar_background);
+ DisplayController.isTransientTaskbar(mLauncher)
+ ? mLauncher.getColor(R.color.transient_taskbar_background)
+ : mLauncher.getColor(R.color.taskbar_background);
TaskTransitionSpec customTaskAnimationSpec = new TaskTransitionSpec(
taskAnimationBackgroundColor,
@@ -284,13 +281,6 @@
&& !mLauncher.getOnboardingPrefs().getBoolean(OnboardingPrefs.TASKBAR_EDU_SEEN);
}
- /**
- * Manually ends the taskbar education flow.
- */
- public void hideEdu() {
- mControllers.taskbarEduController.hideEdu();
- }
-
@Override
public void onTaskbarIconLaunched(ItemInfo item) {
InstanceId instanceId = new InstanceIdSequence().newInstanceId();
@@ -301,9 +291,16 @@
@Override
public void setSystemGestureInProgress(boolean inProgress) {
super.setSystemGestureInProgress(inProgress);
- // Launcher's ScrimView will draw the background throughout the gesture. But once the
- // gesture ends, start drawing taskbar's background again since launcher might stop drawing.
- forceHideBackground(inProgress);
+ if (DisplayController.isTransientTaskbar(mLauncher)) {
+ forceHideBackground(false);
+ return;
+ }
+ if (!FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get()) {
+ // Launcher's ScrimView will draw the background throughout the gesture. But once the
+ // gesture ends, start drawing taskbar's background again since launcher might stop
+ // drawing.
+ forceHideBackground(inProgress);
+ }
}
/**
@@ -361,6 +358,11 @@
}
@Override
+ public boolean isIconAlignedWithHotseat() {
+ return mTaskbarLauncherStateController.isIconAlignedWithHotseat();
+ }
+
+ @Override
public void dumpLogs(String prefix, PrintWriter pw) {
super.dumpLogs(prefix, pw);
@@ -393,4 +395,9 @@
mTaskbarLauncherStateController.dumpLogs(prefix + "\t", pw);
}
+
+ @Override
+ public RecentsView getRecentsView() {
+ return mLauncher.getOverviewPanel();
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index f1f18c1..875327d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -15,13 +15,14 @@
*/
package com.android.launcher3.taskbar;
+import static android.view.View.AccessibilityDelegate;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor;
import static com.android.launcher3.taskbar.LauncherTaskbarUIController.SYSUI_SURFACE_PROGRESS_INDEX;
import static com.android.launcher3.taskbar.TaskbarManager.isPhoneButtonNavMode;
-import static com.android.launcher3.taskbar.TaskbarManager.isPhoneMode;
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_A11Y;
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_BACK;
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_HOME;
@@ -30,6 +31,7 @@
import static com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_KEYGUARD;
import static com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_SMALL_SCREEN;
import static com.android.launcher3.taskbar.Utilities.appendFlag;
+import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BACK_DISABLED;
@@ -41,7 +43,6 @@
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING;
-import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo.TOUCHABLE_INSETS_REGION;
import android.animation.ArgbEvaluator;
import android.animation.ObjectAnimator;
@@ -53,6 +54,7 @@
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Color;
+import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.Region.Op;
@@ -68,6 +70,7 @@
import android.view.View.OnClickListener;
import android.view.View.OnHoverListener;
import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.ImageView;
@@ -79,6 +82,10 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AlphaUpdateListener;
import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarButton;
+import com.android.launcher3.taskbar.navbutton.NavButtonLayoutFactory;
+import com.android.launcher3.taskbar.navbutton.NavButtonLayoutFactory.NavButtonLayoutter;
+import com.android.launcher3.util.DimensionUtils;
+import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.launcher3.util.TouchController;
import com.android.launcher3.views.BaseDragLayer;
@@ -87,7 +94,6 @@
import com.android.systemui.shared.rotation.RotationButton;
import com.android.systemui.shared.rotation.RotationButtonController;
import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.shared.system.ViewTreeObserverWrapper;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -115,12 +121,18 @@
private static final int FLAG_SCREEN_PINNING_ACTIVE = 1 << 11;
private static final int FLAG_VOICE_INTERACTION_WINDOW_SHOWING = 1 << 12;
private static final int FLAG_SMALL_SCREEN = 1 << 13;
+ private static final int FLAG_SLIDE_IN_VIEW_VISIBLE = 1 << 14;
+
+ /** Flags where a UI could be over a slide in view, so the color override should be disabled. */
+ private static final int FLAGS_SLIDE_IN_VIEW_ICON_COLOR_OVERRIDE_DISABLED =
+ FLAG_NOTIFICATION_SHADE_EXPANDED | FLAG_VOICE_INTERACTION_WINDOW_SHOWING;
private static final String NAV_BUTTONS_SEPARATE_WINDOW_TITLE = "Taskbar Nav Buttons";
public static final int ALPHA_INDEX_IMMERSIVE_MODE = 0;
public static final int ALPHA_INDEX_KEYGUARD_OR_DISABLE = 1;
- private static final int NUM_ALPHA_CHANNELS = 2;
+ public static final int ALPHA_INDEX_SUW = 2;
+ private static final int NUM_ALPHA_CHANNELS = 3;
private final ArrayList<StatePropertyHolder> mPropertyHolders = new ArrayList<>();
private final ArrayList<ImageView> mAllButtons = new ArrayList<>();
@@ -134,6 +146,8 @@
private final ViewGroup mStartContextualContainer;
private final int mLightIconColor;
private final int mDarkIconColor;
+ /** Color to use for navigation bar buttons, if a slide in view is visible. */
+ private final int mSlideInViewIconColor;
private final AnimatedFloat mTaskbarNavButtonTranslationY = new AnimatedFloat(
this::updateNavButtonTranslationY);
@@ -148,6 +162,9 @@
this::updateNavButtonDarkIntensity);
private final AnimatedFloat mNavButtonDarkIntensityMultiplier = new AnimatedFloat(
this::updateNavButtonDarkIntensity);
+ /** Overrides the navigation button color to {@code mSlideInViewIconColor} when {@code 1}. */
+ private final AnimatedFloat mSlideInViewNavButtonColorOverride = new AnimatedFloat(
+ this::updateNavButtonDarkIntensity);
private final RotationButtonListener mRotationButtonListener = new RotationButtonListener();
private final Rect mFloatingRotationButtonBounds = new Rect();
@@ -166,9 +183,10 @@
// Variables for moving nav buttons to a separate window above IME
private boolean mAreNavButtonsInSeparateWindow = false;
private BaseDragLayer<TaskbarActivityContext> mSeparateWindowParent; // Initialized in init.
- private final ViewTreeObserverWrapper.OnComputeInsetsListener mSeparateWindowInsetsComputer =
+ private final ViewTreeObserver.OnComputeInternalInsetsListener mSeparateWindowInsetsComputer =
this::onComputeInsetsForSeparateWindow;
private final RecentsHitboxExtender mHitboxExtender = new RecentsHitboxExtender();
+ private View mRecentsButton;
public NavbarButtonsViewController(TaskbarActivityContext context, FrameLayout navButtonsView) {
mContext = context;
@@ -179,6 +197,8 @@
mLightIconColor = context.getColor(R.color.taskbar_nav_icon_light_color);
mDarkIconColor = context.getColor(R.color.taskbar_nav_icon_dark_color);
+ // Can precompute color since dark theme change recreates taskbar.
+ mSlideInViewIconColor = Utilities.isDarkTheme(context) ? mLightIconColor : mDarkIconColor;
}
/**
@@ -189,9 +209,11 @@
boolean isThreeButtonNav = mContext.isThreeButtonNav();
DeviceProfile deviceProfile = mContext.getDeviceProfile();
Resources resources = mContext.getResources();
- mNavButtonsView.getLayoutParams().height = !isPhoneMode(deviceProfile) ?
- deviceProfile.taskbarSize :
- resources.getDimensionPixelSize(R.dimen.taskbar_size);
+ Point p = !mContext.isUserSetupComplete()
+ ? new Point(0, resources.getDimensionPixelSize(R.dimen.taskbar_suw_frame))
+ : DimensionUtils.getTaskbarPhoneDimensions(deviceProfile, resources,
+ TaskbarManager.isPhoneMode(deviceProfile));
+ mNavButtonsView.getLayoutParams().height = p.y;
mIsImeRenderingNavButtons =
InputMethodService.canImeRenderGesturalNavButtons() && mContext.imeDrawsImeNavBar();
@@ -207,13 +229,13 @@
mPropertyHolders.add(new StatePropertyHolder(
mControllers.taskbarViewController.getTaskbarIconAlpha()
- .getProperty(ALPHA_INDEX_KEYGUARD),
+ .get(ALPHA_INDEX_KEYGUARD),
flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0
&& (flags & FLAG_SCREEN_PINNING_ACTIVE) == 0));
mPropertyHolders.add(new StatePropertyHolder(
mControllers.taskbarViewController.getTaskbarIconAlpha()
- .getProperty(ALPHA_INDEX_SMALL_SCREEN),
+ .get(ALPHA_INDEX_SMALL_SCREEN),
flags -> (flags & FLAG_SMALL_SCREEN) == 0));
mPropertyHolders.add(new StatePropertyHolder(mControllers.taskbarDragLayerController
@@ -242,88 +264,16 @@
flags -> (flags & FLAG_IME_VISIBLE) != 0 && !isInKidsMode, AnimatedFloat.VALUE,
transForIme, defaultButtonTransY));
+ mPropertyHolders.add(new StatePropertyHolder(
+ mSlideInViewNavButtonColorOverride,
+ flags -> ((flags & FLAG_SLIDE_IN_VIEW_VISIBLE) != 0)
+ && ((flags & FLAGS_SLIDE_IN_VIEW_ICON_COLOR_OVERRIDE_DISABLED) == 0)));
+
if (alwaysShowButtons) {
initButtons(mNavButtonContainer, mEndContextualContainer,
mControllers.navButtonController);
updateButtonLayoutSpacing();
updateStateForFlag(FLAG_SMALL_SCREEN, isPhoneButtonNavMode(mContext));
- if (isInSetup) {
- // Since setup wizard only has back button enabled, it looks strange to be
- // end-aligned, so start-align instead.
- FrameLayout.LayoutParams navButtonsLayoutParams = (FrameLayout.LayoutParams)
- mNavButtonContainer.getLayoutParams();
- navButtonsLayoutParams.setMarginStart(navButtonsLayoutParams.getMarginEnd());
- navButtonsLayoutParams.setMarginEnd(0);
- navButtonsLayoutParams.gravity = Gravity.START;
- mNavButtonContainer.requestLayout();
-
- // TODO(b/210906568) Dark intensity is currently not propagated during setup, so set
- // it based on dark theme for now.
- int mode = resources.getConfiguration().uiMode
- & Configuration.UI_MODE_NIGHT_MASK;
- boolean isDarkTheme = mode == Configuration.UI_MODE_NIGHT_YES;
- mTaskbarNavButtonDarkIntensity.updateValue(isDarkTheme ? 0 : 1);
- } else if (isInKidsMode) {
- int iconSize = resources.getDimensionPixelSize(
- R.dimen.taskbar_icon_size_kids);
- int buttonWidth = resources.getDimensionPixelSize(
- R.dimen.taskbar_nav_buttons_width_kids);
- int buttonHeight = resources.getDimensionPixelSize(
- R.dimen.taskbar_nav_buttons_height_kids);
- int buttonRadius = resources.getDimensionPixelSize(
- R.dimen.taskbar_nav_buttons_corner_radius_kids);
- int paddingleft = (buttonWidth - iconSize) / 2;
- int paddingRight = paddingleft;
- int paddingTop = (buttonHeight - iconSize) / 2;
- int paddingBottom = paddingTop;
-
- // Update icons
- ((ImageView) mBackButton).setImageDrawable(
- mBackButton.getContext().getDrawable(R.drawable.ic_sysbar_back_kids));
- ((ImageView) mBackButton).setScaleType(ImageView.ScaleType.FIT_CENTER);
- mBackButton.setPadding(paddingleft, paddingTop, paddingRight, paddingBottom);
- ((ImageView) mHomeButton).setImageDrawable(
- mHomeButton.getContext().getDrawable(R.drawable.ic_sysbar_home_kids));
- ((ImageView) mHomeButton).setScaleType(ImageView.ScaleType.FIT_CENTER);
- mHomeButton.setPadding(paddingleft, paddingTop, paddingRight, paddingBottom);
-
- // Home button layout
- LinearLayout.LayoutParams homeLayoutparams = new LinearLayout.LayoutParams(
- buttonWidth,
- buttonHeight
- );
- int homeButtonLeftMargin = resources.getDimensionPixelSize(
- R.dimen.taskbar_home_button_left_margin_kids);
- homeLayoutparams.setMargins(homeButtonLeftMargin, 0, 0, 0);
- mHomeButton.setLayoutParams(homeLayoutparams);
-
- // Back button layout
- LinearLayout.LayoutParams backLayoutParams = new LinearLayout.LayoutParams(
- buttonWidth,
- buttonHeight
- );
- int backButtonLeftMargin = resources.getDimensionPixelSize(
- R.dimen.taskbar_back_button_left_margin_kids);
- backLayoutParams.setMargins(backButtonLeftMargin, 0, 0, 0);
- mBackButton.setLayoutParams(backLayoutParams);
-
- // Button backgrounds
- int whiteWith10PctAlpha = Color.argb(0.1f, 1, 1, 1);
- PaintDrawable buttonBackground = new PaintDrawable(whiteWith10PctAlpha);
- buttonBackground.setCornerRadius(buttonRadius);
- mHomeButton.setBackground(buttonBackground);
- mBackButton.setBackground(buttonBackground);
-
- // Update alignment within taskbar
- FrameLayout.LayoutParams navButtonsLayoutParams = (FrameLayout.LayoutParams)
- mNavButtonContainer.getLayoutParams();
- navButtonsLayoutParams.setMarginStart(navButtonsLayoutParams.getMarginEnd() / 2);
- navButtonsLayoutParams.setMarginEnd(navButtonsLayoutParams.getMarginStart());
- navButtonsLayoutParams.gravity = Gravity.CENTER;
- mNavButtonContainer.requestLayout();
-
- mHomeButton.setOnLongClickListener(null);
- }
// Animate taskbar background when either..
// notification shade expanded AND not on keyguard
@@ -392,7 +342,7 @@
mBackButtonAlpha = new MultiValueAlpha(mBackButton, NUM_ALPHA_CHANNELS);
mBackButtonAlpha.setUpdateVisibility(true);
mPropertyHolders.add(new StatePropertyHolder(
- mBackButtonAlpha.getProperty(ALPHA_INDEX_KEYGUARD_OR_DISABLE),
+ mBackButtonAlpha.get(ALPHA_INDEX_KEYGUARD_OR_DISABLE),
flags -> {
// Show only if not disabled, and if not on the keyguard or otherwise only when
// the bouncer or a lockscreen app is showing above the keyguard
@@ -420,26 +370,26 @@
mHomeButtonAlpha = new MultiValueAlpha(mHomeButton, NUM_ALPHA_CHANNELS);
mHomeButtonAlpha.setUpdateVisibility(true);
mPropertyHolders.add(
- new StatePropertyHolder(mHomeButtonAlpha.getProperty(
+ new StatePropertyHolder(mHomeButtonAlpha.get(
ALPHA_INDEX_KEYGUARD_OR_DISABLE),
flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0 &&
(flags & FLAG_DISABLE_HOME) == 0));
// Recents button
- View recentsButton = addButton(R.drawable.ic_sysbar_recent, BUTTON_RECENTS,
+ mRecentsButton = addButton(R.drawable.ic_sysbar_recent, BUTTON_RECENTS,
navContainer, navButtonController, R.id.recent_apps);
- mHitboxExtender.init(recentsButton, mNavButtonsView, mContext.getDeviceProfile(),
+ mHitboxExtender.init(mRecentsButton, mNavButtonsView, mContext.getDeviceProfile(),
() -> {
float[] recentsCoords = new float[2];
- getDescendantCoordRelativeToAncestor(recentsButton, mNavButtonsView,
+ getDescendantCoordRelativeToAncestor(mRecentsButton, mNavButtonsView,
recentsCoords, false);
return recentsCoords;
}, new Handler());
- recentsButton.setOnClickListener(v -> {
+ mRecentsButton.setOnClickListener(v -> {
navButtonController.onButtonClick(BUTTON_RECENTS, v);
mHitboxExtender.onRecentsButtonClicked();
});
- mPropertyHolders.add(new StatePropertyHolder(recentsButton,
+ mPropertyHolders.add(new StatePropertyHolder(mRecentsButton,
flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0 && (flags & FLAG_DISABLE_RECENTS) == 0
&& !mContext.isNavBarKidsModeActive()));
@@ -523,6 +473,12 @@
applyState();
}
+ /** {@code true} if a slide in view is currently visible over taskbar. */
+ public void setSlideInViewVisible(boolean isSlideInViewVisible) {
+ updateStateForFlag(FLAG_SLIDE_IN_VIEW_VISIBLE, isSlideInViewVisible);
+ applyState();
+ }
+
/**
* Returns true if IME bar is visible
*/
@@ -582,6 +538,26 @@
return mHomeButtonAlpha;
}
+ /**
+ * Sets the AccessibilityDelegate for the home button.
+ */
+ public void setHomeButtonAccessibilityDelegate(AccessibilityDelegate accessibilityDelegate) {
+ if (mHomeButton == null) {
+ return;
+ }
+ mHomeButton.setAccessibilityDelegate(accessibilityDelegate);
+ }
+
+ /**
+ * Sets the AccessibilityDelegate for the back button.
+ */
+ public void setBackButtonAccessibilityDelegate(AccessibilityDelegate accessibilityDelegate) {
+ if (mBackButton == null) {
+ return;
+ }
+ mBackButton.setAccessibilityDelegate(accessibilityDelegate);
+ }
+
/** Use to set the translationY for the all nav+contextual buttons */
public AnimatedFloat getTaskbarNavButtonTranslationY() {
return mTaskbarNavButtonTranslationY;
@@ -648,8 +624,11 @@
private void updateNavButtonDarkIntensity() {
float darkIntensity = mTaskbarNavButtonDarkIntensity.value
* mNavButtonDarkIntensityMultiplier.value;
- int iconColor = (int) ArgbEvaluator.getInstance().evaluate(darkIntensity, mLightIconColor,
- mDarkIconColor);
+ ArgbEvaluator argbEvaluator = ArgbEvaluator.getInstance();
+ int iconColor = (int) argbEvaluator.evaluate(
+ darkIntensity, mLightIconColor, mDarkIconColor);
+ iconColor = (int) argbEvaluator.evaluate(
+ mSlideInViewNavButtonColorOverride.value, iconColor, mSlideInViewIconColor);
for (ImageView button : mAllButtons) {
button.setImageTintList(ColorStateList.valueOf(iconColor));
}
@@ -691,91 +670,166 @@
if (mFloatingRotationButton != null) {
mFloatingRotationButton.onConfigurationChanged(configChanges);
}
+ if (!mContext.isUserSetupComplete()) {
+ handleSetupUi();
+ }
updateButtonLayoutSpacing();
}
- /**
- * Adds the correct spacing to 3 button nav container. No-op if using gesture nav or kids mode.
- */
- private void updateButtonLayoutSpacing() {
- if (!mContext.isThreeButtonNav() || mContext.isNavBarKidsModeActive()) {
- return;
- }
-
- if (isPhoneButtonNavMode(mContext)) {
- updatePhoneButtonSpacing();
- return;
- }
-
- DeviceProfile dp = mContext.getDeviceProfile();
- Resources res = mContext.getResources();
-
- // Add spacing after the end of the last nav button
- FrameLayout.LayoutParams navButtonParams =
- (FrameLayout.LayoutParams) mNavButtonContainer.getLayoutParams();
- navButtonParams.gravity = Gravity.END;
- navButtonParams.width = FrameLayout.LayoutParams.WRAP_CONTENT;
- navButtonParams.height = MATCH_PARENT;
-
- int navMarginEnd = (int) res.getDimension(dp.inv.inlineNavButtonsEndSpacing);
- int contextualWidth = mEndContextualContainer.getWidth();
- // If contextual buttons are showing, we check if the end margin is enough for the
- // contextual button to be showing - if not, move the nav buttons over a smidge
- if (isContextualButtonShowing() && navMarginEnd < contextualWidth) {
- // Additional spacing, eat up half of space between last icon and nav button
- navMarginEnd += res.getDimensionPixelSize(R.dimen.taskbar_hotseat_nav_spacing) / 2;
- }
- navButtonParams.setMarginEnd(navMarginEnd);
- mNavButtonContainer.setLayoutParams(navButtonParams);
-
- // Add the spaces in between the nav buttons
- int spaceInBetween = res.getDimensionPixelSize(R.dimen.taskbar_button_space_inbetween);
- for (int i = 0; i < mNavButtonContainer.getChildCount(); i++) {
- View navButton = mNavButtonContainer.getChildAt(i);
- LinearLayout.LayoutParams buttonLayoutParams =
- (LinearLayout.LayoutParams) navButton.getLayoutParams();
- buttonLayoutParams.weight = 0;
- if (i == 0) {
- buttonLayoutParams.setMarginEnd(spaceInBetween / 2);
- } else if (i == mNavButtonContainer.getChildCount() - 1) {
- buttonLayoutParams.setMarginStart(spaceInBetween / 2);
- } else {
- buttonLayoutParams.setMarginStart(spaceInBetween / 2);
- buttonLayoutParams.setMarginEnd(spaceInBetween / 2);
- }
- }
+ private void handleSetupUi() {
+ // Since setup wizard only has back button enabled, it looks strange to be
+ // end-aligned, so start-align instead.
+ FrameLayout.LayoutParams navButtonsLayoutParams = (FrameLayout.LayoutParams)
+ mNavButtonContainer.getLayoutParams();
+ Resources resources = mContext.getResources();
+ DeviceProfile deviceProfile = mContext.getDeviceProfile();
+ int setupMargin = resources.getDimensionPixelSize(R.dimen.taskbar_contextual_button_margin);
+ navButtonsLayoutParams.setMarginStart(setupMargin);
+ navButtonsLayoutParams.bottomMargin = !deviceProfile.isLandscape
+ ? 0
+ : setupMargin -
+ (resources.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size) / 2);
+ navButtonsLayoutParams.setMarginEnd(0);
+ navButtonsLayoutParams.gravity = Gravity.START;
+ mNavButtonContainer.setLayoutParams(navButtonsLayoutParams);
+ mNavButtonContainer.requestLayout();
}
- /** Uniformly spaces out the 3 button nav for smaller phone screens */
- private void updatePhoneButtonSpacing() {
+ /**
+ * Adds the correct spacing to 3 button nav container depending on if device is in kids mode,
+ * setup wizard, or normal 3 button nav.
+ */
+ private void updateButtonLayoutSpacing() {
DeviceProfile dp = mContext.getDeviceProfile();
Resources res = mContext.getResources();
+ boolean isInSetup = !mContext.isUserSetupComplete();
+ // TODO(b/244231596) we're getting the incorrect kidsMode value in small-screen
+ boolean isInKidsMode = mContext.isNavBarKidsModeActive();
- // TODO: Polish pending, this is just to make it usable
- FrameLayout.LayoutParams navContainerParams =
- (FrameLayout.LayoutParams) mNavButtonContainer.getLayoutParams();
- int endStartMargins = res.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size);
- navContainerParams.gravity = Gravity.CENTER;
- navContainerParams.setMarginEnd(endStartMargins);
- navContainerParams.setMarginStart(endStartMargins);
- mNavButtonContainer.setLayoutParams(navContainerParams);
+ if (TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW) {
+ boolean isThreeButtonNav = mContext.isThreeButtonNav();
- // Add the spaces in between the nav buttons
- int spaceInBetween = res.getDimensionPixelSize(R.dimen.taskbar_button_space_inbetween_phone);
- for (int i = 0; i < mNavButtonContainer.getChildCount(); i++) {
- View navButton = mNavButtonContainer.getChildAt(i);
- LinearLayout.LayoutParams buttonLayoutParams =
- (LinearLayout.LayoutParams) navButton.getLayoutParams();
- buttonLayoutParams.weight = 1;
- if (i == 0) {
- buttonLayoutParams.setMarginEnd(spaceInBetween / 2);
- } else if (i == mNavButtonContainer.getChildCount() - 1) {
- buttonLayoutParams.setMarginStart(spaceInBetween / 2);
- } else {
- buttonLayoutParams.setMarginStart(spaceInBetween / 2);
- buttonLayoutParams.setMarginEnd(spaceInBetween / 2);
+ NavButtonLayoutter navButtonLayoutter =
+ NavButtonLayoutFactory.Companion.getUiLayoutter(
+ dp, mNavButtonsView, res, isInKidsMode, isInSetup, isThreeButtonNav,
+ TaskbarManager.isPhoneMode(dp));
+ navButtonLayoutter.layoutButtons(dp, isContextualButtonShowing());
+ return;
+ }
+
+ if (isInSetup) {
+ handleSetupUi();
+
+ // Hide back button in SUW if keyboard is showing (IME draws its own back).
+ mPropertyHolders.add(new StatePropertyHolder(
+ mBackButtonAlpha.get(ALPHA_INDEX_SUW),
+ flags -> (flags & FLAG_IME_VISIBLE) == 0));
+
+ // TODO(b/210906568) Dark intensity is currently not propagated during setup, so set
+ // it based on dark theme for now.
+ int mode = res.getConfiguration().uiMode
+ & Configuration.UI_MODE_NIGHT_MASK;
+ boolean isDarkTheme = mode == Configuration.UI_MODE_NIGHT_YES;
+ mTaskbarNavButtonDarkIntensity.updateValue(isDarkTheme ? 0 : 1);
+ } else if (isInKidsMode) {
+ int iconSize = res.getDimensionPixelSize(
+ R.dimen.taskbar_icon_size_kids);
+ int buttonWidth = res.getDimensionPixelSize(
+ R.dimen.taskbar_nav_buttons_width_kids);
+ int buttonHeight = res.getDimensionPixelSize(
+ R.dimen.taskbar_nav_buttons_height_kids);
+ int buttonRadius = res.getDimensionPixelSize(
+ R.dimen.taskbar_nav_buttons_corner_radius_kids);
+ int paddingleft = (buttonWidth - iconSize) / 2;
+ int paddingRight = paddingleft;
+ int paddingTop = (buttonHeight - iconSize) / 2;
+ int paddingBottom = paddingTop;
+
+ // Update icons
+ ((ImageView) mBackButton).setImageDrawable(
+ mBackButton.getContext().getDrawable(R.drawable.ic_sysbar_back_kids));
+ ((ImageView) mBackButton).setScaleType(ImageView.ScaleType.FIT_CENTER);
+ mBackButton.setPadding(paddingleft, paddingTop, paddingRight, paddingBottom);
+ ((ImageView) mHomeButton).setImageDrawable(
+ mHomeButton.getContext().getDrawable(R.drawable.ic_sysbar_home_kids));
+ ((ImageView) mHomeButton).setScaleType(ImageView.ScaleType.FIT_CENTER);
+ mHomeButton.setPadding(paddingleft, paddingTop, paddingRight, paddingBottom);
+
+ // Home button layout
+ LinearLayout.LayoutParams homeLayoutparams = new LinearLayout.LayoutParams(
+ buttonWidth,
+ buttonHeight
+ );
+ int homeButtonLeftMargin = res.getDimensionPixelSize(
+ R.dimen.taskbar_home_button_left_margin_kids);
+ homeLayoutparams.setMargins(homeButtonLeftMargin, 0, 0, 0);
+ mHomeButton.setLayoutParams(homeLayoutparams);
+
+ // Back button layout
+ LinearLayout.LayoutParams backLayoutParams = new LinearLayout.LayoutParams(
+ buttonWidth,
+ buttonHeight
+ );
+ int backButtonLeftMargin = res.getDimensionPixelSize(
+ R.dimen.taskbar_back_button_left_margin_kids);
+ backLayoutParams.setMargins(backButtonLeftMargin, 0, 0, 0);
+ mBackButton.setLayoutParams(backLayoutParams);
+
+ // Button backgrounds
+ int whiteWith10PctAlpha = Color.argb(0.1f, 1, 1, 1);
+ PaintDrawable buttonBackground = new PaintDrawable(whiteWith10PctAlpha);
+ buttonBackground.setCornerRadius(buttonRadius);
+ mHomeButton.setBackground(buttonBackground);
+ mBackButton.setBackground(buttonBackground);
+
+ // Update alignment within taskbar
+ FrameLayout.LayoutParams navButtonsLayoutParams = (FrameLayout.LayoutParams)
+ mNavButtonContainer.getLayoutParams();
+ navButtonsLayoutParams.setMarginStart(
+ navButtonsLayoutParams.getMarginEnd() / 2);
+ navButtonsLayoutParams.setMarginEnd(navButtonsLayoutParams.getMarginStart());
+ navButtonsLayoutParams.gravity = Gravity.CENTER;
+ mNavButtonContainer.requestLayout();
+
+ mHomeButton.setOnLongClickListener(null);
+ } else if (mContext.isThreeButtonNav()) {
+ // Setup normal 3 button
+ // Add spacing after the end of the last nav button
+ FrameLayout.LayoutParams navButtonParams =
+ (FrameLayout.LayoutParams) mNavButtonContainer.getLayoutParams();
+ navButtonParams.gravity = Gravity.END;
+ navButtonParams.width = FrameLayout.LayoutParams.WRAP_CONTENT;
+ navButtonParams.height = MATCH_PARENT;
+
+ int navMarginEnd = (int) res.getDimension(dp.inv.inlineNavButtonsEndSpacing);
+ int contextualWidth = mEndContextualContainer.getWidth();
+ // If contextual buttons are showing, we check if the end margin is enough for the
+ // contextual button to be showing - if not, move the nav buttons over a smidge
+ if (isContextualButtonShowing() && navMarginEnd < contextualWidth) {
+ // Additional spacing, eat up half of space between last icon and nav button
+ navMarginEnd += res.getDimensionPixelSize(R.dimen.taskbar_hotseat_nav_spacing) / 2;
+ }
+ navButtonParams.setMarginEnd(navMarginEnd);
+ mNavButtonContainer.setLayoutParams(navButtonParams);
+
+ // Add the spaces in between the nav buttons
+ int spaceInBetween = res.getDimensionPixelSize(R.dimen.taskbar_button_space_inbetween);
+ for (int i = 0; i < mNavButtonContainer.getChildCount(); i++) {
+ View navButton = mNavButtonContainer.getChildAt(i);
+ LinearLayout.LayoutParams buttonLayoutParams =
+ (LinearLayout.LayoutParams) navButton.getLayoutParams();
+ buttonLayoutParams.weight = 0;
+ if (i == 0) {
+ buttonLayoutParams.setMarginEnd(spaceInBetween / 2);
+ } else if (i == mNavButtonContainer.getChildCount() - 1) {
+ buttonLayoutParams.setMarginStart(spaceInBetween / 2);
+ } else {
+ buttonLayoutParams.setMarginStart(spaceInBetween / 2);
+ buttonLayoutParams.setMarginEnd(spaceInBetween / 2);
+ }
}
}
+
}
public void onDestroy() {
@@ -787,6 +841,8 @@
moveNavButtonsBackToTaskbarWindow();
mNavButtonContainer.removeAllViews();
+ mEndContextualContainer.removeAllViews();
+ mStartContextualContainer.removeAllViews();
mAllButtons.clear();
}
@@ -806,14 +862,14 @@
mSeparateWindowParent.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View view) {
- ViewTreeObserverWrapper.addOnComputeInsetsListener(
- mSeparateWindowParent.getViewTreeObserver(), mSeparateWindowInsetsComputer);
+ mSeparateWindowParent.getViewTreeObserver().addOnComputeInternalInsetsListener(
+ mSeparateWindowInsetsComputer);
}
@Override
public void onViewDetachedFromWindow(View view) {
mSeparateWindowParent.removeOnAttachStateChangeListener(this);
- ViewTreeObserverWrapper.removeOnComputeInsetsListener(
+ mSeparateWindowParent.getViewTreeObserver().removeOnComputeInternalInsetsListener(
mSeparateWindowInsetsComputer);
}
});
@@ -841,7 +897,7 @@
mContext.getDragLayer().addView(mNavButtonsView);
}
- private void onComputeInsetsForSeparateWindow(ViewTreeObserverWrapper.InsetsInfo insetsInfo) {
+ private void onComputeInsetsForSeparateWindow(ViewTreeObserver.InternalInsetsInfo insetsInfo) {
addVisibleButtonsRegion(mSeparateWindowParent, insetsInfo.touchableRegion);
insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
}
@@ -992,9 +1048,9 @@
mAnimator.addListener(new AlphaUpdateListener(view));
}
- StatePropertyHolder(MultiValueAlpha.AlphaProperty alphaProperty,
+ StatePropertyHolder(MultiProperty alphaProperty,
IntPredicate enableCondition) {
- this(alphaProperty, enableCondition, MultiValueAlpha.VALUE, 1, 0);
+ this(alphaProperty, enableCondition, MULTI_PROPERTY_VALUE, 1, 0);
}
StatePropertyHolder(AnimatedFloat animatedFloat, IntPredicate enableCondition) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
index 6b67b50..45d5739 100644
--- a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
@@ -30,7 +30,9 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.RevealOutlineAnimation;
import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
+import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.Executors;
+import com.android.launcher3.util.MultiPropertyFactory;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.quickstep.AnimatedFloat;
import com.android.systemui.shared.navigationbar.RegionSamplingHelper;
@@ -65,6 +67,7 @@
// Initialized in init.
private TaskbarControllers mControllers;
+ private int mTaskbarSize;
// The bounds we want to clip to in the settled state when showing the stashed handle.
private final Rect mStashedHandleBounds = new Rect();
@@ -95,17 +98,20 @@
DeviceProfile deviceProfile = mActivity.getDeviceProfile();
Resources resources = mActivity.getResources();
if (isPhoneGestureNavMode(mActivity.getDeviceProfile())) {
- mStashedHandleView.getLayoutParams().height =
- resources.getDimensionPixelSize(R.dimen.taskbar_size);
+ mTaskbarSize = resources.getDimensionPixelSize(R.dimen.taskbar_size);
mStashedHandleWidth =
resources.getDimensionPixelSize(R.dimen.taskbar_stashed_small_screen);
} else {
- mStashedHandleView.getLayoutParams().height = deviceProfile.taskbarSize;
- mStashedHandleWidth =
- resources.getDimensionPixelSize(R.dimen.taskbar_stashed_handle_width);
+ mTaskbarSize = deviceProfile.taskbarSize;
+ mStashedHandleWidth = resources
+ .getDimensionPixelSize(R.dimen.taskbar_stashed_handle_width);
}
+ int taskbarBottomMargin = DisplayController.isTransientTaskbar(mActivity)
+ ? resources.getDimensionPixelSize(R.dimen.transient_taskbar_margin)
+ : 0;
+ mStashedHandleView.getLayoutParams().height = mTaskbarSize + taskbarBottomMargin;
- mTaskbarStashedHandleAlpha.getProperty(ALPHA_INDEX_STASHED).setValue(
+ mTaskbarStashedHandleAlpha.get(ALPHA_INDEX_STASHED).setValue(
isPhoneGestureNavMode(deviceProfile) ? 1 : 0);
mTaskbarStashedHandleHintScale.updateValue(1f);
@@ -166,7 +172,7 @@
return TaskbarManager.isPhoneMode(deviceProfile) && !mActivity.isThreeButtonNav();
}
- public MultiValueAlpha getStashedHandleAlpha() {
+ public MultiPropertyFactory<View> getStashedHandleAlpha() {
return mTaskbarStashedHandleAlpha;
}
@@ -180,9 +186,17 @@
* morphs into the size of where the taskbar icons will be.
*/
public Animator createRevealAnimToIsStashed(boolean isStashed) {
+ Rect visualBounds = new Rect(mControllers.taskbarViewController.getIconLayoutBounds());
+
+ if (DisplayController.isTransientTaskbar(mActivity)) {
+ // Account for the full visual height of the transient taskbar.
+ int heightDiff = (mTaskbarSize - visualBounds.height()) / 2;
+ visualBounds.top -= heightDiff;
+ visualBounds.bottom += heightDiff;
+ }
+
final RevealOutlineAnimation handleRevealProvider = new RoundedRectRevealOutlineProvider(
- mStashedHandleRadius, mStashedHandleRadius,
- mControllers.taskbarViewController.getIconLayoutBounds(), mStashedHandleBounds);
+ mStashedHandleRadius, mStashedHandleRadius, visualBounds, mStashedHandleBounds);
boolean isReversed = !isStashed;
boolean changingDirection = mWasLastRevealAnimReversed != isReversed;
@@ -219,10 +233,17 @@
}
/**
+ * Sets the translation of the stashed handle during the swipe up gesture.
+ */
+ protected void setTranslationYForSwipe(float transY) {
+ mStashedHandleView.setTranslationY(transY);
+ }
+
+ /**
* Should be called when the home button is disabled, so we can hide this handle as well.
*/
public void setIsHomeButtonDisabled(boolean homeDisabled) {
- mTaskbarStashedHandleAlpha.getProperty(ALPHA_INDEX_HOME_DISABLED).setValue(
+ mTaskbarStashedHandleAlpha.get(ALPHA_INDEX_HOME_DISABLED).setValue(
homeDisabled ? 0 : 1);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index e1bcbe2..27159d3 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -23,7 +23,10 @@
import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE;
+import static com.android.launcher3.Utilities.IS_RUNNING_IN_TEST_HARNESS;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_OPEN;
+import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_DRAGGING;
+import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_FULLSCREEN;
import static com.android.launcher3.taskbar.TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW;
import static com.android.launcher3.testing.shared.ResourceUtils.getBoolByName;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
@@ -75,20 +78,25 @@
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.popup.PopupDataProvider;
+import com.android.launcher3.taskbar.TaskbarAutohideSuspendController.AutohideSuspendFlag;
+import com.android.launcher3.taskbar.TaskbarTranslationController.TransitionCallback;
import com.android.launcher3.taskbar.allapps.TaskbarAllAppsController;
+import com.android.launcher3.taskbar.overlay.TaskbarOverlayController;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.util.DisplayController;
-import com.android.launcher3.util.DisplayController.NavigationMode;
+import com.android.launcher3.util.NavigationMode;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.SettingsCache;
import com.android.launcher3.util.TraceHelper;
import com.android.launcher3.util.ViewCache;
import com.android.launcher3.views.ActivityContext;
+import com.android.quickstep.views.RecentsView;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.rotation.RotationButtonController;
import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.unfold.updates.RotationChangeProvider;
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider;
import java.io.PrintWriter;
@@ -127,20 +135,23 @@
private final boolean mIsUserSetupComplete;
private final boolean mIsNavBarForceVisible;
private final boolean mIsNavBarKidsMode;
+
private boolean mIsDestroyed = false;
// The flag to know if the window is excluded from magnification region computation.
private boolean mIsExcludeFromMagnificationRegion = false;
private boolean mBindingItems = false;
private boolean mAddedWindow = false;
+ // The bounds of the taskbar items relative to TaskbarDragLayer
+ private final Rect mTransientTaskbarBounds = new Rect();
private final TaskbarShortcutMenuAccessibilityDelegate mAccessibilityDelegate;
- public TaskbarActivityContext(Context windowContext, DeviceProfile dp,
+ public TaskbarActivityContext(Context windowContext, DeviceProfile launcherDp,
TaskbarNavButtonController buttonController, ScopedUnfoldTransitionProgressProvider
unfoldTransitionProgressProvider) {
super(windowContext);
- mDeviceProfile = dp.copy(this);
+ mDeviceProfile = launcherDp.copy(this);
final Resources resources = getResources();
@@ -148,11 +159,14 @@
mImeDrawsImeNavBar = getBoolByName(IME_DRAWS_IME_NAV_BAR_RES_NAME, resources, false);
mIsSafeModeEnabled = TraceHelper.allowIpcs("isSafeMode",
() -> getPackageManager().isSafeMode());
- mIsUserSetupComplete = SettingsCache.INSTANCE.get(this).getValue(
+ SettingsCache settingsCache = SettingsCache.INSTANCE.get(this);
+ mIsUserSetupComplete = settingsCache.getValue(
Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE), 0);
- mIsNavBarForceVisible = SettingsCache.INSTANCE.get(this).getValue(
+ mIsNavBarForceVisible = settingsCache.getValue(
Settings.Secure.getUriFor(Settings.Secure.NAV_BAR_KIDS_MODE), 0);
- mIsNavBarKidsMode = SettingsCache.INSTANCE.get(this).getValue(
+ // 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);
updateIconSize(resources);
@@ -167,8 +181,10 @@
mRightCorner = display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_RIGHT);
// Inflate views.
- mDragLayer = (TaskbarDragLayer) mLayoutInflater.inflate(
- R.layout.taskbar, null, false);
+ int taskbarLayout = DisplayController.isTransientTaskbar(this)
+ ? R.layout.transient_taskbar
+ : R.layout.taskbar;
+ mDragLayer = (TaskbarDragLayer) mLayoutInflater.inflate(taskbarLayout, null, false);
TaskbarView taskbarView = mDragLayer.findViewById(R.id.taskbar_view);
TaskbarScrimView taskbarScrimView = mDragLayer.findViewById(R.id.taskbar_scrim);
FrameLayout navButtonsView = mDragLayer.findViewById(R.id.navbuttons_view);
@@ -197,7 +213,9 @@
new TaskbarViewController(this, taskbarView),
new TaskbarScrimViewController(this, taskbarScrimView),
new TaskbarUnfoldAnimationController(this, unfoldTransitionProgressProvider,
- mWindowManager, WindowManagerGlobal.getWindowManagerService()),
+ mWindowManager,
+ new RotationChangeProvider(WindowManagerGlobal.getWindowManagerService(), this,
+ getMainExecutor())),
new TaskbarKeyguardController(this),
new StashedHandleViewController(this, stashedHandleView),
new TaskbarStashController(this),
@@ -205,9 +223,11 @@
new TaskbarAutohideSuspendController(this),
new TaskbarPopupController(this),
new TaskbarForceVisibleImmersiveController(this),
- new TaskbarAllAppsController(this, dp),
+ new TaskbarOverlayController(this, launcherDp),
+ new TaskbarAllAppsController(),
new TaskbarInsetsController(this),
new VoiceInteractionWindowController(this),
+ new TaskbarTranslationController(this),
isDesktopMode
? new DesktopTaskbarRecentAppsController(this)
: TaskbarRecentAppsController.DEFAULT);
@@ -235,10 +255,10 @@
}
/** Updates {@link DeviceProfile} instances for any Taskbar windows. */
- public void updateDeviceProfile(DeviceProfile dp, NavigationMode navMode) {
+ public void updateDeviceProfile(DeviceProfile launcherDp, NavigationMode navMode) {
mNavMode = navMode;
- mControllers.taskbarAllAppsController.updateDeviceProfile(dp);
- mDeviceProfile = dp.copy(this);
+ mControllers.taskbarOverlayController.updateLauncherDeviceProfile(launcherDp);
+ mDeviceProfile = launcherDp.copy(this);
updateIconSize(getResources());
AbstractFloatingView.closeAllOpenViewsExcept(this, false, TYPE_REBIND_SAFE);
@@ -249,12 +269,21 @@
}
private void updateIconSize(Resources resources) {
- float taskbarIconSize = resources.getDimension(R.dimen.taskbar_icon_size);
+ float taskbarIconSize = DisplayController.isTransientTaskbar(this)
+ ? resources.getDimension(R.dimen.transient_taskbar_icon_size)
+ : resources.getDimension(R.dimen.taskbar_icon_size);
mDeviceProfile.updateIconSize(1, resources);
float iconScale = taskbarIconSize / mDeviceProfile.iconSizePx;
mDeviceProfile.updateIconSize(iconScale, resources);
}
+ /**
+ * Returns the View bounds of transient taskbar.
+ */
+ public Rect getTransientTaskbarBounds() {
+ return mTransientTaskbarBounds;
+ }
+
@VisibleForTesting
@Override
public StatsLogManager getStatsLogManager() {
@@ -272,17 +301,31 @@
* @param type The window type to pass to the created WindowManager.LayoutParams.
*/
public WindowManager.LayoutParams createDefaultWindowLayoutParams(int type) {
+ DeviceProfile deviceProfile = getDeviceProfile();
+ // Taskbar is on the logical bottom of the screen
+ boolean isVerticalBarLayout = TaskbarManager.isPhoneMode(deviceProfile) &&
+ deviceProfile.isLandscape;
+
+ int windowFlags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_SLIPPERY
+ | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
+ if (DisplayController.isTransientTaskbar(this)
+ && !IS_RUNNING_IN_TEST_HARNESS) {
+ windowFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
+ }
WindowManager.LayoutParams windowLayoutParams = new WindowManager.LayoutParams(
- MATCH_PARENT,
- mLastRequestedNonFullscreenHeight,
+ isVerticalBarLayout ? mLastRequestedNonFullscreenHeight : MATCH_PARENT,
+ isVerticalBarLayout ? MATCH_PARENT : mLastRequestedNonFullscreenHeight,
type,
- WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_SLIPPERY
- | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
+ windowFlags,
PixelFormat.TRANSLUCENT);
windowLayoutParams.setTitle(WINDOW_TITLE);
windowLayoutParams.packageName = getPackageName();
- windowLayoutParams.gravity = Gravity.BOTTOM;
+ windowLayoutParams.gravity = !isVerticalBarLayout ?
+ Gravity.BOTTOM :
+ Gravity.END; // TODO(b/230394142): seascape
+
windowLayoutParams.setFitInsetsTypes(0);
windowLayoutParams.receiveInsetsIgnoringZOrder = true;
windowLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
@@ -439,7 +482,7 @@
@Override
public void onDragEnd() {
- maybeSetTaskbarWindowNotFullscreen();
+ onDragEndOrViewRemoved();
}
@Override
@@ -508,7 +551,7 @@
private void onNotificationShadeExpandChanged(boolean isExpanded, boolean skipAnim) {
float alpha = isExpanded ? 0 : 1;
AnimatorSet anim = new AnimatorSet();
- anim.play(mControllers.taskbarViewController.getTaskbarIconAlpha().getProperty(
+ anim.play(mControllers.taskbarViewController.getTaskbarIconAlpha().get(
TaskbarViewController.ALPHA_INDEX_NOTIFICATION_EXPANDED).animateToValue(alpha));
if (!isThreeButtonNav()) {
anim.play(mControllers.taskbarDragLayerController.getNotificationShadeBgTaskbar()
@@ -544,24 +587,33 @@
}
/**
+ * Called to update a {@link AutohideSuspendFlag} with a new value.
+ */
+ public void setAutohideSuspendFlag(@AutohideSuspendFlag int flag, boolean newValue) {
+ mControllers.taskbarAutohideSuspendController.updateFlag(flag, newValue);
+ }
+
+ /**
* Updates the TaskbarContainer to MATCH_PARENT vs original Taskbar size.
*/
public void setTaskbarWindowFullscreen(boolean fullscreen) {
- mControllers.taskbarAutohideSuspendController.updateFlag(
- TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_FULLSCREEN, fullscreen);
+ setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_FULLSCREEN, fullscreen);
mIsFullscreen = fullscreen;
setTaskbarWindowHeight(fullscreen ? MATCH_PARENT : mLastRequestedNonFullscreenHeight);
}
/**
- * Reverts Taskbar window to its original size, if all floating views are closed and there is
- * no system drag operation in progress.
+ * Called when drag ends or when a view is removed from the DragLayer.
*/
- void maybeSetTaskbarWindowNotFullscreen() {
- if (AbstractFloatingView.getAnyView(this, TYPE_ALL) == null
- && !mControllers.taskbarDragController.isSystemDragInProgress()) {
+ void onDragEndOrViewRemoved() {
+ boolean isDragInProgress = mControllers.taskbarDragController.isSystemDragInProgress();
+
+ if (!isDragInProgress && !AbstractFloatingView.hasOpenView(this, TYPE_ALL)) {
+ // Reverts Taskbar window to its original size
setTaskbarWindowFullscreen(false);
}
+
+ setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_DRAGGING, isDragInProgress);
}
public boolean isTaskbarWindowFullscreen() {
@@ -608,12 +660,24 @@
* Returns the default height of the window, including the static corner radii above taskbar.
*/
public int getDefaultTaskbarWindowHeight() {
+ Resources resources = getResources();
+
if (FLAG_HIDE_NAVBAR_WINDOW && mDeviceProfile.isPhone) {
- Resources resources = getResources();
return isThreeButtonNav() ?
resources.getDimensionPixelSize(R.dimen.taskbar_size) :
resources.getDimensionPixelSize(R.dimen.taskbar_stashed_size);
}
+
+ if (!isUserSetupComplete()) {
+ return resources.getDimensionPixelSize(R.dimen.taskbar_suw_frame);
+ }
+
+ if (DisplayController.isTransientTaskbar(this)) {
+ return resources.getDimensionPixelSize(R.dimen.transient_taskbar_size)
+ + (2 * resources.getDimensionPixelSize(R.dimen.transient_taskbar_margin))
+ + resources.getDimensionPixelSize(R.dimen.transient_taskbar_shadow_blur);
+ }
+
return mDeviceProfile.taskbarSize + Math.max(getLeftCornerRadius(), getRightCornerRadius());
}
@@ -663,6 +727,7 @@
Task task = (Task) tag;
ActivityManagerWrapper.getInstance().startActivityFromRecents(task.key,
ActivityOptions.makeBasic());
+ mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
} else if (tag instanceof FolderInfo) {
FolderIcon folderIcon = (FolderIcon) view;
Folder folder = folderIcon.getFolder();
@@ -696,41 +761,64 @@
if (info.isDisabled()) {
ItemClickHandler.handleDisabledItemClicked(info, this);
} else {
- Intent intent = new Intent(info.getIntent())
- .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- try {
- if (mIsSafeModeEnabled && !PackageManagerHelper.isSystemApp(this, intent)) {
- Toast.makeText(this, R.string.safemode_shortcut_error,
- Toast.LENGTH_SHORT).show();
- } else if (info.isPromise()) {
- TestLogging.recordEvent(
- TestProtocol.SEQUENCE_MAIN, "start: taskbarPromiseIcon");
- intent = new PackageManagerHelper(this)
- .getMarketIntent(info.getTargetPackage())
- .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- startActivity(intent);
+ TaskbarUIController taskbarUIController = mControllers.uiController;
+ RecentsView recents = taskbarUIController.getRecentsView();
+ if (recents != null
+ && taskbarUIController.getRecentsView().isSplitSelectionActive()) {
+ // If we are selecting a second app for split, launch the split tasks
+ taskbarUIController.triggerSecondAppForSplit(info, info.intent, view);
+ } else {
+ // Else launch the selected task
+ Intent intent = new Intent(info.getIntent())
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ try {
+ if (mIsSafeModeEnabled && !PackageManagerHelper.isSystemApp(this, intent)) {
+ Toast.makeText(this, R.string.safemode_shortcut_error,
+ Toast.LENGTH_SHORT).show();
+ } else if (info.isPromise()) {
+ TestLogging.recordEvent(
+ TestProtocol.SEQUENCE_MAIN, "start: taskbarPromiseIcon");
+ intent = new PackageManagerHelper(this)
+ .getMarketIntent(info.getTargetPackage())
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(intent);
- } else if (info.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
- TestLogging.recordEvent(
- TestProtocol.SEQUENCE_MAIN, "start: taskbarDeepShortcut");
- String id = info.getDeepShortcutId();
- String packageName = intent.getPackage();
- getSystemService(LauncherApps.class)
- .startShortcut(packageName, id, null, null, info.user);
- } else {
- startItemInfoActivity(info);
+ } else if (info.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+ TestLogging.recordEvent(
+ TestProtocol.SEQUENCE_MAIN, "start: taskbarDeepShortcut");
+ String id = info.getDeepShortcutId();
+ String packageName = intent.getPackage();
+ getSystemService(LauncherApps.class)
+ .startShortcut(packageName, id, null, null, info.user);
+ } else {
+ startItemInfoActivity(info);
+ }
+
+ mControllers.uiController.onTaskbarIconLaunched(info);
+ } catch (NullPointerException
+ | ActivityNotFoundException
+ | SecurityException e) {
+ Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT)
+ .show();
+ Log.e(TAG, "Unable to launch. tag=" + info + " intent=" + intent, e);
}
-
- mControllers.uiController.onTaskbarIconLaunched(info);
- } catch (NullPointerException | ActivityNotFoundException | SecurityException e) {
- Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT)
- .show();
- Log.e(TAG, "Unable to launch. tag=" + info + " intent=" + intent, e);
}
+ mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
}
} else if (tag instanceof AppInfo) {
- startItemInfoActivity((AppInfo) tag);
- mControllers.uiController.onTaskbarIconLaunched((AppInfo) tag);
+ AppInfo info = (AppInfo) tag;
+ TaskbarUIController taskbarUIController = mControllers.uiController;
+ RecentsView recents = taskbarUIController.getRecentsView();
+ if (recents != null
+ && taskbarUIController.getRecentsView().isSplitSelectionActive()) {
+ // If we are selecting a second app for split, launch the split tasks
+ taskbarUIController.triggerSecondAppForSplit(info, info.intent, view);
+ } else {
+ // Else launch the selected task
+ startItemInfoActivity((AppInfo) tag);
+ mControllers.uiController.onTaskbarIconLaunched((AppInfo) tag);
+ }
+ mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
} else {
Log.e(TAG, "Unknown type clicked: " + tag);
}
@@ -766,6 +854,34 @@
}
/**
+ * Called when we want to unstash taskbar when user performs swipes up gesture.
+ */
+ public void onSwipeToUnstashTaskbar() {
+ mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(false);
+ }
+
+ /**
+ * Called to start the taskbar translation spring to its settled translation (0).
+ */
+ public void startTranslationSpring() {
+ mControllers.taskbarTranslationController.startSpring();
+ }
+
+ /**
+ * Returns a callback to help monitor the swipe gesture.
+ */
+ public TransitionCallback getTranslationCallbacks() {
+ return mControllers.taskbarTranslationController.getTransitionCallback();
+ }
+
+ /**
+ * Called when a transient Autohide flag suspend status changes.
+ */
+ public void onTransientAutohideSuspendFlagChanged(boolean isSuspended) {
+ mControllers.taskbarStashController.updateTaskbarTimeout(isSuspended);
+ }
+
+ /**
* Called when we detect a motion down or up/cancel in the nav region while stashed.
* @param animateForward Whether to animate towards the unstashed hint state or back to stashed.
*/
@@ -773,11 +889,42 @@
mControllers.taskbarStashController.startUnstashHint(animateForward);
}
+ /**
+ * Enables manual taskbar stashing. This method should only be used for tests that need to
+ * stash/unstash the taskbar.
+ */
+ @VisibleForTesting
+ public void enableManualStashingDuringTests(boolean enableManualStashing) {
+ mControllers.taskbarStashController.enableManualStashingDuringTests(enableManualStashing);
+ }
+
+ /**
+ * Enables the auto timeout for taskbar stashing. This method should only be used for taskbar
+ * testing.
+ */
+ @VisibleForTesting
+ public void enableBlockingTimeoutDuringTests(boolean enableBlockingTimeout) {
+ mControllers.taskbarStashController.enableBlockingTimeoutDuringTests(enableBlockingTimeout);
+ }
+
+ /**
+ * Unstashes the Taskbar if it is stashed. This method should only be used to unstash the
+ * taskbar at the end of a test.
+ */
+ @VisibleForTesting
+ public void unstashTaskbarIfStashed() {
+ if (DisplayController.isTransientTaskbar(this)) {
+ mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(false);
+ } else {
+ mControllers.taskbarStashController.onLongPressToUnstashTaskbar();
+ }
+ }
+
protected boolean isUserSetupComplete() {
return mIsUserSetupComplete;
}
- protected boolean isNavBarKidsModeActive() {
+ public boolean isNavBarKidsModeActive() {
return mIsNavBarKidsMode && isThreeButtonNav();
}
@@ -850,6 +997,10 @@
btv.post(() -> mControllers.taskbarPopupController.showForIcon(btv));
}
+ public boolean isInApp() {
+ return mControllers.taskbarStashController.isInApp();
+ }
+
protected void dumpLogs(String prefix, PrintWriter pw) {
pw.println(prefix + "TaskbarActivityContext:");
@@ -866,4 +1017,9 @@
mControllers.dumpLogs(prefix + "\t", pw);
mDeviceProfile.dump(this, prefix, pw);
}
+
+ @VisibleForTesting
+ public int getTaskbarAllAppsTopPadding() {
+ return mControllers.taskbarAllAppsController.getTaskbarAllAppsTopPadding();
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendController.java
index 3cf9c99..4350e9c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendController.java
@@ -33,21 +33,28 @@
public class TaskbarAutohideSuspendController implements
TaskbarControllers.LoggableTaskbarController {
+ // Taskbar window is fullscreen.
public static final int FLAG_AUTOHIDE_SUSPEND_FULLSCREEN = 1 << 0;
+ // User is dragging item.
public static final int FLAG_AUTOHIDE_SUSPEND_DRAGGING = 1 << 1;
+ // User has touched down but has not lifted finger.
+ public static final int FLAG_AUTOHIDE_SUSPEND_TOUCHING = 1 << 2;
@IntDef(flag = true, value = {
FLAG_AUTOHIDE_SUSPEND_FULLSCREEN,
FLAG_AUTOHIDE_SUSPEND_DRAGGING,
+ FLAG_AUTOHIDE_SUSPEND_TOUCHING,
})
@Retention(RetentionPolicy.SOURCE)
public @interface AutohideSuspendFlag {}
+ private final TaskbarActivityContext mActivity;
private final SystemUiProxy mSystemUiProxy;
private @AutohideSuspendFlag int mAutohideSuspendFlags = 0;
public TaskbarAutohideSuspendController(TaskbarActivityContext activity) {
+ mActivity = activity;
mSystemUiProxy = SystemUiProxy.INSTANCE.get(activity);
}
@@ -59,12 +66,27 @@
* Adds or removes the given flag, then notifies system UI proxy whether to suspend auto-hide.
*/
public void updateFlag(@AutohideSuspendFlag int flag, boolean enabled) {
+ int flagsBefore = mAutohideSuspendFlags;
if (enabled) {
mAutohideSuspendFlags |= flag;
} else {
mAutohideSuspendFlags &= ~flag;
}
- mSystemUiProxy.notifyTaskbarAutohideSuspend(mAutohideSuspendFlags != 0);
+ if (flagsBefore == mAutohideSuspendFlags) {
+ // Nothing has changed, no need to notify.
+ return;
+ }
+
+ boolean isSuspended = isSuspended();
+ mSystemUiProxy.notifyTaskbarAutohideSuspend(isSuspended);
+ mActivity.onTransientAutohideSuspendFlagChanged(isSuspended);
+ }
+
+ /**
+ * Returns true iff taskbar autohide is currently suspended.
+ */
+ public boolean isSuspended() {
+ return mAutohideSuspendFlags != 0;
}
@Override
@@ -79,6 +101,7 @@
appendFlag(str, flags, FLAG_AUTOHIDE_SUSPEND_FULLSCREEN,
"FLAG_AUTOHIDE_SUSPEND_FULLSCREEN");
appendFlag(str, flags, FLAG_AUTOHIDE_SUSPEND_DRAGGING, "FLAG_AUTOHIDE_SUSPEND_DRAGGING");
+ appendFlag(str, flags, FLAG_AUTOHIDE_SUSPEND_TOUCHING, "FLAG_AUTOHIDE_SUSPEND_TOUCHING");
return str.toString();
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
index 1177bdb..ff7e8e9 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
@@ -16,10 +16,16 @@
package com.android.launcher3.taskbar
+import com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound
+import com.android.launcher3.Utilities.mapToRange
+
import android.graphics.Canvas
+import android.graphics.Color
import android.graphics.Paint
import android.graphics.Path
import com.android.launcher3.R
+import com.android.launcher3.anim.Interpolators
+import com.android.launcher3.util.DisplayController
/**
* Helps draw the taskbar background, made up of a rectangle plus two inverted rounded corners.
@@ -28,9 +34,23 @@
val paint: Paint = Paint()
var backgroundHeight = context.deviceProfile.taskbarSize.toFloat()
+ var translationYForSwipe = 0f
- private val leftCornerRadius = context.leftCornerRadius.toFloat()
- private val rightCornerRadius = context.rightCornerRadius.toFloat()
+ private var maxBackgroundHeight = context.deviceProfile.taskbarSize.toFloat()
+ private val transientBackgroundBounds = context.transientTaskbarBounds
+
+ private val isTransientTaskbar = DisplayController.isTransientTaskbar(context);
+
+ private var shadowBlur = 0f
+ private var keyShadowDistance = 0f
+ private var bottomMargin = 0
+
+ private val fullLeftCornerRadius = context.leftCornerRadius.toFloat()
+ private val fullRightCornerRadius = context.rightCornerRadius.toFloat()
+ private var leftCornerRadius = fullLeftCornerRadius
+ private var rightCornerRadius = fullRightCornerRadius
+ private val square: Path = Path()
+ private val circle: Path = Path()
private val invertedLeftCornerPath: Path = Path()
private val invertedRightCornerPath: Path = Path()
@@ -39,13 +59,38 @@
paint.flags = Paint.ANTI_ALIAS_FLAG
paint.style = Paint.Style.FILL
+ if (isTransientTaskbar) {
+ paint.color = context.getColor(R.color.transient_taskbar_background)
+
+ val res = context.resources
+ bottomMargin = res.getDimensionPixelSize(R.dimen.transient_taskbar_margin)
+ shadowBlur = res.getDimension(R.dimen.transient_taskbar_shadow_blur)
+ keyShadowDistance = res.getDimension(R.dimen.transient_taskbar_key_shadow_distance)
+ }
+
+ setCornerRoundness(DEFAULT_ROUNDNESS)
+ }
+
+ /**
+ * Sets the roundness of the round corner above Taskbar. No effect on transient Taskkbar.
+ * @param cornerRoundness 0 has no round corner, 1 has complete round corner.
+ */
+ fun setCornerRoundness(cornerRoundness: Float) {
+ if (isTransientTaskbar && !transientBackgroundBounds.isEmpty) {
+ return
+ }
+
+ leftCornerRadius = fullLeftCornerRadius * cornerRoundness
+ rightCornerRadius = fullRightCornerRadius * cornerRoundness
+
// Create the paths for the inverted rounded corners above the taskbar. Start with a filled
// square, and then subtract out a circle from the appropriate corner.
- val square = Path()
+ square.reset()
square.addRect(0f, 0f, leftCornerRadius, leftCornerRadius, Path.Direction.CW)
- val circle = Path()
+ circle.reset()
circle.addCircle(leftCornerRadius, 0f, leftCornerRadius, Path.Direction.CW)
invertedLeftCornerPath.op(square, circle, Path.Op.DIFFERENCE)
+
square.reset()
square.addRect(0f, 0f, rightCornerRadius, rightCornerRadius, Path.Direction.CW)
circle.reset()
@@ -58,18 +103,49 @@
*/
fun draw(canvas: Canvas) {
canvas.save()
- canvas.translate(0f, canvas.height - backgroundHeight)
+ canvas.translate(0f, canvas.height - backgroundHeight - bottomMargin)
+ if (!isTransientTaskbar || transientBackgroundBounds.isEmpty) {
+ // Draw the background behind taskbar content.
+ canvas.drawRect(0f, 0f, canvas.width.toFloat(), backgroundHeight, paint)
- // Draw the background behind taskbar content.
- canvas.drawRect(0f, 0f, canvas.width.toFloat(), backgroundHeight, paint)
+ // Draw the inverted rounded corners above the taskbar.
+ canvas.translate(0f, -leftCornerRadius)
+ canvas.drawPath(invertedLeftCornerPath, paint)
+ canvas.translate(0f, leftCornerRadius)
+ canvas.translate(canvas.width - rightCornerRadius, -rightCornerRadius)
+ canvas.drawPath(invertedRightCornerPath, paint)
+ } else {
+ // Approximates the stash/unstash animation to transform the background.
+ val scaleFactor = backgroundHeight / maxBackgroundHeight
+ val width = transientBackgroundBounds.width()
+ val widthScale = mapToRange(scaleFactor, 0f, 1f, 0.4f, 1f, Interpolators.LINEAR)
+ val newWidth = widthScale * width
+ val delta = width - newWidth
+ canvas.translate(0f, bottomMargin * ((1f - scaleFactor) / 2f))
- // Draw the inverted rounded corners above the taskbar.
- canvas.translate(0f, -leftCornerRadius)
- canvas.drawPath(invertedLeftCornerPath, paint)
- canvas.translate(0f, leftCornerRadius)
- canvas.translate(canvas.width - rightCornerRadius, -rightCornerRadius)
- canvas.drawPath(invertedRightCornerPath, paint)
+ // Draw shadow.
+ val shadowAlpha = mapToRange(paint.alpha.toFloat(), 0f, 255f, 0f, 25f,
+ Interpolators.LINEAR)
+ paint.setShadowLayer(shadowBlur, 0f, keyShadowDistance,
+ setColorAlphaBound(Color.BLACK, Math.round(shadowAlpha))
+ )
+
+ // Draw background.
+ val radius = backgroundHeight / 2f;
+
+ canvas.drawRoundRect(
+ transientBackgroundBounds.left + (delta / 2f),
+ translationYForSwipe,
+ transientBackgroundBounds.right - (delta / 2f),
+ backgroundHeight + translationYForSwipe,
+ radius, radius, paint
+ )
+ }
canvas.restore()
}
+
+ companion object {
+ const val DEFAULT_ROUNDNESS = 1f
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
index 2b80b75..0328df9 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
@@ -22,6 +22,8 @@
import androidx.annotation.VisibleForTesting;
import com.android.launcher3.taskbar.allapps.TaskbarAllAppsController;
+import com.android.launcher3.taskbar.overlay.TaskbarOverlayController;
+import com.android.quickstep.AnimatedFloat;
import com.android.systemui.shared.rotation.RotationButtonController;
import java.io.PrintWriter;
@@ -54,8 +56,11 @@
public final TaskbarInsetsController taskbarInsetsController;
public final VoiceInteractionWindowController voiceInteractionWindowController;
public final TaskbarRecentAppsController taskbarRecentAppsController;
+ public final TaskbarTranslationController taskbarTranslationController;
+ public final TaskbarOverlayController taskbarOverlayController;
@Nullable private LoggableTaskbarController[] mControllersToLog = null;
+ @Nullable private BackgroundRendererController[] mBackgroundRendererControllers = null;
/** Do not store this controller, as it may change at runtime. */
@NonNull public TaskbarUIController uiController = TaskbarUIController.DEFAULT;
@@ -65,6 +70,9 @@
@Nullable private TaskbarSharedState mSharedState = null;
+ // Roundness property for round corner above taskbar .
+ private final AnimatedFloat mCornerRoundness = new AnimatedFloat(this::updateCornerRoundness);
+
public TaskbarControllers(TaskbarActivityContext taskbarActivityContext,
TaskbarDragController taskbarDragController,
TaskbarNavButtonController navButtonController,
@@ -81,9 +89,11 @@
TaskbarAutohideSuspendController taskbarAutoHideSuspendController,
TaskbarPopupController taskbarPopupController,
TaskbarForceVisibleImmersiveController taskbarForceVisibleImmersiveController,
+ TaskbarOverlayController taskbarOverlayController,
TaskbarAllAppsController taskbarAllAppsController,
TaskbarInsetsController taskbarInsetsController,
VoiceInteractionWindowController voiceInteractionWindowController,
+ TaskbarTranslationController taskbarTranslationController,
TaskbarRecentAppsController taskbarRecentAppsController) {
this.taskbarActivityContext = taskbarActivityContext;
this.taskbarDragController = taskbarDragController;
@@ -101,9 +111,11 @@
this.taskbarAutohideSuspendController = taskbarAutoHideSuspendController;
this.taskbarPopupController = taskbarPopupController;
this.taskbarForceVisibleImmersiveController = taskbarForceVisibleImmersiveController;
+ this.taskbarOverlayController = taskbarOverlayController;
this.taskbarAllAppsController = taskbarAllAppsController;
this.taskbarInsetsController = taskbarInsetsController;
this.voiceInteractionWindowController = voiceInteractionWindowController;
+ this.taskbarTranslationController = taskbarTranslationController;
this.taskbarRecentAppsController = taskbarRecentAppsController;
}
@@ -129,11 +141,13 @@
taskbarEduController.init(this);
taskbarPopupController.init(this);
taskbarForceVisibleImmersiveController.init(this);
+ taskbarOverlayController.init(this);
taskbarAllAppsController.init(this, sharedState.allAppsVisible);
navButtonController.init(this);
taskbarInsetsController.init(this);
voiceInteractionWindowController.init(this);
taskbarRecentAppsController.init(this);
+ taskbarTranslationController.init(this);
mControllersToLog = new LoggableTaskbarController[] {
taskbarDragController, navButtonController, navbarButtonsViewController,
@@ -141,8 +155,13 @@
taskbarUnfoldAnimationController, taskbarKeyguardController,
stashedHandleViewController, taskbarStashController, taskbarEduController,
taskbarAutohideSuspendController, taskbarPopupController, taskbarInsetsController,
+ voiceInteractionWindowController, taskbarTranslationController
+ };
+ mBackgroundRendererControllers = new BackgroundRendererController[] {
+ taskbarDragLayerController, taskbarScrimViewController,
voiceInteractionWindowController
};
+ mCornerRoundness.updateValue(TaskbarBackgroundRenderer.DEFAULT_ROUNDNESS);
mAreAllControllersInitialized = true;
for (Runnable postInitCallback : mPostInitCallbacks) {
@@ -159,12 +178,14 @@
public void onConfigurationChanged(@Config int configChanges) {
navbarButtonsViewController.onConfigurationChanged(configChanges);
+ taskbarDragLayerController.onConfigurationChanged();
}
/**
* Cleans up all controllers.
*/
public void onDestroy() {
+ mAreAllControllersInitialized = false;
mSharedState = null;
navbarButtonsViewController.onDestroy();
@@ -178,13 +199,14 @@
taskbarAutohideSuspendController.onDestroy();
taskbarPopupController.onDestroy();
taskbarForceVisibleImmersiveController.onDestroy();
- taskbarAllAppsController.onDestroy();
+ taskbarOverlayController.onDestroy();
navButtonController.onDestroy();
taskbarInsetsController.onDestroy();
voiceInteractionWindowController.onDestroy();
taskbarRecentAppsController.onDestroy();
mControllersToLog = null;
+ mBackgroundRendererControllers = null;
}
/**
@@ -218,6 +240,23 @@
rotationButtonController.dumpLogs(prefix + "\t", pw);
}
+ /**
+ * Returns a float property that animates roundness of the round corner above Taskbar.
+ */
+ public AnimatedFloat getTaskbarCornerRoundness() {
+ return mCornerRoundness;
+ }
+
+ private void updateCornerRoundness() {
+ if (mBackgroundRendererControllers == null) {
+ return;
+ }
+
+ for (BackgroundRendererController controller : mBackgroundRendererControllers) {
+ controller.setCornerRoundness(mCornerRoundness.value);
+ }
+ }
+
@VisibleForTesting
TaskbarActivityContext getTaskbarActivityContext() {
// Used to mock
@@ -227,4 +266,12 @@
protected interface LoggableTaskbarController {
void dumpLogs(String prefix, PrintWriter pw);
}
+
+ protected interface BackgroundRendererController {
+ /**
+ * Sets the roundness of the round corner above Taskbar.
+ * @param cornerRoundness 0 has no round corner, 1 has complete round corner.
+ */
+ void setCornerRoundness(float cornerRoundness);
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
index 97029fe..3045eca 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
@@ -15,13 +15,16 @@
*/
package com.android.launcher3.taskbar;
+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;
import android.animation.ValueAnimator;
+import android.annotation.NonNull;
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.Intent;
@@ -32,6 +35,7 @@
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.UserHandle;
+import android.util.Pair;
import android.view.DragEvent;
import android.view.MotionEvent;
import android.view.SurfaceControl;
@@ -42,14 +46,12 @@
import androidx.annotation.Nullable;
import com.android.internal.logging.InstanceId;
-import com.android.internal.logging.InstanceIdSequence;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.DragSource;
import com.android.launcher3.DropTarget;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
import com.android.launcher3.accessibility.DragViewStateAnnouncer;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.config.FeatureFlags;
@@ -69,6 +71,8 @@
import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.ItemInfoMatcher;
+import com.android.quickstep.util.LogUtils;
+import com.android.quickstep.util.MultiValueUpdateListener;
import com.android.systemui.shared.recents.model.Task;
import java.io.PrintWriter;
@@ -82,7 +86,8 @@
public class TaskbarDragController extends DragController<BaseTaskbarContext> implements
TaskbarControllers.LoggableTaskbarController {
- private static boolean DEBUG_DRAG_SHADOW_SURFACE = false;
+ private static final boolean DEBUG_DRAG_SHADOW_SURFACE = false;
+ private static final int ANIM_DURATION_RETURN_ICON_TO_TASKBAR = 300;
private final int mDragIconSize;
private final int[] mTempXY = new int[2];
@@ -98,6 +103,8 @@
// Animation for the drag shadow back into position after an unsuccessful drag
private ValueAnimator mReturnAnimator;
+ private boolean mDisallowGlobalDrag;
+ private boolean mDisallowLongClick;
public TaskbarDragController(BaseTaskbarContext activity) {
super(activity);
@@ -109,6 +116,14 @@
mControllers = controllers;
}
+ public void setDisallowGlobalDrag(boolean disallowGlobalDrag) {
+ mDisallowGlobalDrag = disallowGlobalDrag;
+ }
+
+ public void setDisallowLongClick(boolean disallowLongClick) {
+ mDisallowLongClick = disallowLongClick;
+ }
+
/**
* Attempts to start a system drag and drop operation for the given View, using its tag to
* generate the ClipDescription and Intent.
@@ -130,7 +145,7 @@
View view,
@Nullable DragPreviewProvider dragPreviewProvider,
@Nullable Point iconShift) {
- if (!(view instanceof BubbleTextView)) {
+ if (!(view instanceof BubbleTextView) || mDisallowLongClick) {
return false;
}
TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onTaskbarItemLongClick");
@@ -287,11 +302,17 @@
protected void callOnDragStart() {
super.callOnDragStart();
// Pre-drag has ended, start the global system drag.
- AbstractFloatingView.closeAllOpenViews(mActivity);
+ if (mDisallowGlobalDrag) {
+ AbstractFloatingView.closeAllOpenViewsExcept(mActivity, TYPE_TASKBAR_ALL_APPS);
+ } else {
+ AbstractFloatingView.closeAllOpenViews(mActivity);
+ }
+
startSystemDrag((BubbleTextView) mDragObject.originalView);
}
private void startSystemDrag(BubbleTextView btv) {
+ if (mDisallowGlobalDrag) return;
View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(btv) {
@Override
@@ -359,11 +380,11 @@
}
if (clipDescription != null && intent != null) {
+ Pair<InstanceId, com.android.launcher3.logging.InstanceId> instanceIds =
+ LogUtils.getShellShareableInstanceId();
// Need to share the same InstanceId between launcher3 and WM Shell (internal).
- InstanceId internalInstanceId = new InstanceIdSequence(
- com.android.launcher3.logging.InstanceId.INSTANCE_ID_MAX).newInstanceId();
- com.android.launcher3.logging.InstanceId launcherInstanceId =
- new com.android.launcher3.logging.InstanceId(internalInstanceId.getId());
+ InstanceId internalInstanceId = instanceIds.first;
+ com.android.launcher3.logging.InstanceId launcherInstanceId = instanceIds.second;
intent.putExtra(ClipDescription.EXTRA_LOGGING_INSTANCE_ID, internalInstanceId);
@@ -421,6 +442,45 @@
}
@Override
+ protected void endDrag() {
+ if (mDisallowGlobalDrag) {
+ // We need to explicitly set deferDragViewCleanupPostAnimation to true here so the
+ // super call doesn't remove it from the drag layer before the animation completes.
+ // This variable gets set in to false in super.dispatchDropComplete() because it
+ // (rightfully so, perhaps) thinks this drag operation has failed, and does its own
+ // internal cleanup.
+ // Another way to approach this would be to make all of overview a drop target and
+ // accept the drop as successful and then run the setupReturnDragAnimator to simulate
+ // drop failure to the user
+ mDragObject.deferDragViewCleanupPostAnimation = true;
+
+ float fromX = mDragObject.x - mDragObject.xOffset;
+ float fromY = mDragObject.y - mDragObject.yOffset;
+ DragView dragView = mDragObject.dragView;
+ setupReturnDragAnimator(fromX, fromY, (View) mDragObject.originalView,
+ (x, y, scale, alpha) -> {
+ dragView.setTranslationX(x);
+ dragView.setTranslationY(y);
+ dragView.setScaleX(scale);
+ dragView.setScaleY(scale);
+ dragView.setAlpha(alpha);
+ });
+ mReturnAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ callOnDragEnd();
+ dragView.remove();
+ dragView.clearAnimation();
+ mReturnAnimator = null;
+
+ }
+ });
+ mReturnAnimator.start();
+ }
+ super.endDrag();
+ }
+
+ @Override
protected void callOnDragEnd() {
super.callOnDragEnd();
maybeOnDragEnd();
@@ -431,56 +491,20 @@
SurfaceControl dragSurface = dragEvent.getDragSurface();
// For top level icons, the target is the icon itself
- View target = btv;
- Object tag = btv.getTag();
- if (tag instanceof ItemInfo) {
- ItemInfo item = (ItemInfo) tag;
- TaskbarViewController taskbarViewController = mControllers.taskbarViewController;
- if (item.container == CONTAINER_ALL_APPS || item.container == CONTAINER_PREDICTION) {
- // Since all apps closes when the drag starts, target the all apps button instead.
- target = taskbarViewController.getAllAppsButtonView();
- } else if (item.container >= 0) {
- // Since folders close when the drag starts, target the folder icon instead.
- Predicate<ItemInfo> matcher = ItemInfoMatcher.forFolderMatch(
- ItemInfoMatcher.ofItemIds(IntSet.wrap(item.id)));
- target = taskbarViewController.getFirstIconMatch(matcher);
- } else if (item.itemType == ITEM_TYPE_DEEP_SHORTCUT) {
- // Find first icon with same package/user as the deep shortcut.
- Predicate<ItemInfo> packageUserMatcher = ItemInfoMatcher.ofPackages(
- Collections.singleton(item.getTargetPackage()), item.user);
- target = taskbarViewController.getFirstIconMatch(packageUserMatcher);
- }
- }
-
- // Finish any pending return animation before starting a new drag
- if (mReturnAnimator != null) {
- mReturnAnimator.end();
- }
+ View target = findTaskbarTargetForIconView(btv);
float fromX = dragEvent.getX() - dragEvent.getOffsetX();
float fromY = dragEvent.getY() - dragEvent.getOffsetY();
- int[] toPosition = target.getLocationOnScreen();
- float toScale = (float) target.getWidth() / mDragIconSize;
- float toAlpha = (target == btv) ? 1f : 0f;
final ViewRootImpl viewRoot = target.getViewRootImpl();
SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
- mReturnAnimator = ValueAnimator.ofFloat(0f, 1f);
- mReturnAnimator.setDuration(300);
- mReturnAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
- mReturnAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- float t = animation.getAnimatedFraction();
- float accelT = Interpolators.ACCEL_2.getInterpolation(t);
- float scale = 1f - t * (1f - toScale);
- float alpha = 1f - accelT * (1f - toAlpha);
- tx.setPosition(dragSurface, Utilities.mapRange(t, fromX, toPosition[0]),
- Utilities.mapRange(t, fromY, toPosition[1]));
- tx.setScale(dragSurface, scale, scale);
- tx.setAlpha(dragSurface, alpha);
- tx.apply();
- }
- });
+ setupReturnDragAnimator(fromX, fromY, btv,
+ (x, y, scale, alpha) -> {
+ tx.setPosition(dragSurface, x, y);
+ tx.setScale(dragSurface, scale, scale);
+ tx.setAlpha(dragSurface, alpha);
+ tx.apply();
+ });
+
mReturnAnimator.addListener(new AnimatorListenerAdapter() {
private boolean mCanceled = false;
@@ -516,6 +540,68 @@
mReturnAnimator.start();
}
+ private View findTaskbarTargetForIconView(@NonNull View iconView) {
+ Object tag = iconView.getTag();
+ TaskbarViewController taskbarViewController = mControllers.taskbarViewController;
+
+ if (tag instanceof ItemInfo) {
+ ItemInfo item = (ItemInfo) tag;
+ if (item.container == CONTAINER_ALL_APPS || item.container == CONTAINER_PREDICTION) {
+ if (mDisallowGlobalDrag) {
+ // We're dragging in taskbarAllApps, we don't have folders or shortcuts
+ return iconView;
+ }
+ // Since all apps closes when the drag starts, target the all apps button instead.
+ return taskbarViewController.getAllAppsButtonView();
+ } else if (item.container >= 0) {
+ // Since folders close when the drag starts, target the folder icon instead.
+ Predicate<ItemInfo> matcher = ItemInfoMatcher.forFolderMatch(
+ ItemInfoMatcher.ofItemIds(IntSet.wrap(item.id)));
+ return taskbarViewController.getFirstIconMatch(matcher);
+ } else if (item.itemType == ITEM_TYPE_DEEP_SHORTCUT) {
+ // Find first icon with same package/user as the deep shortcut.
+ Predicate<ItemInfo> packageUserMatcher = ItemInfoMatcher.ofPackages(
+ Collections.singleton(item.getTargetPackage()), item.user);
+ return taskbarViewController.getFirstIconMatch(packageUserMatcher);
+ }
+ }
+ return iconView;
+ }
+
+ private void setupReturnDragAnimator(float fromX, float fromY, View originalView,
+ TaskbarReturnPropertiesListener animListener) {
+ // Finish any pending return animation before starting a new return
+ if (mReturnAnimator != null) {
+ mReturnAnimator.end();
+ }
+
+ // For top level icons, the target is the icon itself
+ View target = findTaskbarTargetForIconView(originalView);
+
+ int[] toPosition = target.getLocationOnScreen();
+ float toScale = (float) target.getWidth() / mDragIconSize;
+ float toAlpha = (target == originalView) ? 1f : 0f;
+ MultiValueUpdateListener listener = new MultiValueUpdateListener() {
+ final FloatProp mDx = new FloatProp(fromX, toPosition[0], 0,
+ ANIM_DURATION_RETURN_ICON_TO_TASKBAR, Interpolators.FAST_OUT_SLOW_IN);
+ final FloatProp mDy = new FloatProp(fromY, toPosition[1], 0,
+ ANIM_DURATION_RETURN_ICON_TO_TASKBAR,
+ FAST_OUT_SLOW_IN);
+ 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);
+ @Override
+ public void onUpdate(float percent, boolean initOnly) {
+ animListener.updateDragShadow(mDx.value, mDy.value, mScale.value, mAlpha.value);
+ }
+ };
+ mReturnAnimator = ValueAnimator.ofFloat(0f, 1f);
+ mReturnAnimator.setDuration(ANIM_DURATION_RETURN_ICON_TO_TASKBAR);
+ mReturnAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+ mReturnAnimator.addUpdateListener(listener);
+ }
+
@Override
protected float getX(MotionEvent ev) {
// We will resize to fill the screen while dragging, so use screen coordinates. This ensures
@@ -539,7 +625,7 @@
@Override
protected void exitDrag() {
- if (mDragObject != null) {
+ if (mDragObject != null && !mDisallowGlobalDrag) {
mActivity.getDragLayer().removeView(mDragObject.dragView);
}
}
@@ -555,6 +641,10 @@
return null;
}
+ interface TaskbarReturnPropertiesListener {
+ void updateDragShadow(float x, float y, float scale, float alpha);
+ }
+
@Override
public void dumpLogs(String prefix, PrintWriter pw) {
pw.println(prefix + "TaskbarDragController:");
@@ -565,5 +655,7 @@
pw.println(prefix + "\tmRegistrationY=" + mRegistrationY);
pw.println(prefix + "\tmIsSystemDragInProgress=" + mIsSystemDragInProgress);
pw.println(prefix + "\tisInternalDragInProgess=" + super.isDragging());
+ pw.println(prefix + "\tmDisallowGlobalDrag=" + mDisallowGlobalDrag);
+ pw.println(prefix + "\tmDisallowLongClick=" + mDisallowLongClick);
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
index ee17ad0..d0059f7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
@@ -24,6 +24,7 @@
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewTreeObserver;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -32,9 +33,6 @@
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.views.BaseDragLayer;
-import com.android.systemui.shared.system.ViewTreeObserverWrapper;
-import com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo;
-import com.android.systemui.shared.system.ViewTreeObserverWrapper.OnComputeInsetsListener;
/**
* Top-level ViewGroup that hosts the TaskbarView as well as Views created by it such as Folder.
@@ -42,7 +40,8 @@
public class TaskbarDragLayer extends BaseDragLayer<TaskbarActivityContext> {
private final TaskbarBackgroundRenderer mBackgroundRenderer;
- private final OnComputeInsetsListener mTaskbarInsetsComputer = this::onComputeTaskbarInsets;
+ private final ViewTreeObserver.OnComputeInternalInsetsListener mTaskbarInsetsComputer =
+ this::onComputeTaskbarInsets;
// Initialized in init.
private TaskbarDragLayerController.TaskbarDragLayerCallbacks mControllerCallbacks;
@@ -80,7 +79,7 @@
mControllers = mControllerCallbacks.getTouchControllers();
}
- private void onComputeTaskbarInsets(InsetsInfo insetsInfo) {
+ private void onComputeTaskbarInsets(ViewTreeObserver.InternalInsetsInfo insetsInfo) {
if (mControllerCallbacks != null) {
mControllerCallbacks.updateInsetsTouchability(insetsInfo);
}
@@ -88,7 +87,7 @@
protected void onDestroy(boolean forceDestroy) {
if (forceDestroy) {
- ViewTreeObserverWrapper.removeOnComputeInsetsListener(mTaskbarInsetsComputer);
+ getViewTreeObserver().removeOnComputeInternalInsetsListener(mTaskbarInsetsComputer);
}
}
@@ -99,8 +98,7 @@
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- ViewTreeObserverWrapper.addOnComputeInsetsListener(getViewTreeObserver(),
- mTaskbarInsetsComputer);
+ getViewTreeObserver().addOnComputeInternalInsetsListener(mTaskbarInsetsComputer);
}
@Override
@@ -118,6 +116,22 @@
}
@Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ if (mControllerCallbacks != null) {
+ mControllerCallbacks.tryStashBasedOnMotionEvent(ev);
+ }
+ return super.onInterceptTouchEvent(ev);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ if (mControllerCallbacks != null) {
+ mControllerCallbacks.tryStashBasedOnMotionEvent(ev);
+ }
+ return super.onTouchEvent(ev);
+ }
+
+ @Override
public void onViewRemoved(View child) {
super.onViewRemoved(child);
if (mControllerCallbacks != null) {
@@ -152,6 +166,23 @@
invalidate();
}
+ /**
+ * Sets the roundness of the round corner above Taskbar.
+ * @param cornerRoundness 0 has no round corner, 1 has complete round corner.
+ */
+ protected void setCornerRoundness(float cornerRoundness) {
+ mBackgroundRenderer.setCornerRoundness(cornerRoundness);
+ invalidate();
+ }
+
+ /*
+ * Sets the translation of the background during the swipe up gesture.
+ */
+ protected void setBackgroundTranslationYForSwipe(float translationY) {
+ mBackgroundRenderer.setTranslationYForSwipe(translationY);
+ invalidate();
+ }
+
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
TestLogging.recordMotionEvent(TestProtocol.SEQUENCE_MAIN, "Touch event", ev);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
index ec9760c..e54fc00 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
@@ -16,24 +16,31 @@
package com.android.launcher3.taskbar;
import android.content.res.Resources;
+import android.graphics.Point;
import android.graphics.Rect;
+import android.view.MotionEvent;
+import android.view.ViewTreeObserver;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
+import com.android.launcher3.testing.shared.ResourceUtils;
+import com.android.launcher3.util.DimensionUtils;
+import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.TouchController;
import com.android.quickstep.AnimatedFloat;
-import com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo;
import java.io.PrintWriter;
/**
* Handles properties/data collection, then passes the results to TaskbarDragLayer to render.
*/
-public class TaskbarDragLayerController implements TaskbarControllers.LoggableTaskbarController {
+public class TaskbarDragLayerController implements TaskbarControllers.LoggableTaskbarController,
+ TaskbarControllers.BackgroundRendererController {
private final TaskbarActivityContext mActivity;
private final TaskbarDragLayer mTaskbarDragLayer;
private final int mFolderMargin;
+ private float mGestureHeightYThreshold;
// Alpha properties for taskbar background.
private final AnimatedFloat mBgTaskbar = new AnimatedFloat(this::updateBackgroundAlpha);
@@ -60,6 +67,7 @@
mTaskbarDragLayer = taskbarDragLayer;
final Resources resources = mTaskbarDragLayer.getResources();
mFolderMargin = resources.getDimensionPixelSize(R.dimen.taskbar_folder_margin);
+ updateGestureHeight();
}
public void init(TaskbarControllers controllers) {
@@ -119,6 +127,19 @@
return mBgOffset;
}
+ private void updateGestureHeight() {
+ int gestureHeight = ResourceUtils.getNavbarSize(ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE,
+ mActivity.getResources());
+ mGestureHeightYThreshold = mActivity.getDeviceProfile().heightPx - gestureHeight;
+ }
+
+ /**
+ * Make updates when configuration changes.
+ */
+ public void onConfigurationChanged() {
+ updateGestureHeight();
+ }
+
private void updateBackgroundAlpha() {
final float bgNavbar = mBgNavbar.value;
final float bgTaskbar = mBgTaskbar.value * mKeyguardBgTaskbar.value
@@ -129,12 +150,24 @@
updateNavBarDarkIntensityMultiplier();
}
+ /**
+ * Sets the translation of the background during the swipe up gesture.
+ */
+ public void setTranslationYForSwipe(float transY) {
+ mTaskbarDragLayer.setBackgroundTranslationYForSwipe(transY);
+ }
+
private void updateBackgroundOffset() {
mTaskbarDragLayer.setTaskbarBackgroundOffset(mBgOffset.value);
updateNavBarDarkIntensityMultiplier();
}
+ @Override
+ public void setCornerRoundness(float cornerRoundness) {
+ mTaskbarDragLayer.setCornerRoundness(cornerRoundness);
+ }
+
private void updateNavBarDarkIntensityMultiplier() {
// Zero out the app-requested dark intensity when we're drawing our own background.
float effectiveBgAlpha = mLastSetBackgroundAlpha * (1 - mBgOffset.value);
@@ -155,19 +188,52 @@
*/
public class TaskbarDragLayerCallbacks {
+ private final int[] mTempOutLocation = new int[2];
+
/**
* Called to update the touchable insets.
- * @see InsetsInfo#setTouchableInsets(int)
+ * @see ViewTreeObserver.InternalInsetsInfo#setTouchableInsets(int)
*/
- public void updateInsetsTouchability(InsetsInfo insetsInfo) {
+ public void updateInsetsTouchability(ViewTreeObserver.InternalInsetsInfo insetsInfo) {
mControllers.taskbarInsetsController.updateInsetsTouchability(insetsInfo);
}
/**
+ * Listens to TaskbarDragLayer touch events and responds accordingly.
+ */
+ public void tryStashBasedOnMotionEvent(MotionEvent ev) {
+ if (!DisplayController.isTransientTaskbar(mActivity)) {
+ return;
+ }
+ if (mControllers.taskbarStashController.isStashed()) {
+ return;
+ }
+
+ boolean stashTaskbar = false;
+
+ MotionEvent screenCoordinates = MotionEvent.obtain(ev);
+ if (ev.getAction() == MotionEvent.ACTION_OUTSIDE) {
+ stashTaskbar = true;
+ } else if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ mTaskbarDragLayer.getLocationOnScreen(mTempOutLocation);
+ screenCoordinates.offsetLocation(mTempOutLocation[0], mTempOutLocation[1]);
+
+ if (!mControllers.taskbarViewController.isEventOverAnyItem(screenCoordinates)
+ && screenCoordinates.getY() < mGestureHeightYThreshold) {
+ stashTaskbar = true;
+ }
+ }
+
+ if (stashTaskbar) {
+ mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
+ }
+ }
+
+ /**
* Called when a child is removed from TaskbarDragLayer.
*/
public void onDragLayerViewRemoved() {
- mActivity.maybeSetTaskbarWindowNotFullscreen();
+ mActivity.onDragEndOrViewRemoved();
}
/**
@@ -177,9 +243,12 @@
DeviceProfile deviceProfile = mActivity.getDeviceProfile();
if (TaskbarManager.isPhoneMode(deviceProfile)) {
Resources resources = mActivity.getResources();
- return mActivity.isThreeButtonNav() ?
- resources.getDimensionPixelSize(R.dimen.taskbar_size) :
- resources.getDimensionPixelSize(R.dimen.taskbar_stashed_size);
+ Point taskbarDimensions =
+ DimensionUtils.getTaskbarPhoneDimensions(deviceProfile, resources,
+ TaskbarManager.isPhoneMode(deviceProfile));
+ return taskbarDimensions.y == -1 ?
+ deviceProfile.getDisplayInfo().currentSize.y :
+ taskbarDimensions.y;
} else {
return deviceProfile.taskbarSize;
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduController.java
index 32a3c10..365ec75 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduController.java
@@ -35,6 +35,7 @@
import com.android.launcher3.R;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
import com.android.launcher3.uioverrides.PredictedAppIcon;
import java.io.PrintWriter;
@@ -87,21 +88,20 @@
void showEdu() {
mActivity.setTaskbarWindowFullscreen(true);
mActivity.getDragLayer().post(() -> {
- mTaskbarEduView = (TaskbarEduView) mActivity.getLayoutInflater().inflate(
- R.layout.taskbar_edu, mActivity.getDragLayer(), false);
+ TaskbarOverlayContext overlayContext =
+ mControllers.taskbarOverlayController.requestWindow();
+ mTaskbarEduView = (TaskbarEduView) overlayContext.getLayoutInflater().inflate(
+ R.layout.taskbar_edu, overlayContext.getDragLayer(), false);
mTaskbarEduView.init(new TaskbarEduCallbacks());
+ mControllers.navbarButtonsViewController.setSlideInViewVisible(true);
+ mTaskbarEduView.setOnCloseBeginListener(
+ () -> mControllers.navbarButtonsViewController.setSlideInViewVisible(false));
mTaskbarEduView.addOnCloseListener(() -> mTaskbarEduView = null);
mTaskbarEduView.show();
startAnim(createWaveAnim());
});
}
- void hideEdu() {
- if (mTaskbarEduView != null) {
- mTaskbarEduView.close(true /* animate */);
- }
- }
-
/**
* Starts the given animation, ending the previous animation first if it's still playing.
*/
@@ -217,5 +217,9 @@
v -> mTaskbarEduView.snapToPage(currentPage + 1));
}
}
+
+ int getIconLayoutBoundsWidth() {
+ return mControllers.taskbarViewController.getIconLayoutWidth();
+ }
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduView.java
index 89d67be..bb87f48 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduView.java
@@ -28,10 +28,11 @@
import com.android.launcher3.Insettable;
import com.android.launcher3.R;
+import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
import com.android.launcher3.views.AbstractSlideInView;
/** Education view about the Taskbar. */
-public class TaskbarEduView extends AbstractSlideInView<TaskbarActivityContext>
+public class TaskbarEduView extends AbstractSlideInView<TaskbarOverlayContext>
implements Insettable {
private static final int DEFAULT_OPEN_DURATION = 500;
@@ -39,6 +40,9 @@
private final Rect mInsets = new Rect();
+ // Initialized in init.
+ private TaskbarEduController.TaskbarEduCallbacks mTaskbarEduCallbacks;
+
private Button mStartButton;
private Button mEndButton;
private TaskbarEduPagedView mPagedView;
@@ -56,6 +60,7 @@
if (mPagedView != null) {
mPagedView.setControllerCallbacks(callbacks);
}
+ mTaskbarEduCallbacks = callbacks;
}
@Override
@@ -101,6 +106,22 @@
Settings.Secure.LAUNCHER_TASKBAR_EDUCATION_SHOWING, 0);
}
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+ int contentWidth = Math.min(getContentAreaWidth(), getMeasuredWidth());
+ contentWidth = Math.max(contentWidth, mTaskbarEduCallbacks.getIconLayoutBoundsWidth());
+ int contentAreaWidthSpec = MeasureSpec.makeMeasureSpec(contentWidth, MeasureSpec.EXACTLY);
+
+ mContent.measure(contentAreaWidthSpec, MeasureSpec.UNSPECIFIED);
+ }
+
+ private int getContentAreaWidth() {
+ return mTaskbarEduCallbacks.getIconLayoutBoundsWidth()
+ + getResources().getDimensionPixelSize(R.dimen.taskbar_edu_horizontal_margin) * 2;
+ }
+
/** Show the Education flow. */
public void show() {
attachToContainer();
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarForceVisibleImmersiveController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarForceVisibleImmersiveController.java
index c99cebb..f7aafe0 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarForceVisibleImmersiveController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarForceVisibleImmersiveController.java
@@ -18,22 +18,23 @@
import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_CONTROLS;
import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_ICONS;
+import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS;
+import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
import static com.android.launcher3.taskbar.NavbarButtonsViewController.ALPHA_INDEX_IMMERSIVE_MODE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IMMERSIVE_MODE;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.MotionEvent;
+import android.view.View;
import com.android.launcher3.compat.AccessibilityManagerCompat;
-import com.android.launcher3.util.MultiValueAlpha;
+import com.android.launcher3.util.MultiPropertyFactory;
import com.android.launcher3.util.TouchController;
import com.android.quickstep.AnimatedFloat;
-import java.util.Optional;
-import java.util.function.Consumer;
-
/**
* Controller for taskbar when force visible in immersive mode is set.
*/
@@ -50,8 +51,21 @@
private final Runnable mUndimmingRunnable = this::undimIcons;
private final AnimatedFloat mIconAlphaForDimming = new AnimatedFloat(
this::updateIconDimmingAlpha);
- private final Consumer<MultiValueAlpha> mImmersiveModeAlphaUpdater = alpha -> alpha.getProperty(
- ALPHA_INDEX_IMMERSIVE_MODE).setValue(mIconAlphaForDimming.value);
+ private final View.AccessibilityDelegate mKidsModeAccessibilityDelegate =
+ new View.AccessibilityDelegate() {
+ @Override
+ public boolean performAccessibilityAction(View host, int action, Bundle args) {
+ if (action == ACTION_ACCESSIBILITY_FOCUS || action == ACTION_CLICK) {
+ // Animate undimming of icons on an a11y event, followed by starting the
+ // dimming animation (after its timeout has expired). Both can be called in
+ // succession, as the playing of the two animations in a row is managed by
+ // mHandler's message queue.
+ startIconUndimming();
+ startIconDimming();
+ }
+ return super.performAccessibilityAction(host, action, args);
+ }
+ };
// Initialized in init.
private TaskbarControllers mControllers;
@@ -77,12 +91,21 @@
} else {
startIconUndimming();
}
+ mControllers.navbarButtonsViewController.setHomeButtonAccessibilityDelegate(
+ mKidsModeAccessibilityDelegate);
+ mControllers.navbarButtonsViewController.setBackButtonAccessibilityDelegate(
+ mKidsModeAccessibilityDelegate);
+ } else {
+ mControllers.navbarButtonsViewController.setHomeButtonAccessibilityDelegate(null);
+ mControllers.navbarButtonsViewController.setBackButtonAccessibilityDelegate(null);
}
}
/** Clean up animations. */
public void onDestroy() {
startIconUndimming();
+ mControllers.navbarButtonsViewController.setHomeButtonAccessibilityDelegate(null);
+ mControllers.navbarButtonsViewController.setBackButtonAccessibilityDelegate(null);
}
private void startIconUndimming() {
@@ -117,22 +140,20 @@
}
private void updateIconDimmingAlpha() {
- getBackButtonAlphaOptional().ifPresent(mImmersiveModeAlphaUpdater);
- getHomeButtonAlphaOptional().ifPresent(mImmersiveModeAlphaUpdater);
- }
-
- private Optional<MultiValueAlpha> getBackButtonAlphaOptional() {
if (mControllers == null || mControllers.navbarButtonsViewController == null) {
- return Optional.empty();
+ return;
}
- return Optional.ofNullable(mControllers.navbarButtonsViewController.getBackButtonAlpha());
- }
- private Optional<MultiValueAlpha> getHomeButtonAlphaOptional() {
- if (mControllers == null || mControllers.navbarButtonsViewController == null) {
- return Optional.empty();
+ MultiPropertyFactory<View> ba =
+ mControllers.navbarButtonsViewController.getBackButtonAlpha();
+ if (ba != null) {
+ ba.get(ALPHA_INDEX_IMMERSIVE_MODE).setValue(mIconAlphaForDimming.value);
}
- return Optional.ofNullable(mControllers.navbarButtonsViewController.getHomeButtonAlpha());
+ MultiPropertyFactory<View> ha =
+ mControllers.navbarButtonsViewController.getHomeButtonAlpha();
+ if (ba != null) {
+ ha.get(ALPHA_INDEX_IMMERSIVE_MODE).setValue(mIconAlphaForDimming.value);
+ }
}
@Override
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
index 48fde8f..32c1972 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
@@ -20,18 +20,20 @@
import android.view.InsetsFrameProvider
import android.view.InsetsState.ITYPE_BOTTOM_MANDATORY_GESTURES
import android.view.InsetsState
+import android.view.InsetsState.ITYPE_BOTTOM_TAPPABLE_ELEMENT
+import android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR
+import android.view.ViewTreeObserver
+import android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME
+import android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION
import android.view.WindowManager
import android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD
import android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION
import com.android.launcher3.AbstractFloatingView
-import com.android.launcher3.AbstractFloatingView.TYPE_TASKBAR_ALL_APPS
+import com.android.launcher3.AbstractFloatingView.TYPE_TASKBAR_OVERLAY_PROXY
import com.android.launcher3.DeviceProfile
+import com.android.launcher3.R
import com.android.launcher3.anim.AlphaUpdateListener
import com.android.launcher3.taskbar.TaskbarControllers.LoggableTaskbarController
-import com.android.quickstep.KtR
-import com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo
-import com.android.systemui.shared.system.WindowManagerWrapper
-import com.android.systemui.shared.system.WindowManagerWrapper.*
import java.io.PrintWriter
/**
@@ -40,9 +42,8 @@
class TaskbarInsetsController(val context: TaskbarActivityContext): LoggableTaskbarController {
/** The bottom insets taskbar provides to the IME when IME is visible. */
- val taskbarHeightForIme: Int = context.resources.getDimensionPixelSize(
- KtR.dimen.taskbar_ime_size)
- private val contentRegion: Region = Region()
+ val taskbarHeightForIme: Int = context.resources.getDimensionPixelSize(R.dimen.taskbar_ime_size)
+ private val touchableRegion: Region = Region()
private val deviceProfileChangeListener = { _: DeviceProfile ->
onTaskbarWindowHeightOrInsetsChanged()
}
@@ -55,8 +56,7 @@
this.controllers = controllers
windowLayoutParams = context.windowLayoutParams
- val wmWrapper: WindowManagerWrapper = getInstance()
- wmWrapper.setProvidesInsetsTypes(
+ setProvidesInsetsTypes(
windowLayoutParams,
intArrayOf(
ITYPE_EXTRA_NAVIGATION_BAR,
@@ -76,22 +76,23 @@
}
fun onTaskbarWindowHeightOrInsetsChanged() {
- var contentHeight = controllers.taskbarStashController.contentHeightToReportToApps
- contentRegion.set(0, windowLayoutParams.height - contentHeight,
+ val touchableHeight = controllers.taskbarStashController.touchableHeight
+ touchableRegion.set(0, windowLayoutParams.height - touchableHeight,
context.deviceProfile.widthPx, windowLayoutParams.height)
- var tappableHeight = controllers.taskbarStashController.tappableHeightToReportToApps
+ val contentHeight = controllers.taskbarStashController.contentHeightToReportToApps
+ val tappableHeight = controllers.taskbarStashController.tappableHeightToReportToApps
for (provider in windowLayoutParams.providedInsets) {
if (provider.type == ITYPE_EXTRA_NAVIGATION_BAR) {
- provider.insetsSize = Insets.of(0, 0, 0, contentHeight)
+ provider.insetsSize = getInsetsByNavMode(contentHeight)
} else if (provider.type == ITYPE_BOTTOM_TAPPABLE_ELEMENT
|| provider.type == ITYPE_BOTTOM_MANDATORY_GESTURES) {
- provider.insetsSize = Insets.of(0, 0, 0, tappableHeight)
+ provider.insetsSize = getInsetsByNavMode(tappableHeight)
}
}
- val imeInsetsSize = Insets.of(0, 0, 0, taskbarHeightForIme)
+ val imeInsetsSize = getInsetsByNavMode(taskbarHeightForIme)
// Use 0 insets for the VoiceInteractionWindow (assistant) when gesture nav is enabled.
- val visInsetsSize = Insets.of(0, 0, 0, if (context.isGestureNav) 0 else tappableHeight)
+ val visInsetsSize = getInsetsByNavMode(if (context.isGestureNav) 0 else tappableHeight)
val insetsSizeOverride = arrayOf(
InsetsFrameProvider.InsetsSizeOverride(
TYPE_INPUT_METHOD,
@@ -108,10 +109,37 @@
}
/**
- * Called to update the touchable insets.
- * @see InsetsInfo.setTouchableInsets
+ * @return [Insets] where the [bottomInset] is either used as a bottom inset or
+ * right/left inset if using 3 button nav
*/
- fun updateInsetsTouchability(insetsInfo: InsetsInfo) {
+ private fun getInsetsByNavMode(bottomInset: Int) : Insets {
+ val devicePortrait = !context.deviceProfile.isLandscape
+ if (!TaskbarManager.isPhoneButtonNavMode(context) || devicePortrait) {
+ // Taskbar or portrait phone mode
+ return Insets.of(0, 0, 0, bottomInset)
+ }
+
+ // TODO(b/230394142): seascape
+ return Insets.of(0, 0, bottomInset, 0)
+ }
+
+ /**
+ * Sets {@param providesInsetsTypes} as the inset types provided by {@param params}.
+ * @param params The window layout params.
+ * @param providesInsetsTypes The inset types we would like this layout params to provide.
+ */
+ fun setProvidesInsetsTypes(params: WindowManager.LayoutParams, providesInsetsTypes: IntArray) {
+ params.providedInsets = arrayOfNulls<InsetsFrameProvider>(providesInsetsTypes.size);
+ for (i in providesInsetsTypes.indices) {
+ params.providedInsets[i] = InsetsFrameProvider(providesInsetsTypes[i]);
+ }
+ }
+
+ /**
+ * Called to update the touchable insets.
+ * @see InternalInsetsInfo.setTouchableInsets
+ */
+ fun updateInsetsTouchability(insetsInfo: ViewTreeObserver.InternalInsetsInfo) {
insetsInfo.touchableRegion.setEmpty()
// Always have nav buttons be touchable
controllers.navbarButtonsViewController.addVisibleButtonsRegion(
@@ -120,18 +148,21 @@
var insetsIsTouchableRegion = true
if (context.dragLayer.alpha < AlphaUpdateListener.ALPHA_CUTOFF_THRESHOLD) {
// Let touches pass through us.
- insetsInfo.setTouchableInsets(InsetsInfo.TOUCHABLE_INSETS_REGION)
+ insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION)
} else if (controllers.navbarButtonsViewController.isImeVisible) {
- insetsInfo.setTouchableInsets(InsetsInfo.TOUCHABLE_INSETS_REGION)
+ insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION)
} else if (!controllers.uiController.isTaskbarTouchable) {
// Let touches pass through us.
- insetsInfo.setTouchableInsets(InsetsInfo.TOUCHABLE_INSETS_REGION)
+ insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION)
} else if (controllers.taskbarDragController.isSystemDragInProgress) {
// Let touches pass through us.
- insetsInfo.setTouchableInsets(InsetsInfo.TOUCHABLE_INSETS_REGION)
- } else if (AbstractFloatingView.hasOpenView(context, TYPE_TASKBAR_ALL_APPS)) {
- // Let touches pass through us.
- insetsInfo.setTouchableInsets(InsetsInfo.TOUCHABLE_INSETS_REGION)
+ insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION)
+ } else if (AbstractFloatingView.hasOpenView(context, TYPE_TASKBAR_OVERLAY_PROXY)) {
+ // Let touches pass through us if icons are hidden.
+ if (controllers.taskbarViewController.areIconsVisible()) {
+ insetsInfo.touchableRegion.set(touchableRegion)
+ }
+ insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION)
} else if (controllers.taskbarViewController.areIconsVisible()
|| AbstractFloatingView.hasOpenView(context, AbstractFloatingView.TYPE_ALL)
|| context.isNavBarKidsModeActive
@@ -139,15 +170,15 @@
// Taskbar has some touchable elements, take over the full taskbar area
insetsInfo.setTouchableInsets(
if (context.isTaskbarWindowFullscreen) {
- InsetsInfo.TOUCHABLE_INSETS_FRAME
+ TOUCHABLE_INSETS_FRAME
} else {
- insetsInfo.touchableRegion.set(contentRegion)
- InsetsInfo.TOUCHABLE_INSETS_REGION
+ insetsInfo.touchableRegion.set(touchableRegion)
+ TOUCHABLE_INSETS_REGION
}
)
insetsIsTouchableRegion = false
} else {
- insetsInfo.setTouchableInsets(InsetsInfo.TOUCHABLE_INSETS_REGION)
+ insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION)
}
context.excludeFromMagnificationRegion(insetsIsTouchableRegion)
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index 0bda3cd..ea5df87 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -31,14 +31,15 @@
import androidx.annotation.Nullable;
import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.LauncherState;
import com.android.launcher3.QuickstepTransitionManager;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.statemanager.StateManager;
-import com.android.launcher3.util.MultiValueAlpha;
+import com.android.launcher3.uioverrides.QuickstepLauncher;
+import com.android.launcher3.uioverrides.states.OverviewState;
+import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.RecentsAnimationCallbacks;
import com.android.quickstep.RecentsAnimationController;
@@ -49,8 +50,6 @@
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.StringJoiner;
-import java.util.function.Consumer;
-import java.util.function.Supplier;
/**
* Track LauncherState, RecentsAnimation, resumed state for task bar in one place here and animate
@@ -65,20 +64,18 @@
public static final int FLAG_RECENTS_ANIMATION_RUNNING = 1 << 1;
public static final int FLAG_TRANSITION_STATE_RUNNING = 1 << 2;
+ private static final int FLAGS_LAUNCHER = FLAG_RESUMED | FLAG_RECENTS_ANIMATION_RUNNING;
/** Equivalent to an int with all 1s for binary operation purposes */
private static final int FLAGS_ALL = ~0;
- private final AnimatedFloat mIconAlignmentForResumedState =
- new AnimatedFloat(this::onIconAlignmentRatioChangedForAppAndHomeTransition);
- private final AnimatedFloat mIconAlignmentForGestureState =
- new AnimatedFloat(this::onIconAlignmentRatioChangedForAppAndHomeTransition);
- private final AnimatedFloat mIconAlignmentForLauncherState =
- new AnimatedFloat(this::onIconAlignmentRatioChangedForStateTransition);
+ private final AnimatedFloat mIconAlignment =
+ new AnimatedFloat(this::onIconAlignmentRatioChanged);
private TaskbarControllers mControllers;
private AnimatedFloat mTaskbarBackgroundAlpha;
- private MultiValueAlpha.AlphaProperty mIconAlphaForHome;
- private BaseQuickstepLauncher mLauncher;
+ private AnimatedFloat mTaskbarCornerRoundness;
+ private MultiProperty mIconAlphaForHome;
+ private QuickstepLauncher mLauncher;
private Integer mPrevState;
private int mState;
@@ -86,22 +83,15 @@
private @Nullable TaskBarRecentsAnimationListener mTaskBarRecentsAnimationListener;
- private boolean mIsAnimatingToLauncherViaGesture;
- private boolean mIsAnimatingToLauncherViaResume;
+ private boolean mIsAnimatingToLauncher;
private boolean mShouldDelayLauncherStateAnim;
// We skip any view synchronizations during init/destroy.
private boolean mCanSyncViews;
- private final Consumer<Float> mIconAlphaForHomeConsumer = alpha -> {
- mLauncher.getHotseat().setIconsAlpha(alpha > 0 ? 0 : 1);
- mLauncher.getHotseat().setQsbAlpha(
- mLauncher.getDeviceProfile().isQsbInline && alpha > 0 ? 0 : 1);
- };
-
private final DeviceProfile.OnDeviceProfileChangeListener mOnDeviceProfileChangeListener =
- dp -> mIconAlphaForHomeConsumer.accept(mIconAlphaForHome.getValue());
+ dp -> updateIconAlphaForHome(mIconAlphaForHome.getValue());
private final StateManager.StateListener<LauncherState> mStateListener =
new StateManager.StateListener<LauncherState>() {
@@ -129,10 +119,17 @@
mLauncherState = finalState;
updateStateForFlag(FLAG_TRANSITION_STATE_RUNNING, false);
applyState();
+ boolean disallowGlobalDrag = finalState instanceof OverviewState;
+ boolean disallowLongClick = finalState == LauncherState.OVERVIEW_SPLIT_SELECT;
+ mControllers.taskbarDragController.setDisallowGlobalDrag(disallowGlobalDrag);
+ mControllers.taskbarDragController.setDisallowLongClick(disallowLongClick);
+ mControllers.taskbarAllAppsController.setDisallowGlobalDrag(disallowGlobalDrag);
+ mControllers.taskbarAllAppsController.setDisallowLongClick(disallowLongClick);
+ mControllers.taskbarPopupController.setHideSplitOptions(disallowGlobalDrag);
}
};
- public void init(TaskbarControllers controllers, BaseQuickstepLauncher launcher) {
+ public void init(TaskbarControllers controllers, QuickstepLauncher launcher) {
mCanSyncViews = false;
mControllers = controllers;
@@ -140,12 +137,12 @@
mTaskbarBackgroundAlpha = mControllers.taskbarDragLayerController
.getTaskbarBackgroundAlpha();
- MultiValueAlpha taskbarIconAlpha = mControllers.taskbarViewController.getTaskbarIconAlpha();
- mIconAlphaForHome = taskbarIconAlpha.getProperty(ALPHA_INDEX_HOME);
- mIconAlphaForHome.setConsumer(mIconAlphaForHomeConsumer);
+ mTaskbarCornerRoundness = mControllers.getTaskbarCornerRoundness();
+ mIconAlphaForHome = mControllers.taskbarViewController
+ .getTaskbarIconAlpha().get(ALPHA_INDEX_HOME);
- mIconAlignmentForResumedState.finishAnimation();
- onIconAlignmentRatioChangedForAppAndHomeTransition();
+ mIconAlignment.finishAnimation();
+ onIconAlignmentRatioChanged();
mLauncher.getStateManager().addStateListener(mStateListener);
@@ -161,11 +158,8 @@
public void onDestroy() {
mCanSyncViews = false;
- mIconAlignmentForResumedState.finishAnimation();
- mIconAlignmentForGestureState.finishAnimation();
- mIconAlignmentForLauncherState.finishAnimation();
+ mIconAlignment.finishAnimation();
- mIconAlphaForHome.setConsumer(null);
mLauncher.getHotseat().setIconsAlpha(1f);
mLauncher.getStateManager().removeStateListener(mStateListener);
@@ -183,6 +177,9 @@
TaskbarStashController stashController = mControllers.taskbarStashController;
stashController.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE,
toState.isTaskbarStashed(mLauncher));
+ if (DEBUG) {
+ Log.d(TAG, "createAnimToLauncher - FLAG_IN_APP: " + false);
+ }
stashController.updateStateForFlag(FLAG_IN_APP, false);
updateStateForFlag(FLAG_RECENTS_ANIMATION_RUNNING, true);
@@ -197,7 +194,7 @@
}
public boolean isAnimatingToLauncher() {
- return mIsAnimatingToLauncherViaResume || mIsAnimatingToLauncherViaGesture;
+ return mIsAnimatingToLauncher;
}
public void setShouldDelayLauncherStateAnim(boolean shouldDelayLauncherStateAnim) {
@@ -257,11 +254,19 @@
}
private Animator onStateChangeApplied(int changedFlags, long duration, boolean start) {
+ boolean goingToLauncher = isInLauncher();
+ final float toAlignment = isIconAlignedWithHotseat() ? 1 : 0;
+ if (DEBUG) {
+ Log.d(TAG, "onStateChangeApplied - mState: " + getStateString(mState)
+ + ", changedFlags: " + getStateString(changedFlags)
+ + ", goingToLauncher: " + goingToLauncher
+ + ", mLauncherState: " + mLauncherState
+ + ", toAlignment: " + toAlignment);
+ }
AnimatorSet animatorSet = new AnimatorSet();
// Add the state animation first to ensure FLAG_IN_STASHED_LAUNCHER_STATE is set and we can
// determine whether goingToUnstashedLauncherStateChanged.
- boolean wasGoingToUnstashedLauncherState = goingToUnstashedLauncherState();
if (hasAnyFlag(changedFlags, FLAG_TRANSITION_STATE_RUNNING)) {
boolean committed = !hasAnyFlag(FLAG_TRANSITION_STATE_RUNNING);
playStateTransitionAnim(animatorSet, duration, committed);
@@ -272,95 +277,83 @@
applyState(0 /* duration */);
}
}
- boolean goingToUnstashedLauncherStateChanged = wasGoingToUnstashedLauncherState
- != goingToUnstashedLauncherState();
- boolean launcherStateChangedDuringAnimToResumeAlignment =
- mIconAlignmentForResumedState.isAnimating() && goingToUnstashedLauncherStateChanged;
- if (hasAnyFlag(changedFlags, FLAG_RESUMED)
- || launcherStateChangedDuringAnimToResumeAlignment) {
- boolean isResumed = isResumed();
- // If launcher is resumed, we show the icons when going to an unstashed launcher state
- // or launcher state is not changed (e.g. in overview, launcher is paused and resumed).
- float toAlignmentForResumedState = isResumed && (goingToUnstashedLauncherState()
- || !goingToUnstashedLauncherStateChanged) ? 1 : 0;
- // If we're already animating to the value, just leave it be instead of restarting it.
- if (!mIconAlignmentForResumedState.isAnimatingToValue(toAlignmentForResumedState)) {
- ObjectAnimator resumeAlignAnim = mIconAlignmentForResumedState
- .animateToValue(toAlignmentForResumedState)
- .setDuration(duration);
- if (DEBUG) {
- Log.d(TAG, "mIconAlignmentForResumedState - "
- + mIconAlignmentForResumedState.value
- + " -> " + toAlignmentForResumedState + ": " + duration);
+ if (hasAnyFlag(changedFlags, FLAGS_LAUNCHER)) {
+ animatorSet.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mIsAnimatingToLauncher = false;
}
- resumeAlignAnim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mIsAnimatingToLauncherViaResume = false;
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mIsAnimatingToLauncher = goingToLauncher;
+
+ TaskbarStashController stashController =
+ mControllers.taskbarStashController;
+ if (DEBUG) {
+ Log.d(TAG, "onAnimationStart - FLAG_IN_APP: " + !goingToLauncher);
}
-
- @Override
- public void onAnimationStart(Animator animation) {
- mIsAnimatingToLauncherViaResume = isResumed;
-
- TaskbarStashController stashController =
- mControllers.taskbarStashController;
- stashController.updateStateForFlag(FLAG_IN_APP, !isResumed);
- stashController.applyState(duration);
- }
- });
- animatorSet.play(resumeAlignAnim);
- }
- }
-
-
- boolean launcherStateChangedDuringAnimToGestureAlignment =
- mIconAlignmentForGestureState.isAnimating() && goingToUnstashedLauncherStateChanged;
- if (hasAnyFlag(changedFlags, FLAG_RECENTS_ANIMATION_RUNNING)
- || launcherStateChangedDuringAnimToGestureAlignment) {
- boolean isRecentsAnimationRunning = isRecentsAnimationRunning();
- float toAlignmentForGestureState = isRecentsAnimationRunning
- && goingToUnstashedLauncherState() ? 1 : 0;
- // If we're already animating to the value, just leave it be instead of restarting it.
- if (!mIconAlignmentForGestureState.isAnimatingToValue(toAlignmentForGestureState)) {
- Animator gestureAlignAnim = mIconAlignmentForGestureState
- .animateToValue(toAlignmentForGestureState);
- if (isRecentsAnimationRunning) {
- gestureAlignAnim.setDuration(duration);
+ stashController.updateStateForFlag(FLAG_IN_APP, !goingToLauncher);
+ stashController.applyState(duration);
}
- if (DEBUG) {
- Log.d(TAG, "mIconAlignmentForGestureState - "
- + mIconAlignmentForGestureState.value
- + " -> " + toAlignmentForGestureState + ": " + duration);
- }
- gestureAlignAnim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mIsAnimatingToLauncherViaGesture = false;
- }
+ });
- @Override
- public void onAnimationStart(Animator animation) {
- mIsAnimatingToLauncherViaGesture = isRecentsAnimationRunning();
- }
- });
- animatorSet.play(gestureAlignAnim);
- }
- }
-
- if (hasAnyFlag(changedFlags, FLAG_RESUMED | FLAG_RECENTS_ANIMATION_RUNNING)) {
- boolean goingToLauncher = hasAnyFlag(FLAG_RESUMED | FLAG_RECENTS_ANIMATION_RUNNING);
if (goingToLauncher) {
// Handle closing open popups when going home/overview
AbstractFloatingView.closeAllOpenViews(mControllers.taskbarActivityContext);
}
- animatorSet.play(mTaskbarBackgroundAlpha.animateToValue(goingToLauncher ? 0 : 1)
+ }
+
+ float backgroundAlpha =
+ goingToLauncher && mLauncherState.isTaskbarAlignedWithHotseat(mLauncher)
+ ? 0 : 1;
+ // Don't animate if background has reached desired value.
+ if (mTaskbarBackgroundAlpha.isAnimating()
+ || mTaskbarBackgroundAlpha.value != backgroundAlpha) {
+ mTaskbarBackgroundAlpha.cancelAnimation();
+ if (DEBUG) {
+ Log.d(TAG, "onStateChangeApplied - taskbarBackgroundAlpha - "
+ + mTaskbarBackgroundAlpha.value
+ + " -> " + backgroundAlpha + ": " + duration);
+ }
+ animatorSet.play(mTaskbarBackgroundAlpha.animateToValue(backgroundAlpha)
.setDuration(duration));
}
+ float cornerRoundness = goingToLauncher ? 0 : 1;
+ // Don't animate if corner roundness has reached desired value.
+ if (mTaskbarCornerRoundness.isAnimating()
+ || mTaskbarCornerRoundness.value != cornerRoundness) {
+ mTaskbarCornerRoundness.cancelAnimation();
+ if (DEBUG) {
+ Log.d(TAG, "onStateChangeApplied - taskbarCornerRoundness - "
+ + mTaskbarCornerRoundness.value
+ + " -> " + cornerRoundness + ": " + duration);
+ }
+ animatorSet.play(mTaskbarCornerRoundness.animateToValue(cornerRoundness));
+ }
+
+ if (mIconAlignment.isAnimatingToValue(toAlignment)
+ || mIconAlignment.isSettledOnValue(toAlignment)) {
+ // Already at desired value, but make sure we run the callback at the end.
+ animatorSet.addListener(AnimatorListeners.forEndCallback(
+ this::onIconAlignmentRatioChanged));
+ } else {
+ mIconAlignment.cancelAnimation();
+ ObjectAnimator iconAlignAnim = mIconAlignment
+ .animateToValue(toAlignment)
+ .setDuration(duration);
+ if (DEBUG) {
+ Log.d(TAG, "onStateChangeApplied - iconAlignment - "
+ + mIconAlignment.value
+ + " -> " + toAlignment + ": " + duration);
+ }
+ animatorSet.play(iconAlignAnim);
+ }
+
animatorSet.setInterpolator(EMPHASIZED);
+
if (start) {
animatorSet.start();
}
@@ -368,18 +361,29 @@
}
/** Returns whether we're going to a state where taskbar icons should align with launcher. */
- private boolean goingToUnstashedLauncherState() {
- return !mControllers.taskbarStashController.isInStashedLauncherState();
+ public boolean goingToAlignedLauncherState() {
+ return mLauncherState.isTaskbarAlignedWithHotseat(mLauncher);
+ }
+
+ /**
+ * Returns if icons should be aligned to hotseat in the current transition
+ */
+ public boolean isIconAlignedWithHotseat() {
+ if (isInLauncher()) {
+ boolean isInStashedState = mLauncherState.isTaskbarStashed(mLauncher);
+ boolean willStashVisually = isInStashedState
+ && mControllers.taskbarStashController.supportsVisualStashing();
+ boolean isTaskbarAlignedWithHotseat =
+ mLauncherState.isTaskbarAlignedWithHotseat(mLauncher);
+ return isTaskbarAlignedWithHotseat && !willStashVisually;
+ } else {
+ return false;
+ }
}
private void playStateTransitionAnim(AnimatorSet animatorSet, long duration,
boolean committed) {
boolean isInStashedState = mLauncherState.isTaskbarStashed(mLauncher);
- boolean willStashVisually =
- isInStashedState && mControllers.taskbarStashController.supportsVisualStashing();
- float toAlignment =
- mLauncherState.isTaskbarAlignedWithHotseat(mLauncher) && !willStashVisually ? 1 : 0;
-
TaskbarStashController stashController = mControllers.taskbarStashController;
stashController.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE, isInStashedState);
Animator stashAnimator = stashController.applyStateWithoutStart(duration);
@@ -396,61 +400,29 @@
@Override
public void onAnimationStart(Animator animation) {
if (mLauncher.getHotseat().getIconsAlpha() > 0) {
- mIconAlphaForHome.setValue(mLauncher.getHotseat().getIconsAlpha());
+ updateIconAlphaForHome(mLauncher.getHotseat().getIconsAlpha());
}
}
});
animatorSet.play(stashAnimator);
}
- if (mIconAlignmentForLauncherState.value == toAlignment) {
- // Already at expected value, but make sure we run the callback at the end.
- animatorSet.addListener(AnimatorListeners.forEndCallback(
- this::onIconAlignmentRatioChangedForStateTransition));
- }
- if (!mIconAlignmentForLauncherState.isAnimatingToValue(toAlignment)) {
- // If we're already animating to the value, just leave it be instead of restarting it.
- mIconAlignmentForLauncherState.finishAnimation();
- animatorSet.play(mIconAlignmentForLauncherState.animateToValue(toAlignment)
- .setDuration(duration));
- if (DEBUG) {
- Log.d(TAG, "mIconAlignmentForLauncherState - "
- + mIconAlignmentForLauncherState.value
- + " -> " + toAlignment + ": " + duration);
- }
- animatorSet.setInterpolator(EMPHASIZED);
- }
}
- private boolean isResumed() {
- return (mState & FLAG_RESUMED) != 0;
+ private boolean isInLauncher() {
+ return (mState & FLAGS_LAUNCHER) != 0;
}
- private boolean isRecentsAnimationRunning() {
- return (mState & FLAG_RECENTS_ANIMATION_RUNNING) != 0;
- }
-
- private void onIconAlignmentRatioChangedForStateTransition() {
- if (!isResumed() && mTaskBarRecentsAnimationListener == null) {
- return;
- }
- onIconAlignmentRatioChanged(this::getCurrentIconAlignmentRatioForLauncherState);
- }
-
- private void onIconAlignmentRatioChangedForAppAndHomeTransition() {
- onIconAlignmentRatioChanged(this::getCurrentIconAlignmentRatioBetweenAppAndHome);
- }
-
- private void onIconAlignmentRatioChanged(Supplier<AnimatedFloat> alignmentSupplier) {
- if (mControllers == null) {
- return;
- }
- AnimatedFloat animatedFloat = alignmentSupplier.get();
+ private void onIconAlignmentRatioChanged() {
float currentValue = mIconAlphaForHome.getValue();
- boolean taskbarWillBeVisible = animatedFloat.value < 1;
+ boolean taskbarWillBeVisible = mIconAlignment.value < 1;
boolean firstFrameVisChanged = (taskbarWillBeVisible && Float.compare(currentValue, 1) != 0)
|| (!taskbarWillBeVisible && Float.compare(currentValue, 0) != 0);
- updateIconAlignment(animatedFloat.value, animatedFloat.getEndValue());
+ mControllers.taskbarViewController.setLauncherIconAlignment(
+ mIconAlignment.value, mLauncher.getDeviceProfile());
+ mControllers.navbarButtonsViewController.updateTaskbarAlignment(mIconAlignment.value);
+ // Switch taskbar and hotseat in last frame
+ updateIconAlphaForHome(taskbarWillBeVisible ? 1 : 0);
// Sync the first frame where we swap taskbar and hotseat.
if (firstFrameVisChanged && mCanSyncViews && !Utilities.IS_RUNNING_IN_TEST_HARNESS) {
@@ -460,26 +432,16 @@
}
}
- private void updateIconAlignment(float alignment, Float endAlignment) {
- mControllers.taskbarViewController.setLauncherIconAlignment(
- alignment, endAlignment, mLauncher.getDeviceProfile());
+ private void updateIconAlphaForHome(float alpha) {
+ mIconAlphaForHome.setValue(alpha);
- // Switch taskbar and hotseat in last frame
- setTaskbarViewVisible(alignment < 1);
- mControllers.navbarButtonsViewController.updateTaskbarAlignment(alignment);
- }
-
- private AnimatedFloat getCurrentIconAlignmentRatioBetweenAppAndHome() {
- return mIconAlignmentForResumedState.value > mIconAlignmentForGestureState.value
- ? mIconAlignmentForResumedState : mIconAlignmentForGestureState;
- }
-
- private AnimatedFloat getCurrentIconAlignmentRatioForLauncherState() {
- return mIconAlignmentForLauncherState;
- }
-
- private void setTaskbarViewVisible(boolean isVisible) {
- mIconAlphaForHome.setValue(isVisible ? 1 : 0);
+ /*
+ * Hide Launcher Hotseat icons when Taskbar icons have opacity. Both icon sets
+ * should not be visible at the same time.
+ */
+ mLauncher.getHotseat().setIconsAlpha(alpha > 0 ? 0 : 1);
+ mLauncher.getHotseat().setQsbAlpha(
+ mLauncher.getDeviceProfile().isQsbInline && alpha > 0 ? 0 : 1);
}
private final class TaskBarRecentsAnimationListener implements
@@ -511,11 +473,11 @@
updateStateForFlag(FLAG_RECENTS_ANIMATION_RUNNING, false);
updateStateForFlag(FLAG_RESUMED, launcherResumed);
applyState();
- // Set this last because applyState() might also animate it.
- mIconAlignmentForResumedState.cancelAnimation();
- mIconAlignmentForResumedState.updateValue(launcherResumed ? 1 : 0);
TaskbarStashController controller = mControllers.taskbarStashController;
+ if (DEBUG) {
+ Log.d(TAG, "endGestureStateOverride - FLAG_IN_APP: " + finishedToApp);
+ }
controller.updateStateForFlag(FLAG_IN_APP, finishedToApp);
controller.applyState();
}
@@ -523,29 +485,24 @@
private static String getStateString(int flags) {
StringJoiner str = new StringJoiner("|");
- str.add((flags & FLAG_RESUMED) != 0 ? "FLAG_RESUMED" : "");
- str.add((flags & FLAG_RECENTS_ANIMATION_RUNNING) != 0
- ? "FLAG_RECENTS_ANIMATION_RUNNING" : "");
- str.add((flags & FLAG_TRANSITION_STATE_RUNNING) != 0
- ? "FLAG_TRANSITION_STATE_RUNNING" : "");
+ if ((flags & FLAG_RESUMED) != 0) {
+ str.add("FLAG_RESUMED");
+ }
+ if ((flags & FLAG_RECENTS_ANIMATION_RUNNING) != 0) {
+ str.add("FLAG_RECENTS_ANIMATION_RUNNING");
+ }
+ if ((flags & FLAG_TRANSITION_STATE_RUNNING) != 0) {
+ str.add("FLAG_TRANSITION_STATE_RUNNING");
+ }
return str.toString();
}
protected void dumpLogs(String prefix, PrintWriter pw) {
pw.println(prefix + "TaskbarLauncherStateController:");
-
pw.println(String.format(
- "%s\tmIconAlignmentForResumedState=%.2f",
+ "%s\tmIconAlignment=%.2f",
prefix,
- mIconAlignmentForResumedState.value));
- pw.println(String.format(
- "%s\tmIconAlignmentForGestureState=%.2f",
- prefix,
- mIconAlignmentForGestureState.value));
- pw.println(String.format(
- "%s\tmIconAlignmentForLauncherState=%.2f",
- prefix,
- mIconAlignmentForLauncherState.value));
+ mIconAlignment.value));
pw.println(String.format(
"%s\tmTaskbarBackgroundAlpha=%.2f", prefix, mTaskbarBackgroundAlpha.value));
pw.println(String.format(
@@ -554,13 +511,9 @@
pw.println(String.format("%s\tmState=%s", prefix, getStateString(mState)));
pw.println(String.format("%s\tmLauncherState=%s", prefix, mLauncherState));
pw.println(String.format(
- "%s\tmIsAnimatingToLauncherViaGesture=%b",
+ "%s\tmIsAnimatingToLauncher=%b",
prefix,
- mIsAnimatingToLauncherViaGesture));
- pw.println(String.format(
- "%s\tmIsAnimatingToLauncherViaResume=%b",
- prefix,
- mIsAnimatingToLauncherViaResume));
+ mIsAnimatingToLauncher));
pw.println(String.format(
"%s\tmShouldDelayLauncherStateAnim=%b", prefix, mShouldDelayLauncherStateAnim));
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index 1212c61..bc69088 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -36,14 +36,16 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
-import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.LauncherAppState;
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.SettingsCache;
import com.android.launcher3.util.SimpleBroadcastReceiver;
import com.android.quickstep.RecentsActivity;
@@ -82,7 +84,7 @@
// It's destruction/creation will be managed by the activity.
private final ScopedUnfoldTransitionProgressProvider mUnfoldProgressProvider =
new NonDestroyableScopedUnfoldTransitionProgressProvider();
- private DisplayController.NavigationMode mNavMode;
+ private NavigationMode mNavMode;
private TaskbarActivityContext mTaskbarActivityContext;
private StatefulActivity mActivity;
@@ -237,8 +239,8 @@
*/
private UnfoldTransitionProgressProvider getUnfoldTransitionProgressProviderForActivity(
StatefulActivity activity) {
- if (activity instanceof BaseQuickstepLauncher) {
- return ((BaseQuickstepLauncher) activity).getUnfoldTransitionProgressProvider();
+ if (activity instanceof QuickstepLauncher) {
+ return ((QuickstepLauncher) activity).getUnfoldTransitionProgressProvider();
}
return null;
}
@@ -247,11 +249,11 @@
* Creates a {@link TaskbarUIController} to use while the given StatefulActivity is active.
*/
private TaskbarUIController createTaskbarUIControllerForActivity(StatefulActivity activity) {
- if (activity instanceof BaseQuickstepLauncher) {
+ if (activity instanceof QuickstepLauncher) {
if (mTaskbarActivityContext.getPackageManager().hasSystemFeature(FEATURE_PC)) {
- return new DesktopTaskbarUIController((BaseQuickstepLauncher) activity);
+ return new DesktopTaskbarUIController((QuickstepLauncher) activity);
}
- return new LauncherTaskbarUIController((BaseQuickstepLauncher) activity);
+ return new LauncherTaskbarUIController((QuickstepLauncher) activity);
}
if (activity instanceof RecentsActivity) {
return new FallbackTaskbarUIController((RecentsActivity) activity);
@@ -277,7 +279,8 @@
* 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.
*/
- private void recreateTaskbar() {
+ @VisibleForTesting
+ public void recreateTaskbar() {
DeviceProfile dp = mUserUnlocked ?
LauncherAppState.getIDP(mContext).getDeviceProfile(mContext) : null;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
index 7b4501a..9b27c9d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
@@ -15,14 +15,20 @@
*/
package com.android.launcher3.taskbar;
+import static com.android.launcher3.util.SplitConfigurationOptions.getLogEventForPosition;
+
+import android.content.ClipDescription;
import android.content.Intent;
import android.content.pm.LauncherApps;
import android.graphics.Point;
+import android.os.Bundle;
+import android.util.Pair;
import android.view.MotionEvent;
import android.view.View;
import androidx.annotation.NonNull;
+import com.android.internal.logging.InstanceId;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.LauncherSettings;
@@ -47,6 +53,7 @@
import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
import com.android.launcher3.views.ActivityContext;
import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.util.LogUtils;
import java.io.PrintWriter;
import java.util.HashMap;
@@ -68,6 +75,7 @@
// Initialized in init.
private TaskbarControllers mControllers;
+ private boolean mHideSplitOptions;
public TaskbarPopupController(TaskbarActivityContext context) {
mContext = context;
@@ -93,6 +101,10 @@
mPopupDataProvider.setDeepShortcutMap(deepShortcutMapCopy);
}
+ public void setHideSplitOptions(boolean hideSplitOptions) {
+ mHideSplitOptions = hideSplitOptions;
+ }
+
private void updateNotificationDots(Predicate<PackageUserKey> updatedDots) {
final PackageUserKey packageUserKey = new PackageUserKey(null, null);
Predicate<ItemInfo> matcher = info -> !packageUserKey.updateFromItemInfo(info)
@@ -179,11 +191,16 @@
// TODO(b/227800345): Add "Split bottom" option when tablet is in portrait mode.
private Stream<SystemShortcut.Factory> getSystemShortcuts() {
// concat a Stream of split options with a Stream of APP_INFO
+ Stream<SystemShortcut.Factory> appInfo = Stream.of(APP_INFO);
+ if (mHideSplitOptions) {
+ return appInfo;
+ }
+
return Stream.concat(
Utilities.getSplitPositionOptions(mContext.getDeviceProfile())
.stream()
.map(this::createSplitShortcutFactory),
- Stream.of(APP_INFO)
+ appInfo
);
}
@@ -263,8 +280,15 @@
@Override
public void onClick(View view) {
- AbstractFloatingView.closeAllOpenViews(mTarget);
+ // Initiate splitscreen from the in-app Taskbar or Taskbar All Apps
+ Pair<InstanceId, com.android.launcher3.logging.InstanceId> instanceIds =
+ LogUtils.getShellShareableInstanceId();
+ mTarget.getStatsLogManager().logger()
+ .withItemInfo(mItemInfo)
+ .withInstanceId(instanceIds.second)
+ .log(getLogEventForPosition(mPosition.stagePosition));
+ AbstractFloatingView.closeAllOpenViews(mTarget);
if (mItemInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
WorkspaceItemInfo workspaceItemInfo = (WorkspaceItemInfo) mItemInfo;
SystemUiProxy.INSTANCE.get(mTarget).startShortcut(
@@ -272,7 +296,8 @@
workspaceItemInfo.getDeepShortcutId(),
mPosition.stagePosition,
null,
- workspaceItemInfo.user);
+ workspaceItemInfo.user,
+ instanceIds.first);
} else {
SystemUiProxy.INSTANCE.get(mTarget).startIntent(
mTarget.getSystemService(LauncherApps.class).getMainActivityLaunchIntent(
@@ -281,7 +306,8 @@
mItemInfo.user),
new Intent(),
mPosition.stagePosition,
- null);
+ null,
+ instanceIds.first);
}
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimView.java
index 1d3757f..cdc6d59 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimView.java
@@ -69,4 +69,13 @@
mRenderer.getPaint().setAlpha((int) (alpha * 255));
invalidate();
}
+
+ /**
+ * Sets the roundness of the round corner above Taskbar.
+ * @param cornerRoundness 0 has no round corner, 1 has complete round corner.
+ */
+ protected void setCornerRoundness(float cornerRoundness) {
+ mRenderer.setCornerRoundness(cornerRoundness);
+ invalidate();
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
index c3b0f57..ce191b7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
@@ -30,7 +30,8 @@
/**
* Handles properties/data collection, and passes the results to {@link TaskbarScrimView} to render.
*/
-public class TaskbarScrimViewController implements TaskbarControllers.LoggableTaskbarController {
+public class TaskbarScrimViewController implements TaskbarControllers.LoggableTaskbarController,
+ TaskbarControllers.BackgroundRendererController {
private static final float SCRIM_ALPHA = 0.6f;
@@ -95,6 +96,11 @@
}
@Override
+ public void setCornerRoundness(float cornerRoundness) {
+ mScrimView.setCornerRoundness(cornerRoundness);
+ }
+
+ @Override
public void dumpLogs(String prefix, PrintWriter pw) {
pw.println(prefix + "TaskbarScrimViewController:");
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java
index f131595..c10b57a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java
@@ -17,24 +17,29 @@
import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.DEEP_SHORTCUTS;
import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.SHORTCUTS_AND_NOTIFICATIONS;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
+import static com.android.launcher3.util.SplitConfigurationOptions.getLogEventForPosition;
import android.content.Intent;
import android.content.pm.LauncherApps;
+import android.util.Pair;
import android.view.KeyEvent;
import android.view.View;
+import com.android.internal.logging.InstanceId;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
import com.android.launcher3.accessibility.BaseAccessibilityDelegate;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.notification.NotificationListener;
import com.android.launcher3.util.ShortcutUtil;
import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.util.LogUtils;
import java.util.List;
@@ -49,10 +54,12 @@
public static final int MOVE_TO_BOTTOM_OR_RIGHT = R.id.action_move_to_bottom_or_right;
private final LauncherApps mLauncherApps;
+ private final StatsLogManager mStatsLogManager;
public TaskbarShortcutMenuAccessibilityDelegate(TaskbarActivityContext context) {
super(context);
mLauncherApps = context.getSystemService(LauncherApps.class);
+ mStatsLogManager = context.getStatsLogManager();
mActions.put(DEEP_SHORTCUTS, new LauncherAction(DEEP_SHORTCUTS,
R.string.action_deep_shortcut, KeyEvent.KEYCODE_S));
@@ -82,7 +89,14 @@
&& (action == MOVE_TO_TOP_OR_LEFT || action == MOVE_TO_BOTTOM_OR_RIGHT)) {
WorkspaceItemInfo info = (WorkspaceItemInfo) item;
int side = action == MOVE_TO_TOP_OR_LEFT
- ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT;
+ ? STAGE_POSITION_TOP_OR_LEFT : STAGE_POSITION_BOTTOM_OR_RIGHT;
+
+ Pair<InstanceId, com.android.launcher3.logging.InstanceId> instanceIds =
+ LogUtils.getShellShareableInstanceId();
+ mStatsLogManager.logger()
+ .withItemInfo(item)
+ .withInstanceId(instanceIds.second)
+ .log(getLogEventForPosition(side));
if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
SystemUiProxy.INSTANCE.get(mContext).startShortcut(
@@ -90,14 +104,15 @@
info.getDeepShortcutId(),
side,
/* bundleOpts= */ null,
- info.user);
+ info.user,
+ instanceIds.first);
} else {
SystemUiProxy.INSTANCE.get(mContext).startIntent(
mLauncherApps.getMainActivityLaunchIntent(
item.getIntent().getComponent(),
/* startActivityOptions= */null,
item.user),
- new Intent(), side, null);
+ new Intent(), side, null, instanceIds.first);
}
return true;
} else if (action == DEEP_SHORTCUTS || action == SHORTCUTS_AND_NOTIFICATIONS) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 3ea9173..06348e2 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -32,25 +32,26 @@
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.util.Log;
+import android.view.InsetsController;
import android.view.View;
import android.view.ViewConfiguration;
import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
import com.android.internal.jank.InteractionJankMonitor;
+import com.android.launcher3.Alarm;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.testing.shared.TestProtocol;
-import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
+import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.SystemUiProxy;
-import com.android.systemui.shared.system.WindowManagerWrapper;
import java.io.PrintWriter;
-import java.util.Arrays;
-import java.util.Optional;
import java.util.StringJoiner;
import java.util.function.IntPredicate;
@@ -69,9 +70,10 @@
public static final int FLAG_STASHED_IN_APP_SETUP = 1 << 4; // setup wizard and AllSetActivity
public static final int FLAG_STASHED_IN_APP_IME = 1 << 5; // IME is visible
public static final int FLAG_IN_STASHED_LAUNCHER_STATE = 1 << 6;
- public static final int FLAG_STASHED_IN_APP_ALL_APPS = 1 << 7; // All apps is visible.
+ public static final int FLAG_STASHED_IN_TASKBAR_ALL_APPS = 1 << 7; // All apps is visible.
public static final int FLAG_IN_SETUP = 1 << 8; // In the Setup Wizard
public static final int FLAG_STASHED_SMALL_SCREEN = 1 << 9; // phone screen gesture nav, stashed
+ public static final int FLAG_STASHED_IN_APP_AUTO = 1 << 10; // Autohide (transient taskbar).
// If any of these flags are enabled, isInApp should return true.
private static final int FLAGS_IN_APP = FLAG_IN_APP | FLAG_IN_SETUP;
@@ -79,8 +81,8 @@
// If we're in an app and any of these flags are enabled, taskbar should be stashed.
private static final int FLAGS_STASHED_IN_APP = FLAG_STASHED_IN_APP_MANUAL
| FLAG_STASHED_IN_APP_PINNED | FLAG_STASHED_IN_APP_EMPTY | FLAG_STASHED_IN_APP_SETUP
- | FLAG_STASHED_IN_APP_IME | FLAG_STASHED_IN_APP_ALL_APPS |
- FLAG_STASHED_SMALL_SCREEN;
+ | FLAG_STASHED_IN_APP_IME | FLAG_STASHED_IN_TASKBAR_ALL_APPS
+ | FLAG_STASHED_SMALL_SCREEN | FLAG_STASHED_IN_APP_AUTO;
private static final int FLAGS_STASHED_IN_APP_IGNORING_IME =
FLAGS_STASHED_IN_APP & ~FLAG_STASHED_IN_APP_IME;
@@ -90,13 +92,13 @@
// Currently any flag that causes us to stash in an app is included, except for IME or All Apps
// since those cover the underlying app anyway and thus the app shouldn't change insets.
private static final int FLAGS_REPORT_STASHED_INSETS_TO_APP = FLAGS_STASHED_IN_APP
- & ~FLAG_STASHED_IN_APP_IME & ~FLAG_STASHED_IN_APP_ALL_APPS;
+ & ~FLAG_STASHED_IN_APP_IME & ~FLAG_STASHED_IN_TASKBAR_ALL_APPS;
/**
* How long to stash/unstash when manually invoked via long press.
*/
public static final long TASKBAR_STASH_DURATION =
- WindowManagerWrapper.ANIMATION_DURATION_RESIZE;
+ InsetsController.ANIMATION_DURATION_RESIZE;
/**
* How long to stash/unstash when keyboard is appearing/disappearing.
@@ -134,6 +136,9 @@
*/
private static final boolean DEFAULT_STASHED_PREF = false;
+ // Auto stashes when user has not interacted with the Taskbar after X ms.
+ private static final long NO_TOUCH_TIMEOUT_TO_STASH_MS = 5000;
+
private final TaskbarActivityContext mActivity;
private final SharedPreferences mPrefs;
private final int mStashedHeight;
@@ -146,11 +151,11 @@
private AnimatedFloat mTaskbarBackgroundOffset;
private AnimatedFloat mTaskbarImeBgAlpha;
// TaskbarView icon properties.
- private AlphaProperty mIconAlphaForStash;
+ private MultiProperty mIconAlphaForStash;
private AnimatedFloat mIconScaleForStash;
private AnimatedFloat mIconTranslationYForStash;
// Stashed handle properties.
- private AlphaProperty mTaskbarStashedHandleAlpha;
+ private MultiProperty mTaskbarStashedHandleAlpha;
private AnimatedFloat mTaskbarStashedHandleHintScale;
/** Whether we are currently visually stashed (might change based on launcher state). */
@@ -162,7 +167,10 @@
private boolean mIsImeShowing;
private boolean mIsImeSwitcherShowing;
- private boolean mEnableManualStashingForTests = false;
+ private boolean mEnableManualStashingDuringTests = false;
+
+ private final Alarm mTimeoutAlarm = new Alarm();
+ private boolean mEnableBlockingTimeoutDuringTests = false;
// Evaluate whether the handle should be stashed
private final StatePropertyHolder mStatePropertyHolder = new StatePropertyHolder(
@@ -170,9 +178,11 @@
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 stashedInTaskbarAllApps =
+ hasAnyFlag(flags, FLAG_STASHED_IN_TASKBAR_ALL_APPS);
boolean stashedForSmallScreen = hasAnyFlag(flags, FLAG_STASHED_SMALL_SCREEN);
return (inApp && stashedInApp) || (!inApp && stashedLauncherState)
- || stashedForSmallScreen;
+ || stashedInTaskbarAllApps || stashedForSmallScreen;
});
public TaskbarStashController(TaskbarActivityContext activity) {
@@ -182,8 +192,13 @@
if (isPhoneMode()) {
// DeviceProfile's taskbar vars aren't initialized w/ the flag off
Resources resources = mActivity.getResources();
- mUnstashedHeight = resources.getDimensionPixelSize(R.dimen.taskbar_size);
- mStashedHeight = resources.getDimensionPixelOffset(R.dimen.taskbar_stashed_size);
+ boolean isTransientTaskbar = DisplayController.isTransientTaskbar(mActivity);
+ mUnstashedHeight = resources.getDimensionPixelSize(isTransientTaskbar
+ ? R.dimen.transient_taskbar_size
+ : R.dimen.taskbar_size);
+ mStashedHeight = resources.getDimensionPixelSize(isTransientTaskbar
+ ? R.dimen.transient_taskbar_stashed_size
+ : R.dimen.taskbar_stashed_size);
} else {
mUnstashedHeight = mActivity.getDeviceProfile().taskbarSize;
mStashedHeight = mActivity.getDeviceProfile().stashedTaskbarSize;
@@ -199,26 +214,35 @@
mTaskbarImeBgAlpha = dragLayerController.getImeBgTaskbar();
TaskbarViewController taskbarViewController = controllers.taskbarViewController;
- mIconAlphaForStash = taskbarViewController.getTaskbarIconAlpha().getProperty(
+ mIconAlphaForStash = taskbarViewController.getTaskbarIconAlpha().get(
TaskbarViewController.ALPHA_INDEX_STASH);
mIconScaleForStash = taskbarViewController.getTaskbarIconScaleForStash();
mIconTranslationYForStash = taskbarViewController.getTaskbarIconTranslationYForStash();
StashedHandleViewController stashedHandleController =
controllers.stashedHandleViewController;
- mTaskbarStashedHandleAlpha = stashedHandleController.getStashedHandleAlpha().getProperty(
+ mTaskbarStashedHandleAlpha = stashedHandleController.getStashedHandleAlpha().get(
StashedHandleViewController.ALPHA_INDEX_STASHED);
mTaskbarStashedHandleHintScale = stashedHandleController.getStashedHandleHintScale();
- boolean isManuallyStashedInApp = supportsManualStashing()
- && mPrefs.getBoolean(SHARED_PREFS_STASHED_KEY, DEFAULT_STASHED_PREF);
+ boolean isTransientTaskbar = DisplayController.isTransientTaskbar(mActivity);
+ // We use supportsVisualStashing() here instead of supportsManualStashing() because we want
+ // it to work properly for tests that recreate taskbar. This check is here just to ensure
+ // that taskbar unstashes when going to 3 button mode (supportsVisualStashing() false).
+ boolean isManuallyStashedInApp = supportsVisualStashing()
+ && mPrefs.getBoolean(SHARED_PREFS_STASHED_KEY, DEFAULT_STASHED_PREF)
+ && !isTransientTaskbar;
boolean isInSetup = !mActivity.isUserSetupComplete() || setupUIVisible;
updateStateForFlag(FLAG_STASHED_IN_APP_MANUAL, isManuallyStashedInApp);
+ updateStateForFlag(FLAG_STASHED_IN_APP_AUTO, isTransientTaskbar);
updateStateForFlag(FLAG_STASHED_IN_APP_SETUP, isInSetup);
updateStateForFlag(FLAG_IN_SETUP, isInSetup);
updateStateForFlag(FLAG_STASHED_SMALL_SCREEN, isPhoneMode()
&& !mActivity.isThreeButtonNav());
- applyState();
+ // For now, assume we're in an app, since LauncherTaskbarUIController won't be able to tell
+ // us that we're paused until a bit later. This avoids flickering upon recreating taskbar.
+ updateStateForFlag(FLAG_IN_APP, true);
+ applyState(/* duration = */ 0);
notifyStashChange(/* visible */ false, /* stashed */ isStashedInApp());
}
@@ -228,8 +252,7 @@
* state.
*/
public boolean supportsVisualStashing() {
- return mControllers.uiController.supportsVisualStashing() ||
- (isPhoneMode() && !mActivity.isThreeButtonNav());
+ return !mActivity.isThreeButtonNav() && mControllers.uiController.supportsVisualStashing();
}
/**
@@ -237,15 +260,27 @@
*/
protected boolean supportsManualStashing() {
return supportsVisualStashing()
- && (!Utilities.IS_RUNNING_IN_TEST_HARNESS || mEnableManualStashingForTests);
+ && isInApp()
+ && (!Utilities.IS_RUNNING_IN_TEST_HARNESS || mEnableManualStashingDuringTests)
+ && !DisplayController.isTransientTaskbar(mActivity);
}
/**
* Enables support for manual stashing. This should only be used to add this functionality
* to Launcher specific tests.
*/
- public void enableManualStashingForTests(boolean enableManualStashing) {
- mEnableManualStashingForTests = enableManualStashing;
+ @VisibleForTesting
+ public void enableManualStashingDuringTests(boolean enableManualStashing) {
+ mEnableManualStashingDuringTests = enableManualStashing;
+ }
+
+ /**
+ * Enables the auto timeout for taskbar stashing. This method should only be used for taskbar
+ * testing.
+ */
+ @VisibleForTesting
+ public void enableBlockingTimeoutDuringTests(boolean enableBlockingTimeout) {
+ mEnableBlockingTimeoutDuringTests = enableBlockingTimeout;
}
/**
@@ -314,12 +349,20 @@
}
/**
+ * Returns the height that taskbar will be touchable.
+ */
+ public int getTouchableHeight() {
+ return mIsStashed ? mStashedHeight : mUnstashedHeight;
+ }
+
+ /**
* Returns the height that taskbar will inset when inside apps.
* @see WindowInsets.Type#navigationBars()
* @see WindowInsets.Type#systemBars()
*/
public int getContentHeightToReportToApps() {
- if (isPhoneMode() && !mActivity.isThreeButtonNav()) {
+ if ((isPhoneMode() && !mActivity.isThreeButtonNav())
+ || DisplayController.isTransientTaskbar(mActivity)) {
return getStashedHeight();
}
@@ -342,6 +385,11 @@
}
return mStashedHeight;
}
+
+ if (!mActivity.isUserSetupComplete()) {
+ // Special insets for SUW.
+ return mActivity.getResources().getDimensionPixelSize(R.dimen.taskbar_suw_insets);
+ }
return mUnstashedHeight;
}
@@ -359,6 +407,20 @@
}
/**
+ * Stash or unstashes the transient taskbar.
+ */
+ public void updateAndAnimateTransientTaskbar(boolean stash) {
+ if (!DisplayController.isTransientTaskbar(mActivity)) {
+ return;
+ }
+
+ if (hasAnyFlag(FLAG_STASHED_IN_APP_AUTO) != stash) {
+ updateStateForFlag(FLAG_STASHED_IN_APP_AUTO, stash);
+ applyState();
+ }
+ }
+
+ /**
* Should be called when long pressing the nav region when taskbar is present.
* @return Whether taskbar was stashed and now is unstashed.
*/
@@ -531,23 +593,23 @@
public void onAnimationStart(Animator animation) {
mIsStashed = isStashed;
onIsStashedChanged(mIsStashed);
+
+ cancelTimeoutIfExists();
}
@Override
public void onAnimationEnd(Animator animation) {
mAnimator = null;
+
+ if (!mIsStashed) {
+ tryStartTaskbarTimeout();
+ }
}
});
}
private void addJankMonitorListener(AnimatorSet animator, boolean expanding) {
- Optional<View> optionalView =
- Arrays.stream(mControllers.taskbarViewController.getIconViews()).findFirst();
- if (optionalView.isEmpty()) {
- Log.wtf(TAG, "No views to start Interaction jank monitor with.", new Exception());
- return;
- }
- View v = optionalView.get();
+ View v = mControllers.taskbarActivityContext.getDragLayer();
int action = expanding ? InteractionJankMonitor.CUJ_TASKBAR_EXPAND :
InteractionJankMonitor.CUJ_TASKBAR_COLLAPSE;
animator.addListener(new AnimatorListenerAdapter() {
@@ -662,7 +724,7 @@
return;
}
- updateStateForFlag(FLAG_STASHED_IN_APP_ALL_APPS, false);
+ updateStateForFlag(FLAG_STASHED_IN_TASKBAR_ALL_APPS, false);
if (applyState) {
applyState(ALL_APPS.getTransitionDuration(
mControllers.taskbarActivityContext, false /* isToState */));
@@ -703,8 +765,20 @@
applyState(skipAnim ? 0 : animDuration, skipAnim ? 0 : startDelay);
}
+ /**
+ * We stash when IME or IME switcher is showing AND NOT
+ * * in small screen AND
+ * * 3 button nav AND
+ * * landscape (or seascape)
+ * We do not stash if taskbar is transient
+ */
private boolean shouldStashForIme() {
- return mIsImeShowing || mIsImeSwitcherShowing;
+ if (DisplayController.isTransientTaskbar(mActivity)) {
+ return false;
+ }
+ return (mIsImeShowing || mIsImeSwitcherShowing) &&
+ !(isPhoneMode() && mActivity.isThreeButtonNav()
+ && mActivity.getDeviceProfile().isLandscape);
}
/**
@@ -759,6 +833,54 @@
mControllers.rotationButtonController.onTaskbarStateChange(visible, stashed);
}
+ /**
+ * Cancels a timeout if any exists.
+ */
+ public void cancelTimeoutIfExists() {
+ if (mTimeoutAlarm.alarmPending()) {
+ mTimeoutAlarm.cancelAlarm();
+ }
+ }
+
+ /**
+ * Updates the status of the taskbar timeout.
+ * @param isAutohideSuspended If true, cancels any existing timeout
+ * If false, attempts to re/start the timeout
+ */
+ public void updateTaskbarTimeout(boolean isAutohideSuspended) {
+ if (!DisplayController.isTransientTaskbar(mActivity)) {
+ return;
+ }
+ if (isAutohideSuspended) {
+ cancelTimeoutIfExists();
+ } else {
+ tryStartTaskbarTimeout();
+ }
+ }
+
+ /**
+ * Attempts to start timer to auto hide the taskbar based on time.
+ */
+ public void tryStartTaskbarTimeout() {
+ if (!DisplayController.isTransientTaskbar(mActivity)
+ || mIsStashed
+ || mEnableBlockingTimeoutDuringTests) {
+ return;
+ }
+
+ cancelTimeoutIfExists();
+
+ mTimeoutAlarm.setOnAlarmListener(this::onTaskbarTimeout);
+ mTimeoutAlarm.setAlarm(NO_TOUCH_TIMEOUT_TO_STASH_MS);
+ }
+
+ private void onTaskbarTimeout(Alarm alarm) {
+ if (mControllers.taskbarAutohideSuspendController.isSuspended()) {
+ return;
+ }
+ updateAndAnimateTransientTaskbar(true);
+ }
+
@Override
public void dumpLogs(String prefix, PrintWriter pw) {
pw.println(prefix + "TaskbarStashController:");
@@ -774,17 +896,18 @@
}
private static String getStateString(int flags) {
- StringJoiner str = new StringJoiner("|");
- appendFlag(str, flags, FLAGS_IN_APP, "FLAG_IN_APP");
- appendFlag(str, flags, FLAG_STASHED_IN_APP_MANUAL, "FLAG_STASHED_IN_APP_MANUAL");
- appendFlag(str, flags, FLAG_STASHED_IN_APP_PINNED, "FLAG_STASHED_IN_APP_PINNED");
- appendFlag(str, flags, FLAG_STASHED_IN_APP_EMPTY, "FLAG_STASHED_IN_APP_EMPTY");
- appendFlag(str, flags, FLAG_STASHED_IN_APP_SETUP, "FLAG_STASHED_IN_APP_SETUP");
- appendFlag(str, flags, FLAG_STASHED_IN_APP_IME, "FLAG_STASHED_IN_APP_IME");
- appendFlag(str, flags, FLAG_IN_STASHED_LAUNCHER_STATE, "FLAG_IN_STASHED_LAUNCHER_STATE");
- appendFlag(str, flags, FLAG_STASHED_IN_APP_ALL_APPS, "FLAG_STASHED_IN_APP_ALL_APPS");
- appendFlag(str, flags, FLAG_IN_SETUP, "FLAG_IN_SETUP");
- return str.toString();
+ StringJoiner sj = new StringJoiner("|");
+ appendFlag(sj, flags, FLAGS_IN_APP, "FLAG_IN_APP");
+ appendFlag(sj, flags, FLAG_STASHED_IN_APP_MANUAL, "FLAG_STASHED_IN_APP_MANUAL");
+ appendFlag(sj, flags, FLAG_STASHED_IN_APP_PINNED, "FLAG_STASHED_IN_APP_PINNED");
+ appendFlag(sj, flags, FLAG_STASHED_IN_APP_EMPTY, "FLAG_STASHED_IN_APP_EMPTY");
+ appendFlag(sj, flags, FLAG_STASHED_IN_APP_SETUP, "FLAG_STASHED_IN_APP_SETUP");
+ appendFlag(sj, flags, FLAG_STASHED_IN_APP_IME, "FLAG_STASHED_IN_APP_IME");
+ appendFlag(sj, flags, FLAG_IN_STASHED_LAUNCHER_STATE, "FLAG_IN_STASHED_LAUNCHER_STATE");
+ appendFlag(sj, flags, FLAG_STASHED_IN_TASKBAR_ALL_APPS, "FLAG_STASHED_IN_TASKBAR_ALL_APPS");
+ appendFlag(sj, flags, FLAG_IN_SETUP, "FLAG_IN_SETUP");
+ appendFlag(sj, flags, FLAG_STASHED_IN_APP_AUTO, "FLAG_STASHED_IN_APP_AUTO");
+ return sj.toString();
}
private class StatePropertyHolder {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java
new file mode 100644
index 0000000..1a7ec13
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.taskbar;
+
+import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
+import static com.android.quickstep.AnimatedFloat.VALUE;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+
+import androidx.annotation.Nullable;
+import androidx.dynamicanimation.animation.SpringForce;
+
+import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.anim.SpringAnimationBuilder;
+import com.android.launcher3.util.DisplayController;
+import com.android.quickstep.AnimatedFloat;
+
+import java.io.PrintWriter;
+
+/**
+ * Class responsible for translating the transient taskbar UI during a swipe gesture.
+ *
+ * The translation is controlled, in priority order:
+ * - animation to home
+ * - a spring animation
+ * - controlled by user
+ *
+ * The spring animation will play start once the user lets go or when user pauses to go to overview.
+ * When the user goes home, the stash animation will play.
+ */
+public class TaskbarTranslationController implements TaskbarControllers.LoggableTaskbarController {
+
+ private final TaskbarActivityContext mContext;
+ private TaskbarControllers mControllers;
+ private final AnimatedFloat mTranslationYForSwipe = new AnimatedFloat(
+ this::updateTranslationYForSwipe);
+
+ private boolean mHasSprungOnceThisGesture;
+ private @Nullable ValueAnimator mSpringBounce;
+ private boolean mGestureEnded;
+ private boolean mAnimationToHomeRunning;
+
+ private final boolean mIsTransientTaskbar;
+
+ private final TransitionCallback mCallback;
+
+ public TaskbarTranslationController(TaskbarActivityContext context) {
+ mContext = context;
+ mIsTransientTaskbar = DisplayController.isTransientTaskbar(mContext);
+ mCallback = new TransitionCallback();
+ }
+
+ /**
+ * Initialization method.
+ */
+ public void init(TaskbarControllers controllers) {
+ mControllers = controllers;
+ }
+
+ /**
+ * Called to cancel any existing animations.
+ */
+ public void cancelAnimationIfExists() {
+ if (mSpringBounce != null) {
+ mSpringBounce.cancel();
+ mSpringBounce = null;
+ }
+ reset();
+ }
+
+ private void updateTranslationYForSwipe() {
+ if (!mIsTransientTaskbar) {
+ return;
+ }
+
+ float transY = mTranslationYForSwipe.value;
+ mControllers.stashedHandleViewController.setTranslationYForSwipe(transY);
+ mControllers.taskbarViewController.setTranslationYForSwipe(transY);
+ mControllers.taskbarDragLayerController.setTranslationYForSwipe(transY);
+ }
+
+ /**
+ * Starts a spring aniamtion to set the views back to the resting state.
+ */
+ public void startSpring() {
+ if (mHasSprungOnceThisGesture || mAnimationToHomeRunning) {
+ return;
+ }
+ mSpringBounce = new SpringAnimationBuilder(mContext)
+ .setStartValue(mTranslationYForSwipe.value)
+ .setEndValue(0)
+ .setDampingRatio(SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY)
+ .setStiffness(SpringForce.STIFFNESS_LOW)
+ .build(mTranslationYForSwipe, VALUE);
+ mSpringBounce.addListener(forEndCallback(() -> {
+ if (mGestureEnded) {
+ reset();
+ }
+ }));
+ mSpringBounce.start();
+ mHasSprungOnceThisGesture = true;
+ }
+
+ private void reset() {
+ mGestureEnded = false;
+ mHasSprungOnceThisGesture = false;
+ }
+
+ /**
+ * Returns a callback to help monitor the swipe gesture.
+ */
+ public TransitionCallback getTransitionCallback() {
+ return mCallback;
+ }
+
+ /**
+ * Returns an animation to reset the taskbar translation for animation back to launcher.
+ */
+ public ObjectAnimator createAnimToLauncher(long duration) {
+ ObjectAnimator animator = ObjectAnimator.ofFloat(mTranslationYForSwipe, VALUE, 0);
+ animator.setInterpolator(Interpolators.LINEAR);
+ animator.setDuration(duration);
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ cancelAnimationIfExists();
+ mAnimationToHomeRunning = true;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mAnimationToHomeRunning = false;
+ reset();
+ }
+ });
+ return animator;
+ }
+
+ /**
+ * Helper class to communicate to/from the input consumer.
+ */
+ public class TransitionCallback {
+
+ /**
+ * Called when there is movement to move the taskbar.
+ */
+ public void onActionMove(float dY) {
+ if (mAnimationToHomeRunning
+ || (mHasSprungOnceThisGesture && !mGestureEnded)) {
+ return;
+ }
+
+ mTranslationYForSwipe.updateValue(dY);
+ }
+
+ /**
+ * Called when swipe gesture has ended.
+ */
+ public void onActionEnd() {
+ if (mHasSprungOnceThisGesture) {
+ reset();
+ } else {
+ mGestureEnded = true;
+ startSpring();
+ }
+ }
+ }
+
+ @Override
+ public void dumpLogs(String prefix, PrintWriter pw) {
+ pw.println(prefix + "TaskbarTranslationController:");
+
+ pw.println(prefix + "\tmTranslationYForSwipe=" + mTranslationYForSwipe.value);
+ pw.println(prefix + "\tmHasSprungOnceThisGesture=" + mHasSprungOnceThisGesture);
+ pw.println(prefix + "\tmAnimationToHomeRunning=" + mAnimationToHomeRunning);
+ pw.println(prefix + "\tmGestureEnded=" + mGestureEnded);
+ pw.println(prefix + "\tmSpringBounce is running=" + (mSpringBounce != null
+ && mSpringBounce.isRunning()));
+ }
+}
+
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index fcc34c6..4ec9b41 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -15,12 +15,18 @@
*/
package com.android.launcher3.taskbar;
+import android.content.Intent;
+import android.graphics.drawable.BitmapDrawable;
+import android.view.MotionEvent;
import android.view.View;
import androidx.annotation.CallSuper;
+import androidx.annotation.Nullable;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
+import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.TaskView;
import java.io.PrintWriter;
import java.util.stream.Stream;
@@ -49,9 +55,13 @@
return true;
}
+ /**
+ * This should only be called by TaskbarStashController so that a TaskbarUIController can
+ * disable stashing. All other controllers should use
+ * {@link TaskbarStashController#supportsVisualStashing()} as the source of truth.
+ */
public boolean supportsVisualStashing() {
- if (mControllers == null) return false;
- return !mControllers.taskbarActivityContext.isThreeButtonNav();
+ return true;
}
protected void onStashedInAppChanged() { }
@@ -76,10 +86,10 @@
}
/**
- * Manually closes the all apps window.
+ * Manually closes the overlay window.
*/
- public void hideAllApps() {
- mControllers.taskbarAllAppsController.hide();
+ public void hideOverlayWindow() {
+ mControllers.taskbarOverlayController.hideWindow();
}
/**
@@ -93,6 +103,36 @@
}
}
+ /**
+ * Returns true iff taskbar is stashed.
+ */
+ public boolean isTaskbarStashed() {
+ return mControllers.taskbarStashController.isStashed();
+ }
+
+ /**
+ * Called at the end of the swipe gesture on Transient taskbar.
+ */
+ public void startTranslationSpring() {
+ mControllers.taskbarActivityContext.startTranslationSpring();
+ }
+
+ /*
+ * @param ev MotionEvent in screen coordinates.
+ * @return Whether any Taskbar item could handle the given MotionEvent if given the chance.
+ */
+ public boolean isEventOverAnyTaskbarItem(MotionEvent ev) {
+ return mControllers.taskbarViewController.isEventOverAnyItem(ev)
+ || mControllers.navbarButtonsViewController.isEventOverAnyItem(ev);
+ }
+
+ /**
+ * Returns true if icons should be aligned to hotseat in the current transition.
+ */
+ public boolean isIconAlignedWithHotseat() {
+ return false;
+ }
+
@CallSuper
protected void dumpLogs(String prefix, PrintWriter pw) {
pw.println(String.format(
@@ -100,4 +140,38 @@
prefix,
getClass().getSimpleName()));
}
+
+ /**
+ * Returns RecentsView. Overwritten in LauncherTaskbarUIController and
+ * FallbackTaskbarUIController with Launcher-specific implementations. Returns null for other
+ * UI controllers (like DesktopTaskbarUIController) that don't have a RecentsView.
+ */
+ public @Nullable RecentsView getRecentsView() {
+ return null;
+ }
+
+ /**
+ * Uses the clicked Taskbar icon to launch a second app for splitscreen.
+ */
+ public void triggerSecondAppForSplit(ItemInfoWithIcon info, Intent intent, View startingView) {
+ RecentsView recents = getRecentsView();
+ TaskView foundTaskView = recents.getTaskViewByComponentName(info.getTargetComponent());
+ if (foundTaskView != null) {
+ recents.confirmSplitSelect(
+ foundTaskView,
+ foundTaskView.getTask(),
+ foundTaskView.getIconView().getDrawable(),
+ foundTaskView.getThumbnail(),
+ foundTaskView.getThumbnail().getThumbnail(),
+ /* intent */ null);
+ } else {
+ recents.confirmSplitSelect(
+ /* containerTaskView */ null,
+ /* task */ null,
+ new BitmapDrawable(info.bitmap.icon),
+ startingView,
+ /* thumbnail */ null,
+ intent);
+ }
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUnfoldAnimationController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUnfoldAnimationController.java
index 64a4fa7..4c937a7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUnfoldAnimationController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUnfoldAnimationController.java
@@ -15,13 +15,13 @@
*/
package com.android.launcher3.taskbar;
-import android.view.IWindowManager;
import android.view.View;
import android.view.WindowManager;
import com.android.quickstep.util.LauncherViewsMoveFromCenterTranslationApplier;
import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator;
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener;
+import com.android.systemui.unfold.updates.RotationChangeProvider;
import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider;
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider;
@@ -41,16 +41,20 @@
public TaskbarUnfoldAnimationController(BaseTaskbarContext context,
ScopedUnfoldTransitionProgressProvider source,
- WindowManager windowManager, IWindowManager iWindowManager) {
+ WindowManager windowManager,
+ RotationChangeProvider rotationChangeProvider) {
mScopedUnfoldTransitionProgressProvider = source;
mNaturalUnfoldTransitionProgressProvider =
- new NaturalRotationUnfoldProgressProvider(context, iWindowManager, source);
+ new NaturalRotationUnfoldProgressProvider(context,
+ rotationChangeProvider,
+ source);
mMoveFromCenterAnimator = new UnfoldMoveFromCenterAnimator(windowManager,
new LauncherViewsMoveFromCenterTranslationApplier());
}
/**
* Initializes the controller
+ *
* @param taskbarControllers references to all other taskbar controllers
*/
public void init(TaskbarControllers taskbarControllers) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index dbf9759..fe38bb1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -16,6 +16,7 @@
package com.android.launcher3.taskbar;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Rect;
@@ -23,7 +24,6 @@
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
-import android.view.ViewGroup;
import android.widget.FrameLayout;
import androidx.annotation.LayoutRes;
@@ -42,9 +42,9 @@
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.LauncherBindableItemsContainer;
import com.android.launcher3.views.ActivityContext;
-import com.android.launcher3.views.AllAppsButton;
import com.android.launcher3.views.DoubleShadowBubbleTextView;
import java.util.function.Predicate;
@@ -53,12 +53,13 @@
* Hosts the Taskbar content such as Hotseat and Recent Apps. Drawn on top of other apps.
*/
public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconParent, Insettable {
+ private static final String TAG = TaskbarView.class.getSimpleName();
private static final float TASKBAR_BACKGROUND_LUMINANCE = 0.30f;
public int mThemeIconsBackground;
private final int[] mTempOutLocation = new int[2];
- private final Rect mIconLayoutBounds = new Rect();
+ private final Rect mIconLayoutBounds;
private final int mIconTouchSize;
private final int mItemMarginLeftRight;
private final int mItemPadding;
@@ -77,7 +78,7 @@
private @Nullable FolderIcon mLeaveBehindFolderIcon;
// Only non-null when device supports having an All Apps button.
- private @Nullable AllAppsButton mAllAppsButton;
+ private @Nullable View mAllAppsButton;
private View mQsb;
@@ -98,11 +99,14 @@
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
mActivityContext = ActivityContext.lookupContext(context);
+ mIconLayoutBounds = mActivityContext.getTransientTaskbarBounds();
Resources resources = getResources();
mIconTouchSize = resources.getDimensionPixelSize(R.dimen.taskbar_icon_touch_size);
- int actualMargin = resources.getDimensionPixelSize(R.dimen.taskbar_icon_spacing);
+ int actualMargin = DisplayController.isTransientTaskbar(mActivityContext)
+ ? resources.getDimensionPixelSize(R.dimen.transient_taskbar_icon_spacing)
+ : resources.getDimensionPixelSize(R.dimen.taskbar_icon_spacing);
int actualIconSize = mActivityContext.getDeviceProfile().iconSizePx;
// We layout the icons to be of mIconTouchSize in width and height
@@ -115,14 +119,17 @@
mThemeIconsBackground = calculateThemeIconsBackground();
if (FeatureFlags.ENABLE_ALL_APPS_IN_TASKBAR.get()) {
- mAllAppsButton = new AllAppsButton(context);
- mAllAppsButton.setLayoutParams(
- new ViewGroup.LayoutParams(mIconTouchSize, mIconTouchSize));
+ mAllAppsButton = LayoutInflater.from(context)
+ .inflate(R.layout.taskbar_all_apps_button, this, false);
mAllAppsButton.setPadding(mItemPadding, mItemPadding, mItemPadding, mItemPadding);
+ if (mActivityContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_PC)) {
+ mAllAppsButton.setVisibility(GONE);
+ }
}
// TODO: Disable touch events on QSB otherwise it can crash.
mQsb = LayoutInflater.from(context).inflate(R.layout.search_container_hotseat, this, false);
+
}
private int getColorWithGivenLuminance(int color, float luminance) {
@@ -174,6 +181,7 @@
}
removeView(mQsb);
+
for (int i = 0; i < hotseatItemInfos.length; i++) {
ItemInfo hotseatItemInfo = hotseatItemInfos[i];
if (hotseatItemInfo == null) {
@@ -285,12 +293,8 @@
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
int count = getChildCount();
- int countExcludingQsb = count;
DeviceProfile deviceProfile = mActivityContext.getDeviceProfile();
- if (deviceProfile.isQsbInline) {
- countExcludingQsb--;
- }
- int spaceNeeded = countExcludingQsb * (mItemMarginLeftRight * 2 + mIconTouchSize);
+ int spaceNeeded = getIconLayoutWidth();
int navSpaceNeeded = deviceProfile.hotseatBarEndOffset;
boolean layoutRtl = isLayoutRtl();
int iconEnd = right - (right - left - spaceNeeded) / 2;
@@ -341,12 +345,22 @@
}
@Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ mControllerCallbacks.onInterceptTouchEvent(ev);
+ return super.onInterceptTouchEvent(ev);
+ }
+
+ @Override
public boolean onTouchEvent(MotionEvent event) {
if (!mTouchEnabled) {
return true;
}
- if (mIconLayoutBounds.left <= event.getX() && event.getX() <= mIconLayoutBounds.right) {
- // Don't allow long pressing between icons, or above/below them.
+ if (mIconLayoutBounds.left <= event.getX()
+ && event.getX() <= mIconLayoutBounds.right
+ && !DisplayController.isTransientTaskbar(mActivityContext)) {
+ // Don't allow long pressing between icons, or above/below them
+ // unless its transient taskbar.
+ mControllerCallbacks.clearTouchInProgress();
return true;
}
if (mControllerCallbacks.onTouchEvent(event)) {
@@ -363,6 +377,7 @@
public void setTouchesEnabled(boolean touchEnabled) {
this.mTouchEnabled = touchEnabled;
+ mControllerCallbacks.clearTouchInProgress();
}
/**
@@ -381,6 +396,18 @@
}
/**
+ * Returns the space used by the icons
+ */
+ public int getIconLayoutWidth() {
+ int countExcludingQsb = getChildCount();
+ DeviceProfile deviceProfile = mActivityContext.getDeviceProfile();
+ if (deviceProfile.isQsbInline) {
+ countExcludingQsb--;
+ }
+ return countExcludingQsb * (mItemMarginLeftRight * 2 + mIconTouchSize);
+ }
+
+ /**
* Returns the app icons currently shown in the taskbar.
*/
public View[] getIconViews() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index 992aa4b..80a31b4 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -17,10 +17,13 @@
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
+import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
import static com.android.launcher3.Utilities.squaredHypot;
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.touch.SingleAxisSwipeDetector.DIRECTION_NEGATIVE;
+import static com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL;
import static com.android.quickstep.AnimatedFloat.VALUE;
import android.annotation.NonNull;
@@ -30,6 +33,7 @@
import android.view.MotionEvent;
import android.view.View;
+import androidx.annotation.Nullable;
import androidx.core.graphics.ColorUtils;
import androidx.core.view.OneShotPreDrawListener;
@@ -46,9 +50,12 @@
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.icons.ThemedIconDrawable;
import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.touch.SingleAxisSwipeDetector;
+import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.HorizontalInsettableView;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.LauncherBindableItemsContainer;
+import com.android.launcher3.util.MultiPropertyFactory;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.quickstep.AnimatedFloat;
@@ -84,12 +91,18 @@
this::updateTranslationY);
private AnimatedFloat mTaskbarNavButtonTranslationY;
private AnimatedFloat mTaskbarNavButtonTranslationYForInAppDisplay;
+ private float mTaskbarIconTranslationYForSwipe;
+
+ private final int mTaskbarBottomMargin;
private final AnimatedFloat mThemeIconsBackground = new AnimatedFloat(
this::updateIconsBackground);
private final TaskbarModelCallbacks mModelCallbacks;
+ // Captures swipe down action to close transient taskbar.
+ protected @Nullable SingleAxisSwipeDetector mSwipeDownDetector;
+
// Initialized in init.
private TaskbarControllers mControllers;
@@ -100,12 +113,43 @@
private int mThemeIconsColor;
+ private final DeviceProfile.OnDeviceProfileChangeListener mDeviceProfileChangeListener =
+ dp -> commitRunningAppsToUI();
+
public TaskbarViewController(TaskbarActivityContext activity, TaskbarView taskbarView) {
mActivity = activity;
mTaskbarView = taskbarView;
mTaskbarIconAlpha = new MultiValueAlpha(mTaskbarView, NUM_ALPHA_CHANNELS);
mTaskbarIconAlpha.setUpdateVisibility(true);
mModelCallbacks = new TaskbarModelCallbacks(activity, mTaskbarView);
+ mTaskbarBottomMargin = DisplayController.isTransientTaskbar(activity)
+ ? activity.getResources().getDimensionPixelSize(R.dimen.transient_taskbar_margin)
+ : 0;
+
+ if (DisplayController.isTransientTaskbar(mActivity)) {
+ mSwipeDownDetector = new SingleAxisSwipeDetector(activity,
+ new SingleAxisSwipeDetector.Listener() {
+ private float mLastDisplacement;
+
+ @Override
+ public boolean onDrag(float displacement) {
+ mLastDisplacement = displacement;
+ return false;
+ }
+
+ @Override
+ public void onDragEnd(float velocity) {
+ if (mLastDisplacement > 0) {
+ mControllers.taskbarStashController
+ .updateAndAnimateTransientTaskbar(true);
+ }
+ }
+
+ @Override
+ public void onDragStart(boolean start, float startDisplacement) {}
+ }, VERTICAL);
+ mSwipeDownDetector.setDetectableScrollConditions(DIRECTION_NEGATIVE, false);
+ }
}
public void init(TaskbarControllers controllers) {
@@ -127,10 +171,13 @@
controllers.navbarButtonsViewController.getTaskbarNavButtonTranslationY();
mTaskbarNavButtonTranslationYForInAppDisplay = controllers.navbarButtonsViewController
.getTaskbarNavButtonTranslationYForInAppDisplay();
+
+ mActivity.addOnDeviceProfileChangeListener(mDeviceProfileChangeListener);
}
public void onDestroy() {
LauncherAppState.getInstance(mActivity).getModel().removeCallbacks(mModelCallbacks);
+ mActivity.removeOnDeviceProfileChangeListener(mDeviceProfileChangeListener);
mModelCallbacks.unregisterListeners();
}
@@ -138,7 +185,7 @@
return mTaskbarView.areIconsVisible();
}
- public MultiValueAlpha getTaskbarIconAlpha() {
+ public MultiPropertyFactory<View> getTaskbarIconAlpha() {
return mTaskbarIconAlpha;
}
@@ -153,7 +200,7 @@
* Should be called when the IME switcher visibility changes.
*/
public void setIsImeSwitcherVisible(boolean isImeSwitcherVisible) {
- mTaskbarIconAlpha.getProperty(ALPHA_INDEX_IME_BUTTON_NAV).setValue(
+ mTaskbarIconAlpha.get(ALPHA_INDEX_IME_BUTTON_NAV).setValue(
isImeSwitcherVisible ? 0 : 1);
}
@@ -162,7 +209,7 @@
*/
public void setRecentsButtonDisabled(boolean isDisabled) {
// TODO: check TaskbarStashController#supportsStashing(), to stash instead of setting alpha.
- mTaskbarIconAlpha.getProperty(ALPHA_INDEX_RECENTS_DISABLED).setValue(isDisabled ? 0 : 1);
+ mTaskbarIconAlpha.get(ALPHA_INDEX_RECENTS_DISABLED).setValue(isDisabled ? 0 : 1);
}
/**
@@ -185,6 +232,10 @@
return mTaskbarView.getIconLayoutBounds();
}
+ public int getIconLayoutWidth() {
+ return mTaskbarView.getIconLayoutWidth();
+ }
+
public View[] getIconViews() {
return mTaskbarView.getIconViews();
}
@@ -210,9 +261,18 @@
mTaskbarView.setScaleY(scale);
}
+ /**
+ * Sets the translation of the TaskbarView during the swipe up gesture.
+ */
+ public void setTranslationYForSwipe(float transY) {
+ mTaskbarIconTranslationYForSwipe = transY;
+ updateTranslationY();
+ }
+
private void updateTranslationY() {
mTaskbarView.setTranslationY(mTaskbarIconTranslationYForHome.value
- + mTaskbarIconTranslationYForStash.value);
+ + mTaskbarIconTranslationYForStash.value
+ + mTaskbarIconTranslationYForSwipe);
}
private void updateIconsBackground() {
@@ -230,10 +290,9 @@
* 0 => not aligned
* 1 => fully aligned
*/
- public void setLauncherIconAlignment(float alignmentRatio, Float endAlignment,
- DeviceProfile launcherDp) {
+ public void setLauncherIconAlignment(float alignmentRatio, DeviceProfile launcherDp) {
if (mIconAlignControllerLazy == null) {
- mIconAlignControllerLazy = createIconAlignmentController(launcherDp, endAlignment);
+ mIconAlignControllerLazy = createIconAlignmentController(launcherDp);
}
mIconAlignControllerLazy.setPlayFraction(alignmentRatio);
if (alignmentRatio <= 0 || alignmentRatio >= 1) {
@@ -245,8 +304,7 @@
/**
* Creates an animation for aligning the taskbar icons with the provided Launcher device profile
*/
- private AnimatorPlaybackController createIconAlignmentController(DeviceProfile launcherDp,
- Float endAlignment) {
+ private AnimatorPlaybackController createIconAlignmentController(DeviceProfile launcherDp) {
mOnControllerPreCreateCallback.run();
PendingAnimation setter = new PendingAnimation(100);
DeviceProfile taskbarDp = mActivity.getDeviceProfile();
@@ -272,7 +330,7 @@
setter.addOnFrameListener(anim -> mActivity.setTaskbarWindowHeight(
anim.getAnimatedFraction() > 0 ? expandedHeight : collapsedHeight));
- boolean isToHome = endAlignment != null && endAlignment == 1;
+ boolean isToHome = mControllers.uiController.isIconAlignedWithHotseat();
for (int i = 0; i < mTaskbarView.getChildCount(); i++) {
View child = mTaskbarView.getChildAt(i);
int positionInHotseat;
@@ -308,6 +366,8 @@
float scale = ((float) taskbarDp.iconSizePx) / launcherDp.hotseatQsbVisualHeight;
setter.addFloat(child, SCALE_PROPERTY, scale, 1f, LINEAR);
+ setter.setFloat(child, VIEW_TRANSLATE_Y, mTaskbarBottomMargin, LINEAR);
+
setter.addFloat(child, VIEW_ALPHA, 0f, 1f,
isToHome
? Interpolators.clampToProgress(LINEAR, 0f, 0.35f)
@@ -333,6 +393,8 @@
float childCenter = (child.getLeft() + child.getRight()) / 2f;
setter.setFloat(child, ICON_TRANSLATE_X, hotseatIconCenter - childCenter, LINEAR);
+ setter.setFloat(child, VIEW_TRANSLATE_Y, mTaskbarBottomMargin, LINEAR);
+
setter.setFloat(child, SCALE_PROPERTY, scaleUp, LINEAR);
}
@@ -416,6 +478,8 @@
private float mDownX, mDownY;
private boolean mCanceledStashHint;
+ private boolean mTouchInProgress;
+
public View.OnClickListener getIconOnClickListener() {
return mActivity.getItemOnClickListener();
}
@@ -437,37 +501,75 @@
}
/**
+ * Simply listens to all intercept touch events passed to TaskbarView.
+ */
+ public void onInterceptTouchEvent(MotionEvent ev) {
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ mTouchInProgress = true;
+ }
+
+ if (mTouchInProgress && mSwipeDownDetector != null) {
+ mSwipeDownDetector.onTouchEvent(ev);
+ }
+
+ if (ev.getAction() == MotionEvent.ACTION_UP
+ || ev.getAction() == MotionEvent.ACTION_CANCEL) {
+ clearTouchInProgress();
+ }
+ }
+
+ /**
* Get the first chance to handle TaskbarView#onTouchEvent, and return whether we want to
* consume the touch so TaskbarView treats it as an ACTION_CANCEL.
*/
public boolean onTouchEvent(MotionEvent motionEvent) {
+ boolean shouldConsumeTouch = false;
+ boolean clearTouchInProgress = false;
+
final float x = motionEvent.getRawX();
final float y = motionEvent.getRawY();
switch (motionEvent.getAction()) {
case MotionEvent.ACTION_DOWN:
+ mTouchInProgress = true;
mDownX = x;
mDownY = y;
mControllers.taskbarStashController.startStashHint(/* animateForward = */ true);
mCanceledStashHint = false;
break;
case MotionEvent.ACTION_MOVE:
- if (!mCanceledStashHint
+ if (mTouchInProgress
+ && !mCanceledStashHint
&& squaredHypot(mDownX - x, mDownY - y) > mSquaredTouchSlop) {
mControllers.taskbarStashController.startStashHint(
/* animateForward= */ false);
mCanceledStashHint = true;
- return true;
+ shouldConsumeTouch = true;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
- if (!mCanceledStashHint) {
+ if (mTouchInProgress && !mCanceledStashHint) {
mControllers.taskbarStashController.startStashHint(
/* animateForward= */ false);
}
+ clearTouchInProgress = true;
break;
}
- return false;
+
+ if (mTouchInProgress && mSwipeDownDetector != null) {
+ mSwipeDownDetector.onTouchEvent(motionEvent);
+ }
+ if (clearTouchInProgress) {
+ clearTouchInProgress();
+ }
+ return shouldConsumeTouch;
+ }
+
+ /**
+ * Ensures that we do not pass any more touch events to the SwipeDetector.
+ */
+ public void clearTouchInProgress() {
+ mTouchInProgress = false;
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt b/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt
index 81acda3..a033507 100644
--- a/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt
@@ -14,7 +14,8 @@
* Controls Taskbar behavior while Voice Interaction Window (assistant) is showing.
*/
class VoiceInteractionWindowController(val context: TaskbarActivityContext)
- : TaskbarControllers.LoggableTaskbarController {
+ : TaskbarControllers.LoggableTaskbarController,
+ TaskbarControllers.BackgroundRendererController {
private val taskbarBackgroundRenderer = TaskbarBackgroundRenderer(context)
@@ -63,11 +64,11 @@
// Fade out taskbar icons and stashed handle.
val taskbarIconAlpha = if (isVoiceInteractionWindowVisible) 0f else 1f
val fadeTaskbarIcons = controllers.taskbarViewController.taskbarIconAlpha
- .getProperty(TaskbarViewController.ALPHA_INDEX_ASSISTANT_INVOKED)
+ .get(TaskbarViewController.ALPHA_INDEX_ASSISTANT_INVOKED)
.animateToValue(taskbarIconAlpha)
.setDuration(TASKBAR_ICONS_FADE_DURATION)
val fadeStashedHandle = controllers.stashedHandleViewController.stashedHandleAlpha
- .getProperty(StashedHandleViewController.ALPHA_INDEX_ASSISTANT_INVOKED)
+ .get(StashedHandleViewController.ALPHA_INDEX_ASSISTANT_INVOKED)
.animateToValue(taskbarIconAlpha)
.setDuration(STASHED_HANDLE_FADE_DURATION)
fadeTaskbarIcons.start()
@@ -77,7 +78,7 @@
fadeStashedHandle.end()
}
- moveTaskbarBackgroundToAppropriateLayer()
+ moveTaskbarBackgroundToAppropriateLayer(skipAnim)
}
/**
@@ -86,25 +87,34 @@
* OR
* Removes the temporary window and show the TaskbarDragLayer background again.
*/
- private fun moveTaskbarBackgroundToAppropriateLayer() {
+ private fun moveTaskbarBackgroundToAppropriateLayer(skipAnim: Boolean) {
val taskbarBackgroundOverride = controllers.taskbarDragLayerController
.overrideBackgroundAlpha
val moveToLowerLayer = isVoiceInteractionWindowVisible
- if (moveToLowerLayer) {
+ val onWindowsSynchronized = if (moveToLowerLayer) {
// First add the temporary window, then hide the overlapping taskbar background.
- context.addWindowView(separateWindowForTaskbarBackground, separateWindowLayoutParams)
- ViewRootSync.synchronizeNextDraw(separateWindowForTaskbarBackground, context.dragLayer
- ) {
- taskbarBackgroundOverride.updateValue(0f)
- }
+ context.addWindowView(separateWindowForTaskbarBackground, separateWindowLayoutParams);
+ { taskbarBackgroundOverride.updateValue(0f) }
} else {
// First reapply the original taskbar background, then remove the temporary window.
- taskbarBackgroundOverride.updateValue(1f)
- ViewRootSync.synchronizeNextDraw(separateWindowForTaskbarBackground, context.dragLayer
- ) {
- context.removeWindowView(separateWindowForTaskbarBackground)
- }
+ taskbarBackgroundOverride.updateValue(1f);
+ { context.removeWindowView(separateWindowForTaskbarBackground) }
}
+
+ if (skipAnim) {
+ onWindowsSynchronized()
+ } else {
+ ViewRootSync.synchronizeNextDraw(
+ separateWindowForTaskbarBackground,
+ context.dragLayer,
+ onWindowsSynchronized
+ )
+ }
+ }
+
+ override fun setCornerRoundness(cornerRoundness: Float) {
+ taskbarBackgroundRenderer.setCornerRoundness(cornerRoundness)
+ separateWindowForTaskbarBackground.invalidate()
}
override fun dumpLogs(prefix: String, pw: PrintWriter) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java
index 51fa4d9..e41c75f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java
@@ -20,14 +20,11 @@
import android.view.WindowInsets;
import com.android.launcher3.allapps.ActivityAllAppsContainerView;
-import com.android.launcher3.allapps.AllAppsGridAdapter;
-import com.android.launcher3.allapps.AlphabeticalAppsList;
-import com.android.launcher3.allapps.BaseAdapterProvider;
-import com.android.launcher3.allapps.BaseAllAppsAdapter;
+import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
/** All apps container accessible from taskbar. */
public class TaskbarAllAppsContainerView extends
- ActivityAllAppsContainerView<TaskbarAllAppsContext> {
+ ActivityAllAppsContainerView<TaskbarOverlayContext> {
public TaskbarAllAppsContainerView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
@@ -42,12 +39,4 @@
setInsets(insets.getInsets(WindowInsets.Type.systemBars()).toRect());
return super.onApplyWindowInsets(insets);
}
-
- @Override
- protected BaseAllAppsAdapter<TaskbarAllAppsContext> createAdapter(
- AlphabeticalAppsList<TaskbarAllAppsContext> appsList,
- BaseAdapterProvider[] adapterProviders) {
- return new AllAppsGridAdapter<>(mActivityContext, getLayoutInflater(), appsList,
- adapterProviders);
- }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContext.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContext.java
deleted file mode 100644
index c76180e..0000000
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContext.java
+++ /dev/null
@@ -1,249 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.taskbar.allapps;
-
-import static android.view.KeyEvent.ACTION_UP;
-import static android.view.KeyEvent.KEYCODE_BACK;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
-
-import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo.TOUCHABLE_INSETS_REGION;
-
-import android.content.Context;
-import android.graphics.Insets;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.WindowInsets;
-
-import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.allapps.ActivityAllAppsContainerView;
-import com.android.launcher3.allapps.search.DefaultSearchAdapterProvider;
-import com.android.launcher3.allapps.search.SearchAdapterProvider;
-import com.android.launcher3.dot.DotInfo;
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.popup.PopupDataProvider;
-import com.android.launcher3.taskbar.BaseTaskbarContext;
-import com.android.launcher3.taskbar.TaskbarActivityContext;
-import com.android.launcher3.taskbar.TaskbarDragController;
-import com.android.launcher3.taskbar.TaskbarStashController;
-import com.android.launcher3.testing.TestLogging;
-import com.android.launcher3.testing.shared.TestProtocol;
-import com.android.launcher3.util.OnboardingPrefs;
-import com.android.launcher3.util.TouchController;
-import com.android.launcher3.views.BaseDragLayer;
-import com.android.systemui.shared.system.ViewTreeObserverWrapper;
-import com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo;
-import com.android.systemui.shared.system.ViewTreeObserverWrapper.OnComputeInsetsListener;
-
-/**
- * Window context for the taskbar all apps overlay.
- * <p>
- * All apps has its own window and needs a window context. Some properties are delegated to the
- * {@link TaskbarActivityContext} such as {@link DeviceProfile} and {@link PopupDataProvider}.
- */
-class TaskbarAllAppsContext extends BaseTaskbarContext {
- private final TaskbarActivityContext mTaskbarContext;
- private final OnboardingPrefs<TaskbarAllAppsContext> mOnboardingPrefs;
-
- private final TaskbarAllAppsController mWindowController;
- private final TaskbarAllAppsViewController mAllAppsViewController;
- private final TaskbarDragController mDragController;
- private final TaskbarAllAppsDragLayer mDragLayer;
- private final TaskbarAllAppsContainerView mAppsView;
-
- // We automatically stash taskbar when all apps is opened in gesture navigation mode.
- private final boolean mWillTaskbarBeVisuallyStashed;
- private final int mStashedTaskbarHeight;
-
- TaskbarAllAppsContext(
- TaskbarActivityContext taskbarContext,
- TaskbarAllAppsController windowController,
- TaskbarStashController taskbarStashController) {
- super(taskbarContext.createWindowContext(TYPE_APPLICATION_OVERLAY, null));
- mTaskbarContext = taskbarContext;
- mWindowController = windowController;
- mDragController = new TaskbarDragController(this);
- mOnboardingPrefs = new OnboardingPrefs<>(this, Utilities.getPrefs(this));
-
- mDragLayer = new TaskbarAllAppsDragLayer(this);
- TaskbarAllAppsSlideInView slideInView = (TaskbarAllAppsSlideInView) mLayoutInflater.inflate(
- R.layout.taskbar_all_apps, mDragLayer, false);
- mAllAppsViewController = new TaskbarAllAppsViewController(
- this,
- slideInView,
- windowController,
- taskbarStashController);
- mAppsView = slideInView.getAppsView();
-
- mWillTaskbarBeVisuallyStashed = taskbarStashController.supportsVisualStashing();
- mStashedTaskbarHeight = taskbarStashController.getStashedHeight();
- }
-
- TaskbarAllAppsViewController getAllAppsViewController() {
- return mAllAppsViewController;
- }
-
- @Override
- public DeviceProfile getDeviceProfile() {
- return mWindowController.getDeviceProfile();
- }
-
- @Override
- public TaskbarDragController getDragController() {
- return mDragController;
- }
-
- @Override
- public TaskbarAllAppsDragLayer getDragLayer() {
- return mDragLayer;
- }
-
- @Override
- public TaskbarAllAppsContainerView getAppsView() {
- return mAppsView;
- }
-
- @Override
- public OnboardingPrefs<TaskbarAllAppsContext> getOnboardingPrefs() {
- return mOnboardingPrefs;
- }
-
- @Override
- public boolean isBindingItems() {
- return mTaskbarContext.isBindingItems();
- }
-
- @Override
- public View.OnClickListener getItemOnClickListener() {
- return mTaskbarContext.getItemOnClickListener();
- }
-
- @Override
- public PopupDataProvider getPopupDataProvider() {
- return mTaskbarContext.getPopupDataProvider();
- }
-
- @Override
- public DotInfo getDotInfoForItem(ItemInfo info) {
- return mTaskbarContext.getDotInfoForItem(info);
- }
-
- @Override
- public void onDragStart() {}
-
- @Override
- public void onDragEnd() {
- mWindowController.maybeCloseWindow();
- }
-
- @Override
- public void onPopupVisibilityChanged(boolean isVisible) {}
-
- @Override
- public SearchAdapterProvider<?> createSearchAdapterProvider(
- ActivityAllAppsContainerView<?> appsView) {
- return new DefaultSearchAdapterProvider(this);
- }
-
- /** Root drag layer for this context. */
- private static class TaskbarAllAppsDragLayer extends
- BaseDragLayer<TaskbarAllAppsContext> implements OnComputeInsetsListener {
-
- private TaskbarAllAppsDragLayer(Context context) {
- super(context, null, 1);
- setClipChildren(false);
- recreateControllers();
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- ViewTreeObserverWrapper.addOnComputeInsetsListener(
- getViewTreeObserver(), this);
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- ViewTreeObserverWrapper.removeOnComputeInsetsListener(this);
- }
-
- @Override
- public void recreateControllers() {
- mControllers = new TouchController[]{mActivity.mDragController};
- }
-
- @Override
- public boolean dispatchTouchEvent(MotionEvent ev) {
- TestLogging.recordMotionEvent(TestProtocol.SEQUENCE_MAIN, "Touch event", ev);
- return super.dispatchTouchEvent(ev);
- }
-
- @Override
- public boolean dispatchKeyEvent(KeyEvent event) {
- if (event.getAction() == ACTION_UP && event.getKeyCode() == KEYCODE_BACK) {
- AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mActivity);
- if (topView != null && topView.onBackPressed()) {
- return true;
- }
- }
- return super.dispatchKeyEvent(event);
- }
-
- @Override
- public void onComputeInsets(InsetsInfo inoutInfo) {
- if (mActivity.mDragController.isSystemDragInProgress()) {
- inoutInfo.touchableRegion.setEmpty();
- inoutInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
- }
- }
-
- @Override
- public WindowInsets onApplyWindowInsets(WindowInsets insets) {
- return updateInsetsDueToStashing(insets);
- }
-
- /**
- * Taskbar automatically stashes when opening all apps, but we don't report the insets as
- * changing to avoid moving the underlying app. But internally, the apps view should still
- * layout according to the stashed insets rather than the unstashed insets. So this method
- * does two things:
- * 1) Sets navigationBars bottom inset to stashedHeight.
- * 2) Sets tappableInsets bottom inset to 0.
- */
- private WindowInsets updateInsetsDueToStashing(WindowInsets oldInsets) {
- if (!mActivity.mWillTaskbarBeVisuallyStashed) {
- return oldInsets;
- }
- WindowInsets.Builder updatedInsetsBuilder = new WindowInsets.Builder(oldInsets);
-
- Insets oldNavInsets = oldInsets.getInsets(WindowInsets.Type.navigationBars());
- Insets newNavInsets = Insets.of(oldNavInsets.left, oldNavInsets.top, oldNavInsets.right,
- mActivity.mStashedTaskbarHeight);
- updatedInsetsBuilder.setInsets(WindowInsets.Type.navigationBars(), newNavInsets);
-
- Insets oldTappableInsets = oldInsets.getInsets(WindowInsets.Type.tappableElement());
- Insets newTappableInsets = Insets.of(oldTappableInsets.left, oldTappableInsets.top,
- oldTappableInsets.right, 0);
- updatedInsetsBuilder.setInsets(WindowInsets.Type.tappableElement(), newTappableInsets);
-
- return updatedInsetsBuilder.build();
- }
- }
-}
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
index 6c43e50..4dc8d47 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
@@ -15,34 +15,18 @@
*/
package com.android.launcher3.taskbar.allapps;
-import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
-
-import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
-import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE;
-
-import android.content.Context;
-import android.graphics.PixelFormat;
-import android.view.Gravity;
-import android.view.MotionEvent;
-import android.view.WindowManager;
-import android.view.WindowManager.LayoutParams;
-
import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
-import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.R;
import com.android.launcher3.appprediction.PredictionRowView;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.launcher3.taskbar.TaskbarControllers;
-import com.android.systemui.shared.system.TaskStackChangeListener;
-import com.android.systemui.shared.system.TaskStackChangeListeners;
+import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
import java.util.List;
-import java.util.Optional;
/**
* Handles the all apps overlay window initialization, updates, and its data.
@@ -57,35 +41,15 @@
*/
public final class TaskbarAllAppsController {
- private static final String WINDOW_TITLE = "Taskbar All Apps";
-
- private final TaskbarActivityContext mTaskbarContext;
- private final TaskbarAllAppsProxyView mProxyView;
- private final LayoutParams mLayoutParams;
-
- private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
- @Override
- public void onTaskStackChanged() {
- mProxyView.close(false);
- }
- };
-
- private DeviceProfile mDeviceProfile;
private TaskbarControllers mControllers;
- /** Window context for all apps if it is open. */
- private @Nullable TaskbarAllAppsContext mAllAppsContext;
+ private @Nullable TaskbarAllAppsContainerView mAppsView;
// Application data models.
private AppInfo[] mApps;
private int mAppsModelFlags;
private List<ItemInfo> mPredictedApps;
-
- public TaskbarAllAppsController(TaskbarActivityContext context, DeviceProfile dp) {
- mDeviceProfile = dp;
- mTaskbarContext = context;
- mProxyView = new TaskbarAllAppsProxyView(mTaskbarContext);
- mLayoutParams = createLayoutParams();
- }
+ private boolean mDisallowGlobalDrag;
+ private boolean mDisallowLongClick;
/** Initialize the controller. */
public void init(TaskbarControllers controllers, boolean allAppsVisible) {
@@ -111,11 +75,19 @@
mApps = apps;
mAppsModelFlags = flags;
- if (mAllAppsContext != null) {
- mAllAppsContext.getAppsView().getAppsStore().setApps(mApps, mAppsModelFlags);
+ if (mAppsView != null) {
+ mAppsView.getAppsStore().setApps(mApps, mAppsModelFlags);
}
}
+ public void setDisallowGlobalDrag(boolean disableDragForOverviewState) {
+ mDisallowGlobalDrag = disableDragForOverviewState;
+ }
+
+ public void setDisallowLongClick(boolean disallowLongClick) {
+ mDisallowLongClick = disallowLongClick;
+ }
+
/** Updates the current predictions. */
public void setPredictedApps(List<ItemInfo> predictedApps) {
if (!FeatureFlags.ENABLE_ALL_APPS_IN_TASKBAR.get()) {
@@ -123,8 +95,8 @@
}
mPredictedApps = predictedApps;
- if (mAllAppsContext != null) {
- mAllAppsContext.getAppsView().getFloatingHeaderView()
+ if (mAppsView != null) {
+ mAppsView.getFloatingHeaderView()
.findFixedRowByType(PredictionRowView.class)
.setPredictedApps(mPredictedApps);
}
@@ -136,122 +108,43 @@
}
private void show(boolean animate) {
- if (mProxyView.isOpen()) {
+ if (mAppsView != null) {
return;
}
- mProxyView.show();
// mControllers and getSharedState should never be null here. Do not handle null-pointer
// to catch invalid states.
mControllers.getSharedState().allAppsVisible = true;
- mAllAppsContext = new TaskbarAllAppsContext(mTaskbarContext,
- this,
- mControllers.taskbarStashController);
- mAllAppsContext.getDragController().init(mControllers);
- TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
- Optional.ofNullable(mAllAppsContext.getSystemService(WindowManager.class))
- .ifPresent(m -> m.addView(mAllAppsContext.getDragLayer(), mLayoutParams));
+ TaskbarOverlayContext overlayContext =
+ mControllers.taskbarOverlayController.requestWindow();
+ TaskbarAllAppsSlideInView slideInView =
+ (TaskbarAllAppsSlideInView) overlayContext.getLayoutInflater().inflate(
+ R.layout.taskbar_all_apps, overlayContext.getDragLayer(), false);
+ slideInView.addOnCloseListener(() -> {
+ mControllers.getSharedState().allAppsVisible = false;
+ mAppsView = null;
+ });
+ TaskbarAllAppsViewController viewController = new TaskbarAllAppsViewController(
+ overlayContext, slideInView, mControllers);
- mAllAppsContext.getAppsView().getAppsStore().setApps(mApps, mAppsModelFlags);
- mAllAppsContext.getAppsView().getFloatingHeaderView()
+ viewController.show(animate);
+ mAppsView = overlayContext.getAppsView();
+ mAppsView.getAppsStore().setApps(mApps, mAppsModelFlags);
+ mAppsView.getFloatingHeaderView()
.findFixedRowByType(PredictionRowView.class)
.setPredictedApps(mPredictedApps);
- mAllAppsContext.getAllAppsViewController().show(animate);
+ // 1 alternative that would be more work:
+ // 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);
}
- /** Closes the {@link TaskbarAllAppsContainerView}. */
- public void hide() {
- mProxyView.close(true);
- }
- /**
- * Removes the all apps window from the hierarchy, if all floating views are closed and there is
- * no system drag operation in progress.
- * <p>
- * This method should be called after an exit animation finishes, if applicable.
- */
- void maybeCloseWindow() {
- if (mAllAppsContext != null && (AbstractFloatingView.hasOpenView(mAllAppsContext, TYPE_ALL)
- || mAllAppsContext.getDragController().isSystemDragInProgress())) {
- return;
- }
- mProxyView.close(false);
- // mControllers and getSharedState should never be null here. Do not handle null-pointer
- // to catch invalid states.
- mControllers.getSharedState().allAppsVisible = false;
- onDestroy();
- }
-
- /** Destroys the controller and any All Apps window if present. */
- public void onDestroy() {
- TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
- Optional.ofNullable(mAllAppsContext)
- .map(c -> c.getSystemService(WindowManager.class))
- .ifPresent(m -> m.removeViewImmediate(mAllAppsContext.getDragLayer()));
- mAllAppsContext = null;
- }
-
- /** Updates {@link DeviceProfile} instance for Taskbar's All Apps window. */
- public void updateDeviceProfile(DeviceProfile dp) {
- mDeviceProfile = dp;
- Optional.ofNullable(mAllAppsContext).ifPresent(c -> {
- AbstractFloatingView.closeAllOpenViewsExcept(c, false, TYPE_REBIND_SAFE);
- c.dispatchDeviceProfileChanged();
- });
- }
-
- DeviceProfile getDeviceProfile() {
- return mDeviceProfile;
- }
-
- private LayoutParams createLayoutParams() {
- LayoutParams layoutParams = new LayoutParams(
- TYPE_APPLICATION_OVERLAY,
- WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
- PixelFormat.TRANSLUCENT);
- layoutParams.setTitle(WINDOW_TITLE);
- layoutParams.gravity = Gravity.BOTTOM;
- layoutParams.packageName = mTaskbarContext.getPackageName();
- layoutParams.setFitInsetsTypes(0); // Handled by container view.
- layoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- layoutParams.setSystemApplicationOverlay(true);
- return layoutParams;
- }
-
- /**
- * Proxy view connecting taskbar drag layer to the all apps window.
- * <p>
- * The all apps view is in a separate window and has its own drag layer, but this proxy lets it
- * behave as though its in the taskbar drag layer. For instance, when the taskbar closes all
- * {@link AbstractFloatingView} instances, the all apps window will also close.
- */
- private class TaskbarAllAppsProxyView extends AbstractFloatingView {
-
- private TaskbarAllAppsProxyView(Context context) {
- super(context, null);
- }
-
- private void show() {
- mIsOpen = true;
- mTaskbarContext.getDragLayer().addView(this);
- }
-
- @Override
- protected void handleClose(boolean animate) {
- mTaskbarContext.getDragLayer().removeView(this);
- Optional.ofNullable(mAllAppsContext)
- .map(TaskbarAllAppsContext::getAllAppsViewController)
- .ifPresent(v -> v.close(animate));
- }
-
- @Override
- protected boolean isOfType(int type) {
- return (type & TYPE_TASKBAR_ALL_APPS) != 0;
- }
-
- @Override
- public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
- return false;
- }
+ @VisibleForTesting
+ public int getTaskbarAllAppsTopPadding() {
+ // Allow null-pointer since this should only be null if the apps view is not showing.
+ return mAppsView.getActiveRecyclerView().getClipBounds().top;
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
index c4837a0..c8bfc2a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
@@ -16,8 +16,7 @@
package com.android.launcher3.taskbar.allapps;
import static com.android.launcher3.LauncherState.ALL_APPS;
-import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
-import static com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE;
+import static com.android.launcher3.anim.Interpolators.EMPHASIZED;
import android.animation.PropertyValuesHolder;
import android.content.Context;
@@ -29,15 +28,13 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Insettable;
import com.android.launcher3.R;
+import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
import com.android.launcher3.views.AbstractSlideInView;
-import java.util.Optional;
-
/** Wrapper for taskbar all apps with slide-in behavior. */
-public class TaskbarAllAppsSlideInView extends AbstractSlideInView<TaskbarAllAppsContext>
+public class TaskbarAllAppsSlideInView extends AbstractSlideInView<TaskbarOverlayContext>
implements Insettable, DeviceProfile.OnDeviceProfileChangeListener {
private TaskbarAllAppsContainerView mAppsView;
- private OnCloseListener mOnCloseBeginListener;
private float mShiftRange;
public TaskbarAllAppsSlideInView(Context context, AttributeSet attrs) {
@@ -60,7 +57,7 @@
if (animate) {
mOpenCloseAnimator.setValues(
PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
- mOpenCloseAnimator.setInterpolator(AGGRESSIVE_EASE);
+ mOpenCloseAnimator.setInterpolator(EMPHASIZED);
mOpenCloseAnimator.setDuration(
ALL_APPS.getTransitionDuration(mActivityContext, true /* isToState */)).start();
} else {
@@ -73,21 +70,15 @@
return mAppsView;
}
- /** Callback invoked when the view is beginning to close (e.g. close animation is started). */
- void setOnCloseBeginListener(OnCloseListener onCloseBeginListener) {
- mOnCloseBeginListener = onCloseBeginListener;
- }
-
@Override
protected void handleClose(boolean animate) {
- Optional.ofNullable(mOnCloseBeginListener).ifPresent(OnCloseListener::onSlideInViewClosed);
handleClose(animate,
ALL_APPS.getTransitionDuration(mActivityContext, false /* isToState */));
}
@Override
protected Interpolator getIdleInterpolator() {
- return EMPHASIZED_ACCELERATE;
+ return EMPHASIZED;
}
@Override
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java
index a39e872..54392b2 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java
@@ -16,13 +16,16 @@
package com.android.launcher3.taskbar.allapps;
import static com.android.launcher3.LauncherState.ALL_APPS;
-import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_STASHED_IN_APP_ALL_APPS;
+import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_STASHED_IN_TASKBAR_ALL_APPS;
import static com.android.launcher3.util.OnboardingPrefs.ALL_APPS_VISITED_COUNT;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.appprediction.AppsDividerView;
import com.android.launcher3.appprediction.PredictionRowView;
+import com.android.launcher3.taskbar.NavbarButtonsViewController;
+import com.android.launcher3.taskbar.TaskbarControllers;
import com.android.launcher3.taskbar.TaskbarStashController;
+import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
/**
* Handles the {@link TaskbarAllAppsContainerView} behavior and synchronizes its transitions with
@@ -30,26 +33,26 @@
*/
final class TaskbarAllAppsViewController {
- private final TaskbarAllAppsContext mContext;
+ private final TaskbarOverlayContext mContext;
private final TaskbarAllAppsSlideInView mSlideInView;
private final TaskbarAllAppsContainerView mAppsView;
private final TaskbarStashController mTaskbarStashController;
+ private final NavbarButtonsViewController mNavbarButtonsViewController;
TaskbarAllAppsViewController(
- TaskbarAllAppsContext context,
+ TaskbarOverlayContext context,
TaskbarAllAppsSlideInView slideInView,
- TaskbarAllAppsController windowController,
- TaskbarStashController taskbarStashController) {
+ TaskbarControllers taskbarControllers) {
mContext = context;
mSlideInView = slideInView;
mAppsView = mSlideInView.getAppsView();
- mTaskbarStashController = taskbarStashController;
+ mTaskbarStashController = taskbarControllers.taskbarStashController;
+ mNavbarButtonsViewController = taskbarControllers.navbarButtonsViewController;
setUpIconLongClick();
setUpAppDivider();
setUpTaskbarStashing();
- mSlideInView.addOnCloseListener(windowController::maybeCloseWindow);
}
/** Starts the {@link TaskbarAllAppsSlideInView} enter transition. */
@@ -80,10 +83,12 @@
}
private void setUpTaskbarStashing() {
- mTaskbarStashController.updateStateForFlag(FLAG_STASHED_IN_APP_ALL_APPS, true);
+ mTaskbarStashController.updateStateForFlag(FLAG_STASHED_IN_TASKBAR_ALL_APPS, true);
mTaskbarStashController.applyState(
ALL_APPS.getTransitionDuration(mContext, true /* isToState */));
+ mNavbarButtonsViewController.setSlideInViewVisible(true);
mSlideInView.setOnCloseBeginListener(() -> {
+ mNavbarButtonsViewController.setSlideInViewVisible(false);
AbstractFloatingView.closeOpenContainer(
mContext, AbstractFloatingView.TYPE_ACTION_POPUP);
// Post in case view is closing due to gesture navigation. If a gesture is in progress,
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt
new file mode 100644
index 0000000..68ea27a
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.launcher3.taskbar.navbutton
+
+import android.content.res.Resources
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.LinearLayout
+import com.android.launcher3.R
+import com.android.launcher3.taskbar.navbutton.NavButtonLayoutFactory.NavButtonLayoutter
+
+/**
+ * Meant to be a simple container for data subclasses will need
+ *
+ * Assumes that the 3 navigation buttons (back/home/recents) have already been added to
+ * [navButtonContainer]
+ *
+ * @property navButtonContainer ViewGroup that holds the 3 navigation buttons.
+ * @property endContextualContainer ViewGroup that holds the end contextual button (ex, IME dismiss).
+ * @property startContextualContainer ViewGroup that holds the start contextual button (ex, A11y).
+ */
+abstract class AbstractNavButtonLayoutter(
+ val resources: Resources,
+ val navButtonContainer: LinearLayout,
+ protected val endContextualContainer: ViewGroup,
+ protected val startContextualContainer: ViewGroup
+) : NavButtonLayoutter {
+ protected val homeButton: ImageView = navButtonContainer
+ .findViewById(R.id.home)
+ protected val recentsButton: ImageView = navButtonContainer
+ .findViewById(R.id.recent_apps)
+ protected val backButton: ImageView = navButtonContainer
+ .findViewById(R.id.back)
+}
+
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/KidsNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/KidsNavLayoutter.kt
new file mode 100644
index 0000000..c67ab79
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/KidsNavLayoutter.kt
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.launcher3.taskbar.navbutton
+
+import android.content.res.Resources
+import android.graphics.Color
+import android.graphics.drawable.PaintDrawable
+import android.view.Gravity
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import android.widget.ImageView
+import android.widget.LinearLayout
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.DIMEN_TASKBAR_BACK_BUTTON_LEFT_MARGIN_KIDS
+import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.DIMEN_TASKBAR_HOME_BUTTON_LEFT_MARGIN_KIDS
+import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.DIMEN_TASKBAR_ICON_SIZE_KIDS
+import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.DIMEN_TASKBAR_NAV_BUTTONS_CORNER_RADIUS_KIDS
+import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.DIMEN_TASKBAR_NAV_BUTTONS_HEIGHT_KIDS
+import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.DIMEN_TASKBAR_NAV_BUTTONS_WIDTH_KIDS
+import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.DRAWABLE_SYSBAR_BACK_KIDS
+import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.DRAWABLE_SYSBAR_HOME_KIDS
+
+class KidsNavLayoutter(
+ resources: Resources,
+ navBarContainer: LinearLayout,
+ endContextualContainer: ViewGroup,
+ startContextualContainer: ViewGroup
+) : AbstractNavButtonLayoutter(
+ resources,
+ navBarContainer,
+ endContextualContainer,
+ startContextualContainer
+) {
+
+ override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) {
+ val iconSize: Int = resources.getDimensionPixelSize(
+ DIMEN_TASKBAR_ICON_SIZE_KIDS)
+ val buttonWidth: Int = resources.getDimensionPixelSize(
+ DIMEN_TASKBAR_NAV_BUTTONS_WIDTH_KIDS)
+ val buttonHeight: Int = resources.getDimensionPixelSize(
+ DIMEN_TASKBAR_NAV_BUTTONS_HEIGHT_KIDS)
+ val buttonRadius: Int = resources.getDimensionPixelSize(
+ DIMEN_TASKBAR_NAV_BUTTONS_CORNER_RADIUS_KIDS)
+ val paddingLeft = (buttonWidth - iconSize) / 2
+ val paddingTop = (buttonHeight - iconSize) / 2
+
+ // Update icons
+ backButton.setImageDrawable(
+ backButton.context.getDrawable(DRAWABLE_SYSBAR_BACK_KIDS))
+ backButton.scaleType = ImageView.ScaleType.FIT_CENTER
+ backButton.setPadding(paddingLeft, paddingTop, paddingLeft, paddingTop)
+ homeButton.setImageDrawable(
+ homeButton.getContext().getDrawable(DRAWABLE_SYSBAR_HOME_KIDS))
+ homeButton.scaleType = ImageView.ScaleType.FIT_CENTER
+ homeButton.setPadding(paddingLeft, paddingTop, paddingLeft, paddingTop)
+
+ // Home button layout
+ val homeLayoutparams = LinearLayout.LayoutParams(
+ buttonWidth,
+ buttonHeight
+ )
+ val homeButtonLeftMargin: Int = resources.getDimensionPixelSize(
+ DIMEN_TASKBAR_HOME_BUTTON_LEFT_MARGIN_KIDS)
+ homeLayoutparams.setMargins(homeButtonLeftMargin, 0, 0, 0)
+ homeButton.layoutParams = homeLayoutparams
+
+ // Back button layout
+ val backLayoutParams = LinearLayout.LayoutParams(
+ buttonWidth,
+ buttonHeight
+ )
+ val backButtonLeftMargin: Int = resources.getDimensionPixelSize(
+ DIMEN_TASKBAR_BACK_BUTTON_LEFT_MARGIN_KIDS)
+ backLayoutParams.setMargins(backButtonLeftMargin, 0, 0, 0)
+ backButton.layoutParams = backLayoutParams
+
+ // Button backgrounds
+ val whiteWith10PctAlpha = Color.argb(0.1f, 1f, 1f, 1f)
+ val buttonBackground = PaintDrawable(whiteWith10PctAlpha)
+ buttonBackground.setCornerRadius(buttonRadius.toFloat())
+ homeButton.background = buttonBackground
+ backButton.background = buttonBackground
+
+ // Update alignment within taskbar
+ val navButtonsLayoutParams = navButtonContainer.layoutParams as FrameLayout.LayoutParams
+ navButtonsLayoutParams.apply {
+ marginStart = navButtonsLayoutParams.marginEnd / 2
+ marginEnd = navButtonsLayoutParams.marginStart
+ gravity = Gravity.CENTER
+ }
+ navButtonContainer.requestLayout()
+
+ homeButton.onLongClickListener = null
+ }
+}
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/LayoutResourceHelper.java b/quickstep/src/com/android/launcher3/taskbar/navbutton/LayoutResourceHelper.java
new file mode 100644
index 0000000..0d9b855
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/LayoutResourceHelper.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.launcher3.taskbar.navbutton;
+
+import android.annotation.DimenRes;
+import android.annotation.DrawableRes;
+import android.annotation.IdRes;
+
+import com.android.launcher3.R;
+
+/**
+ * A class for retrieving resources in Kotlin.
+ *
+ * This class should be removed once the build system supports resources loading in Kotlin.
+ */
+public final class LayoutResourceHelper {
+
+ // --------------------------
+ // Kids Nav Layout
+ @DimenRes
+ public static final int DIMEN_TASKBAR_ICON_SIZE_KIDS = R.dimen.taskbar_icon_size_kids;
+ @DrawableRes
+ public static final int DRAWABLE_SYSBAR_BACK_KIDS = R.drawable.ic_sysbar_back_kids;
+ @DrawableRes
+ public static final int DRAWABLE_SYSBAR_HOME_KIDS = R.drawable.ic_sysbar_home_kids;
+ @DimenRes
+ public static final int DIMEN_TASKBAR_HOME_BUTTON_LEFT_MARGIN_KIDS =
+ R.dimen.taskbar_home_button_left_margin_kids;
+ @DimenRes
+ public static final int DIMEN_TASKBAR_BACK_BUTTON_LEFT_MARGIN_KIDS =
+ R.dimen.taskbar_back_button_left_margin_kids;
+ @DimenRes
+ public static final int DIMEN_TASKBAR_NAV_BUTTONS_WIDTH_KIDS =
+ R.dimen.taskbar_nav_buttons_width_kids;
+ @DimenRes
+ public static final int DIMEN_TASKBAR_NAV_BUTTONS_HEIGHT_KIDS =
+ R.dimen.taskbar_nav_buttons_height_kids;
+ @DimenRes
+ public static final int DIMEN_TASKBAR_NAV_BUTTONS_CORNER_RADIUS_KIDS =
+ R.dimen.taskbar_nav_buttons_corner_radius_kids;
+
+ // --------------------------
+ // Nav Layout Factory
+ @IdRes
+ public static final int ID_START_CONTEXTUAL_BUTTONS = R.id.start_contextual_buttons;
+ @IdRes
+ public static final int ID_END_CONTEXTUAL_BUTTONS = R.id.end_contextual_buttons;
+ @IdRes
+ public static final int ID_END_NAV_BUTTONS = R.id.end_nav_buttons;
+
+ private LayoutResourceHelper() {
+
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt
new file mode 100644
index 0000000..db0a2d8
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.launcher3.taskbar.navbutton
+
+import android.content.res.Resources
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import android.widget.LinearLayout
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.ID_END_CONTEXTUAL_BUTTONS
+import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.ID_END_NAV_BUTTONS
+import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.ID_START_CONTEXTUAL_BUTTONS
+import com.android.launcher3.taskbar.navbutton.NavButtonLayoutFactory.Companion
+import com.android.launcher3.taskbar.navbutton.NavButtonLayoutFactory.NavButtonLayoutter
+
+/**
+ * Select the correct layout for nav buttons
+ *
+ * Since layouts are done dynamically for the nav buttons on Taskbar, this
+ * class returns a corresponding [NavButtonLayoutter] via
+ * [Companion.getUiLayoutter]
+ * that can help position the buttons based on the current [DeviceProfile]
+ */
+class NavButtonLayoutFactory {
+ companion object {
+ /**
+ * Get the correct instance of [NavButtonLayoutter]
+ *
+ * No layouts supported for configurations where:
+ * * taskbar isn't showing AND
+ * * the device is not in [phoneMode]
+ * OR
+ * * phone is showing
+ * * device is using gesture navigation
+ *
+ * @param navButtonsView ViewGroup that contains start, end, nav button ViewGroups
+ * @param isKidsMode no-op when taskbar is hidden/not showing
+ * @param isInSetup no-op when taskbar is hidden/not showing
+ * @param phoneMode refers to the device using the taskbar window on phones
+ * @param isThreeButtonNav are no-ops when taskbar is present/showing
+ */
+ fun getUiLayoutter(deviceProfile: DeviceProfile,
+ navButtonsView: FrameLayout,
+ resources: Resources,
+ isKidsMode: Boolean,
+ isInSetup: Boolean,
+ isThreeButtonNav: Boolean,
+ phoneMode: Boolean):
+ NavButtonLayoutter {
+ val navButtonContainer =
+ navButtonsView.findViewById<LinearLayout>(ID_END_NAV_BUTTONS)
+ val endContextualContainer =
+ navButtonsView.findViewById<ViewGroup>(ID_END_CONTEXTUAL_BUTTONS)
+ val startContextualContainer =
+ navButtonsView.findViewById<ViewGroup>(ID_START_CONTEXTUAL_BUTTONS)
+ val isPhoneNavMode = phoneMode && isThreeButtonNav
+ return when {
+ isPhoneNavMode -> {
+ if (!deviceProfile.isLandscape) {
+ PhonePortraitNavLayoutter(resources, navButtonContainer,
+ endContextualContainer, startContextualContainer)
+ } else {
+ PhoneLandscapeNavLayoutter(resources, navButtonContainer,
+ endContextualContainer, startContextualContainer)
+ }
+ }
+ deviceProfile.isTaskbarPresent -> {
+ return when {
+ isInSetup -> {
+ SetupNavLayoutter(resources, navButtonContainer, endContextualContainer,
+ startContextualContainer)
+ }
+ isKidsMode -> {
+ KidsNavLayoutter(resources, navButtonContainer, endContextualContainer,
+ startContextualContainer)
+ }
+ else ->
+ TaskbarNavLayoutter(resources, navButtonContainer, endContextualContainer,
+ startContextualContainer)
+ }
+ }
+ else -> error("No layoutter found")
+ }
+ }
+ }
+
+ /** Lays out and provides access to the home, recents, and back buttons for various mischief */
+ interface NavButtonLayoutter {
+ fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean)
+ }
+}
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt
new file mode 100644
index 0000000..a89476e
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.launcher3.taskbar.navbutton
+
+import android.content.res.Resources
+import android.view.Gravity
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import android.widget.LinearLayout
+import androidx.core.view.children
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.R
+import com.android.launcher3.taskbar.TaskbarManager
+import com.android.launcher3.util.DimensionUtils
+
+class PhoneLandscapeNavLayoutter(
+ resources: Resources,
+ navBarContainer: LinearLayout,
+ endContextualContainer: ViewGroup,
+ startContextualContainer: ViewGroup
+) : AbstractNavButtonLayoutter(
+ resources,
+ navBarContainer,
+ endContextualContainer,
+ startContextualContainer
+) {
+
+ override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) {
+ // TODO(b/230395757): Polish pending, this is just to make it usable
+ val navContainerParams = navButtonContainer.layoutParams as FrameLayout.LayoutParams
+ val endStartMargins =
+ resources.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size)
+ val taskbarDimensions = DimensionUtils.getTaskbarPhoneDimensions(dp, resources,
+ TaskbarManager.isPhoneMode(dp))
+ navButtonContainer.removeAllViews()
+ navButtonContainer.orientation = LinearLayout.VERTICAL
+
+ navContainerParams.apply {
+ width = taskbarDimensions.x
+ height = ViewGroup.LayoutParams.MATCH_PARENT
+ gravity = Gravity.CENTER
+ topMargin = endStartMargins
+ bottomMargin = endStartMargins
+ marginEnd = 0
+ marginStart = 0
+ }
+
+ // Swap recents and back button
+ navButtonContainer.addView(recentsButton)
+ navButtonContainer.addView(homeButton)
+ navButtonContainer.addView(backButton)
+
+ navButtonContainer.layoutParams = navContainerParams
+
+ // Add the spaces in between the nav buttons
+ val spaceInBetween: Int =
+ resources.getDimensionPixelSize(R.dimen.taskbar_button_space_inbetween_phone)
+ navButtonContainer.children.forEachIndexed { i, navButton ->
+ val buttonLayoutParams = navButton.layoutParams as LinearLayout.LayoutParams
+ buttonLayoutParams.weight = 1f
+ when (i) {
+ 0 -> {
+ buttonLayoutParams.bottomMargin = spaceInBetween / 2
+ }
+ navButtonContainer.childCount - 1 -> {
+ buttonLayoutParams.topMargin = spaceInBetween / 2
+ }
+ else -> {
+ buttonLayoutParams.bottomMargin = spaceInBetween / 2
+ buttonLayoutParams.topMargin = spaceInBetween / 2
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt
new file mode 100644
index 0000000..275f59f
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.launcher3.taskbar.navbutton
+
+import android.content.res.Resources
+import android.view.Gravity
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import android.widget.LinearLayout
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.R
+import com.android.launcher3.taskbar.TaskbarManager
+import com.android.launcher3.util.DimensionUtils
+
+class PhonePortraitNavLayoutter(resources: Resources, navBarContainer: LinearLayout,
+ endContextualContainer: ViewGroup,
+ startContextualContainer: ViewGroup) :
+ AbstractNavButtonLayoutter(resources, navBarContainer, endContextualContainer,
+ startContextualContainer) {
+
+ override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) {
+ // TODO(b/230395757): Polish pending, this is just to make it usable
+ val navContainerParams = navButtonContainer.layoutParams as FrameLayout.LayoutParams
+ val taskbarDimensions = DimensionUtils.getTaskbarPhoneDimensions(dp, resources,
+ TaskbarManager.isPhoneMode(dp))
+ val endStartMargins = resources.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size)
+ navContainerParams.width = taskbarDimensions.x
+ navContainerParams.height = ViewGroup.LayoutParams.MATCH_PARENT
+ navContainerParams.gravity = Gravity.CENTER_VERTICAL
+
+ // Ensure order of buttons is correct
+ navButtonContainer.removeAllViews()
+ navButtonContainer.orientation = LinearLayout.HORIZONTAL
+ navContainerParams.topMargin = 0
+ navContainerParams.bottomMargin = 0
+ navContainerParams.marginEnd = endStartMargins
+ navContainerParams.marginStart = endStartMargins
+ // Swap recents and back button in case we were landscape prior to this
+ navButtonContainer.addView(backButton)
+ navButtonContainer.addView(homeButton)
+ navButtonContainer.addView(recentsButton)
+
+ navButtonContainer.layoutParams = navContainerParams
+
+ // Add the spaces in between the nav buttons
+ val spaceInBetween =
+ resources.getDimensionPixelSize(R.dimen.taskbar_button_space_inbetween_phone)
+ for (i in 0 until navButtonContainer.childCount) {
+ val navButton = navButtonContainer.getChildAt(i)
+ val buttonLayoutParams = navButton.layoutParams as LinearLayout.LayoutParams
+ buttonLayoutParams.weight = 1f
+ when (i) {
+ 0 -> {
+ // First button
+ buttonLayoutParams.marginEnd = spaceInBetween / 2
+ }
+ navButtonContainer.childCount - 1 -> {
+ // Last button
+ buttonLayoutParams.marginStart = spaceInBetween / 2
+ }
+ else -> {
+ // other buttons
+ buttonLayoutParams.marginStart = spaceInBetween / 2
+ buttonLayoutParams.marginEnd = spaceInBetween / 2
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt
new file mode 100644
index 0000000..afe70d6
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.launcher3.taskbar.navbutton
+
+import android.content.res.Resources
+import android.view.Gravity
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import android.widget.LinearLayout
+import com.android.launcher3.DeviceProfile
+
+class SetupNavLayoutter(
+ resources: Resources,
+ navButtonContainer: LinearLayout,
+ endContextualContainer: ViewGroup,
+ startContextualContainer: ViewGroup
+) : AbstractNavButtonLayoutter(
+ resources,
+ navButtonContainer,
+ endContextualContainer,
+ startContextualContainer
+) {
+
+ override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) {
+ // Since setup wizard only has back button enabled, it looks strange to be
+ // end-aligned, so start-align instead.
+ val navButtonsLayoutParams = navButtonContainer.layoutParams as FrameLayout.LayoutParams
+ navButtonsLayoutParams.apply {
+ marginStart = navButtonsLayoutParams.marginEnd
+ marginEnd = 0
+ gravity = Gravity.START
+ }
+ navButtonContainer.requestLayout()
+ }
+}
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt
new file mode 100644
index 0000000..b2ca2af
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.launcher3.taskbar.navbutton
+
+import android.content.res.Resources
+import android.view.Gravity
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import android.widget.LinearLayout
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.R
+
+/**
+ * Layoutter for showing 3 button navigation on large screen
+ */
+class TaskbarNavLayoutter(
+ resources: Resources,
+ navBarContainer: LinearLayout,
+ endContextualContainer: ViewGroup,
+ startContextualContainer: ViewGroup
+) : AbstractNavButtonLayoutter(
+ resources,
+ navBarContainer,
+ endContextualContainer,
+ startContextualContainer
+) {
+
+ override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) {
+ // Add spacing after the end of the last nav button
+ val navButtonParams = navButtonContainer.layoutParams as FrameLayout.LayoutParams
+ var navMarginEnd = resources.getDimension(dp.inv.inlineNavButtonsEndSpacing).toInt()
+ val contextualWidth = endContextualContainer.width
+ // If contextual buttons are showing, we check if the end margin is enough for the
+ // contextual button to be showing - if not, move the nav buttons over a smidge
+ if (isContextualButtonShowing && navMarginEnd < contextualWidth) {
+ // Additional spacing, eat up half of space between last icon and nav button
+ navMarginEnd += resources.getDimensionPixelSize(R.dimen.taskbar_hotseat_nav_spacing) / 2
+ }
+
+ navButtonParams.apply {
+ gravity = Gravity.END
+ width = FrameLayout.LayoutParams.WRAP_CONTENT
+ height = ViewGroup.LayoutParams.MATCH_PARENT
+ marginEnd = navMarginEnd
+ }
+ navButtonContainer.orientation = LinearLayout.HORIZONTAL
+ navButtonContainer.layoutParams = navButtonParams
+
+ // Add the spaces in between the nav buttons
+ val spaceInBetween = resources.getDimensionPixelSize(R.dimen.taskbar_button_space_inbetween)
+ for (i in 0 until navButtonContainer.childCount) {
+ val navButton = navButtonContainer.getChildAt(i)
+ val buttonLayoutParams = navButton.layoutParams as LinearLayout.LayoutParams
+ buttonLayoutParams.weight = 0f
+ when (i) {
+ 0 -> {
+ buttonLayoutParams.marginEnd = spaceInBetween / 2
+ }
+ navButtonContainer.childCount - 1 -> {
+ buttonLayoutParams.marginStart = spaceInBetween / 2
+ }
+ else -> {
+ buttonLayoutParams.marginStart = spaceInBetween / 2
+ buttonLayoutParams.marginEnd = spaceInBetween / 2
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java
new file mode 100644
index 0000000..7e3163d
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.taskbar.overlay;
+
+import android.content.Context;
+import android.view.View;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.allapps.ActivityAllAppsContainerView;
+import com.android.launcher3.allapps.search.DefaultSearchAdapterProvider;
+import com.android.launcher3.allapps.search.SearchAdapterProvider;
+import com.android.launcher3.dot.DotInfo;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.popup.PopupDataProvider;
+import com.android.launcher3.taskbar.BaseTaskbarContext;
+import com.android.launcher3.taskbar.TaskbarActivityContext;
+import com.android.launcher3.taskbar.TaskbarControllers;
+import com.android.launcher3.taskbar.TaskbarDragController;
+import com.android.launcher3.taskbar.TaskbarStashController;
+import com.android.launcher3.taskbar.allapps.TaskbarAllAppsContainerView;
+import com.android.launcher3.util.OnboardingPrefs;
+
+/**
+ * Window context for the taskbar overlays such as All Apps and EDU.
+ * <p>
+ * Overlays have their own window and need a window context. Some properties are delegated to the
+ * {@link TaskbarActivityContext} such as {@link PopupDataProvider}.
+ */
+public class TaskbarOverlayContext extends BaseTaskbarContext {
+ private final TaskbarActivityContext mTaskbarContext;
+ private final OnboardingPrefs<TaskbarOverlayContext> mOnboardingPrefs;
+
+ private final TaskbarOverlayController mOverlayController;
+ private final TaskbarDragController mDragController;
+ private final TaskbarOverlayDragLayer mDragLayer;
+
+ // We automatically stash taskbar when All Apps is opened in gesture navigation mode.
+ private final boolean mWillTaskbarBeVisuallyStashed;
+ private final int mStashedTaskbarHeight;
+
+ public TaskbarOverlayContext(
+ Context windowContext,
+ TaskbarActivityContext taskbarContext,
+ TaskbarControllers controllers) {
+ super(windowContext);
+ mTaskbarContext = taskbarContext;
+ mOverlayController = controllers.taskbarOverlayController;
+ mDragController = new TaskbarDragController(this);
+ mDragController.init(controllers);
+ mOnboardingPrefs = new OnboardingPrefs<>(this, Utilities.getPrefs(this));
+ mDragLayer = new TaskbarOverlayDragLayer(this);
+
+ TaskbarStashController taskbarStashController = controllers.taskbarStashController;
+ mWillTaskbarBeVisuallyStashed = taskbarStashController.supportsVisualStashing();
+ mStashedTaskbarHeight = taskbarStashController.getStashedHeight();
+ }
+
+ boolean willTaskbarBeVisuallyStashed() {
+ return mWillTaskbarBeVisuallyStashed;
+ }
+
+ int getStashedTaskbarHeight() {
+ return mStashedTaskbarHeight;
+ }
+
+ public TaskbarOverlayController getOverlayController() {
+ return mOverlayController;
+ }
+
+ @Override
+ public DeviceProfile getDeviceProfile() {
+ return mOverlayController.getLauncherDeviceProfile();
+ }
+
+ @Override
+ public TaskbarDragController getDragController() {
+ return mDragController;
+ }
+
+ @Override
+ public TaskbarOverlayDragLayer getDragLayer() {
+ return mDragLayer;
+ }
+
+ @Override
+ public TaskbarAllAppsContainerView getAppsView() {
+ return mDragLayer.findViewById(R.id.apps_view);
+ }
+
+ @Override
+ public OnboardingPrefs<TaskbarOverlayContext> getOnboardingPrefs() {
+ return mOnboardingPrefs;
+ }
+
+ @Override
+ public boolean isBindingItems() {
+ return mTaskbarContext.isBindingItems();
+ }
+
+ @Override
+ public View.OnClickListener getItemOnClickListener() {
+ return mTaskbarContext.getItemOnClickListener();
+ }
+
+ @Override
+ public PopupDataProvider getPopupDataProvider() {
+ return mTaskbarContext.getPopupDataProvider();
+ }
+
+ @Override
+ public DotInfo getDotInfoForItem(ItemInfo info) {
+ return mTaskbarContext.getDotInfoForItem(info);
+ }
+
+ @Override
+ public void onDragStart() {}
+
+ @Override
+ public void onDragEnd() {
+ mOverlayController.maybeCloseWindow();
+ }
+
+ @Override
+ public void onPopupVisibilityChanged(boolean isVisible) {}
+
+ @Override
+ public SearchAdapterProvider<?> createSearchAdapterProvider(
+ ActivityAllAppsContainerView<?> appsView) {
+ return new DefaultSearchAdapterProvider(this);
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java
new file mode 100644
index 0000000..6c7bdbf
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.taskbar.overlay;
+
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
+import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
+import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.taskbar.TaskbarActivityContext;
+import com.android.launcher3.taskbar.TaskbarControllers;
+import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
+
+import java.util.Optional;
+
+/**
+ * Handles the Taskbar overlay window lifecycle.
+ * <p>
+ * Overlays need to be inflated in a separate window so that have the correct hierarchy. For
+ * instance, they need to be below the notification tray. If there are multiple overlays open, the
+ * same window is used.
+ */
+public final class TaskbarOverlayController {
+
+ private static final String WINDOW_TITLE = "Taskbar Overlay";
+
+ private final TaskbarActivityContext mTaskbarContext;
+ private final Context mWindowContext;
+ private final TaskbarOverlayProxyView mProxyView;
+ private final LayoutParams mLayoutParams;
+
+ private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
+ @Override
+ public void onTaskStackChanged() {
+ mProxyView.close(false);
+ }
+ };
+
+ private DeviceProfile mLauncherDeviceProfile;
+ private @Nullable TaskbarOverlayContext mOverlayContext;
+ private TaskbarControllers mControllers; // Initialized in init.
+
+ public TaskbarOverlayController(
+ TaskbarActivityContext taskbarContext, DeviceProfile launcherDeviceProfile) {
+ mTaskbarContext = taskbarContext;
+ mWindowContext = mTaskbarContext.createWindowContext(TYPE_APPLICATION_OVERLAY, null);
+ mProxyView = new TaskbarOverlayProxyView();
+ mLayoutParams = createLayoutParams();
+ mLauncherDeviceProfile = launcherDeviceProfile;
+ }
+
+ /** Initialize the controller. */
+ public void init(TaskbarControllers controllers) {
+ mControllers = controllers;
+ }
+
+ /**
+ * Creates a window for Taskbar overlays, if it does not already exist. Returns the window
+ * context for the current overlay window.
+ */
+ public TaskbarOverlayContext requestWindow() {
+ if (mOverlayContext == null) {
+ mOverlayContext = new TaskbarOverlayContext(
+ mWindowContext, mTaskbarContext, mControllers);
+ }
+
+ if (!mProxyView.isOpen()) {
+ mProxyView.show();
+ Optional.ofNullable(mOverlayContext.getSystemService(WindowManager.class))
+ .ifPresent(m -> m.addView(mOverlayContext.getDragLayer(), mLayoutParams));
+ TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
+ }
+
+ return mOverlayContext;
+ }
+
+ /** Hides the current overlay window with animation. */
+ public void hideWindow() {
+ mProxyView.close(true);
+ }
+
+ /**
+ * Removes the overlay window from the hierarchy, if all floating views are closed and there is
+ * no system drag operation in progress.
+ * <p>
+ * This method should be called after an exit animation finishes, if applicable.
+ */
+ @SuppressLint("WrongConstant")
+ void maybeCloseWindow() {
+ if (mOverlayContext != null && (AbstractFloatingView.hasOpenView(mOverlayContext, TYPE_ALL)
+ || mOverlayContext.getDragController().isSystemDragInProgress())) {
+ return;
+ }
+ mProxyView.close(false);
+ onDestroy();
+ }
+
+ /** Destroys the controller and any overlay window if present. */
+ public void onDestroy() {
+ TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
+ Optional.ofNullable(mOverlayContext)
+ .map(c -> c.getSystemService(WindowManager.class))
+ .ifPresent(m -> m.removeViewImmediate(mOverlayContext.getDragLayer()));
+ mOverlayContext = null;
+ }
+
+ /** The current device profile for the overlay window. */
+ public DeviceProfile getLauncherDeviceProfile() {
+ return mLauncherDeviceProfile;
+ }
+
+ /** Updates {@link DeviceProfile} instance for Taskbar's overlay window. */
+ public void updateLauncherDeviceProfile(DeviceProfile dp) {
+ mLauncherDeviceProfile = dp;
+ Optional.ofNullable(mOverlayContext).ifPresent(c -> {
+ AbstractFloatingView.closeAllOpenViewsExcept(c, false, TYPE_REBIND_SAFE);
+ c.dispatchDeviceProfileChanged();
+ });
+ }
+
+ @SuppressLint("WrongConstant")
+ private LayoutParams createLayoutParams() {
+ LayoutParams layoutParams = new LayoutParams(
+ TYPE_APPLICATION_OVERLAY,
+ LayoutParams.FLAG_SPLIT_TOUCH,
+ PixelFormat.TRANSLUCENT);
+ layoutParams.setTitle(WINDOW_TITLE);
+ layoutParams.gravity = Gravity.BOTTOM;
+ layoutParams.packageName = mTaskbarContext.getPackageName();
+ layoutParams.setFitInsetsTypes(0); // Handled by container view.
+ layoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ layoutParams.setSystemApplicationOverlay(true);
+ return layoutParams;
+ }
+
+ /**
+ * Proxy view connecting taskbar drag layer to the overlay window.
+ *
+ * Overlays are in a separate window and has its own drag layer, but this proxy lets its views
+ * behave as though they are in the taskbar drag layer. For instance, when the taskbar closes
+ * all {@link AbstractFloatingView} instances, the overlay window will also close.
+ */
+ private class TaskbarOverlayProxyView extends AbstractFloatingView {
+
+ private TaskbarOverlayProxyView() {
+ super(mTaskbarContext, null);
+ }
+
+ private void show() {
+ mIsOpen = true;
+ mTaskbarContext.getDragLayer().addView(this);
+ }
+
+ @Override
+ protected void handleClose(boolean animate) {
+ mTaskbarContext.getDragLayer().removeView(this);
+ Optional.ofNullable(mOverlayContext).ifPresent(c -> closeAllOpenViews(c, animate));
+ }
+
+ @Override
+ protected boolean isOfType(int type) {
+ return (type & TYPE_TASKBAR_OVERLAY_PROXY) != 0;
+ }
+
+ @Override
+ public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+ return false;
+ }
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java
new file mode 100644
index 0000000..044afd6
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.taskbar.overlay;
+
+import static android.view.KeyEvent.ACTION_UP;
+import static android.view.KeyEvent.KEYCODE_BACK;
+import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
+
+import android.content.Context;
+import android.graphics.Insets;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.WindowInsets;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.shared.TestProtocol;
+import com.android.launcher3.util.TouchController;
+import com.android.launcher3.views.BaseDragLayer;
+
+/** Root drag layer for the Taskbar overlay window. */
+public class TaskbarOverlayDragLayer extends
+ BaseDragLayer<TaskbarOverlayContext> implements
+ ViewTreeObserver.OnComputeInternalInsetsListener {
+
+ TaskbarOverlayDragLayer(Context context) {
+ super(context, null, 1);
+ setClipChildren(false);
+ recreateControllers();
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ getViewTreeObserver().addOnComputeInternalInsetsListener(this);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
+ }
+
+ @Override
+ public void recreateControllers() {
+ mControllers = new TouchController[]{mActivity.getDragController()};
+ }
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+ TestLogging.recordMotionEvent(TestProtocol.SEQUENCE_MAIN, "Touch event", ev);
+ return super.dispatchTouchEvent(ev);
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ if (event.getAction() == ACTION_UP && event.getKeyCode() == KEYCODE_BACK) {
+ AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mActivity);
+ if (topView != null && topView.onBackPressed()) {
+ return true;
+ }
+ }
+ return super.dispatchKeyEvent(event);
+ }
+
+ @Override
+ public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) {
+ if (mActivity.getDragController().isSystemDragInProgress()) {
+ inoutInfo.touchableRegion.setEmpty();
+ inoutInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
+ }
+ }
+
+ @Override
+ public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+ return updateInsetsDueToStashing(insets);
+ }
+
+ @Override
+ public void onViewRemoved(View child) {
+ super.onViewRemoved(child);
+ mActivity.getOverlayController().maybeCloseWindow();
+ }
+
+ /**
+ * Taskbar automatically stashes when opening all apps, but we don't report the insets as
+ * changing to avoid moving the underlying app. But internally, the apps view should still
+ * layout according to the stashed insets rather than the unstashed insets. So this method
+ * does two things:
+ * 1) Sets navigationBars bottom inset to stashedHeight.
+ * 2) Sets tappableInsets bottom inset to 0.
+ */
+ private WindowInsets updateInsetsDueToStashing(WindowInsets oldInsets) {
+ if (!mActivity.willTaskbarBeVisuallyStashed()) {
+ return oldInsets;
+ }
+ WindowInsets.Builder updatedInsetsBuilder = new WindowInsets.Builder(oldInsets);
+
+ Insets oldNavInsets = oldInsets.getInsets(WindowInsets.Type.navigationBars());
+ Insets newNavInsets = Insets.of(oldNavInsets.left, oldNavInsets.top, oldNavInsets.right,
+ mActivity.getStashedTaskbarHeight());
+ updatedInsetsBuilder.setInsets(WindowInsets.Type.navigationBars(), newNavInsets);
+
+ Insets oldTappableInsets = oldInsets.getInsets(WindowInsets.Type.tappableElement());
+ Insets newTappableInsets = Insets.of(oldTappableInsets.left, oldTappableInsets.top,
+ oldTappableInsets.right, 0);
+ updatedInsetsBuilder.setInsets(WindowInsets.Type.tappableElement(), newTappableInsets);
+
+ return updatedInsetsBuilder.build();
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
index f450496..379a6cd 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
@@ -17,10 +17,18 @@
package com.android.launcher3.uioverrides;
import android.app.Person;
+import android.appwidget.AppWidgetHost;
import android.content.pm.ShortcutInfo;
-import com.android.launcher3.Utilities;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.widget.LauncherWidgetHolder;
+
+/**
+ * A wrapper for the hidden API calls
+ */
public class ApiWrapper {
public static final boolean TASKBAR_DRAWN_IN_PROCESS = true;
@@ -29,4 +37,14 @@
Person[] persons = si.getPersons();
return persons == null ? Utilities.EMPTY_PERSON_ARRAY : persons;
}
+
+ /**
+ * Set the interaction handler for the host
+ * @param host AppWidgetHost that needs the interaction handler
+ * @param handler InteractionHandler for the views in the host
+ */
+ public static void setHostInteractionHandler(@NonNull AppWidgetHost host,
+ @Nullable LauncherWidgetHolder.LauncherWidgetInteractionHandler handler) {
+ host.setInteractionHandler(handler::onInteraction);
+ }
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
index ea0972f..8fb7030 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
@@ -28,21 +28,20 @@
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y;
import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
+import static com.android.quickstep.views.FloatingTaskView.PRIMARY_TRANSLATE_OFFSCREEN;
import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
-import static com.android.quickstep.views.RecentsView.FIRST_FLOATING_TASK_TRANSLATE_OFFSCREEN;
-import static com.android.quickstep.views.RecentsView.OVERVIEW_PROGRESS;
import static com.android.quickstep.views.RecentsView.RECENTS_GRID_PROGRESS;
import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
-import static com.android.quickstep.views.RecentsView.SPLIT_INSTRUCTIONS_FADE;
import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION;
+import static com.android.quickstep.views.RecentsView.TASK_THUMBNAIL_SPLASH_ALPHA;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.FloatProperty;
+import android.view.animation.Interpolator;
import androidx.annotation.NonNull;
-import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.PendingAnimation;
@@ -61,9 +60,9 @@
public abstract class BaseRecentsViewStateController<T extends RecentsView>
implements StateHandler<LauncherState> {
protected final T mRecentsView;
- protected final BaseQuickstepLauncher mLauncher;
+ protected final QuickstepLauncher mLauncher;
- public BaseRecentsViewStateController(@NonNull BaseQuickstepLauncher launcher) {
+ public BaseRecentsViewStateController(@NonNull QuickstepLauncher launcher) {
mLauncher = launcher;
mRecentsView = launcher.getOverviewPanel();
}
@@ -79,7 +78,7 @@
getTaskModalnessProperty().set(mRecentsView, state.getOverviewModalness());
RECENTS_GRID_PROGRESS.set(mRecentsView,
state.displayOverviewTasksAsGrid(mLauncher.getDeviceProfile()) ? 1f : 0f);
- OVERVIEW_PROGRESS.set(mRecentsView, state == LauncherState.OVERVIEW ? 1f : 0f);
+ TASK_THUMBNAIL_SPLASH_ALPHA.set(mRecentsView, state.showTaskThumbnailSplash() ? 1f : 0f);
}
@Override
@@ -89,6 +88,11 @@
return;
}
setStateWithAnimationInternal(toState, config, builder);
+ builder.addEndListener(success -> {
+ if (!success) {
+ mRecentsView.reset();
+ }
+ });
}
/**
@@ -112,6 +116,7 @@
// TODO (b/238651489): Refactor state management to avoid need for double check
FloatingTaskView floatingTask = mRecentsView.getFirstFloatingTaskView();
if (floatingTask != null) {
+ // We are in split selection state currently, transitioning to another state
DragLayer dragLayer = mLauncher.getDragLayer();
RectF onScreenRectF = new RectF();
Utilities.getBoundsForViewInDragLayer(mLauncher.getDragLayer(), floatingTask,
@@ -127,8 +132,8 @@
);
setter.setFloat(
- mRecentsView,
- FIRST_FLOATING_TASK_TRANSLATE_OFFSCREEN,
+ mRecentsView.getFirstFloatingTaskView(),
+ PRIMARY_TRANSLATE_OFFSCREEN,
mRecentsView.getPagedOrientationHandler()
.getFloatingTaskOffscreenTranslationTarget(
floatingTask,
@@ -140,14 +145,14 @@
ANIM_OVERVIEW_SPLIT_SELECT_FLOATING_TASK_TRANSLATE_OFFSCREEN,
LINEAR
));
- setter.setFloat(
- mRecentsView,
- SPLIT_INSTRUCTIONS_FADE,
- 1,
+ setter.setViewAlpha(
+ mRecentsView.getSplitInstructionsView(),
+ 0,
config.getInterpolator(
ANIM_OVERVIEW_SPLIT_SELECT_INSTRUCTIONS_FADE,
LINEAR
- ));
+ )
+ );
}
}
@@ -158,13 +163,19 @@
mRecentsView, getTaskModalnessProperty(),
toState.getOverviewModalness(),
config.getInterpolator(ANIM_OVERVIEW_MODAL, LINEAR));
- boolean showAsGrid = toState.displayOverviewTasksAsGrid(mLauncher.getDeviceProfile());
- setter.setFloat(mRecentsView, RECENTS_GRID_PROGRESS, showAsGrid ? 1f : 0f,
- showAsGrid ? INSTANT : FINAL_FRAME);
- boolean toOverview = toState == LauncherState.OVERVIEW;
- setter.setFloat(mRecentsView, OVERVIEW_PROGRESS, toOverview ? 1f : 0f,
- toOverview ? INSTANT : FINAL_FRAME);
+ LauncherState fromState = mLauncher.getStateManager().getState();
+ setter.setFloat(mRecentsView, TASK_THUMBNAIL_SPLASH_ALPHA,
+ toState.showTaskThumbnailSplash() ? 1f : 0f,
+ !toState.showTaskThumbnailSplash() && fromState == LauncherState.QUICK_SWITCH
+ ? LINEAR : INSTANT);
+
+ boolean showAsGrid = toState.displayOverviewTasksAsGrid(mLauncher.getDeviceProfile());
+ Interpolator gridProgressInterpolator = showAsGrid
+ ? fromState == LauncherState.QUICK_SWITCH ? LINEAR : INSTANT
+ : FINAL_FRAME;
+ setter.setFloat(mRecentsView, RECENTS_GRID_PROGRESS, showAsGrid ? 1f : 0f,
+ gridProgressInterpolator);
}
abstract FloatProperty getTaskModalnessProperty();
diff --git a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
index 5f3a990..bf0f8f7 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
@@ -49,6 +49,7 @@
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimatorListeners;
+import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.GraphicsUtils;
import com.android.launcher3.icons.IconNormalizer;
@@ -271,7 +272,7 @@
mIsPinned = true;
applyFromWorkspaceItem(info);
setOnLongClickListener(ItemLongClickListener.INSTANCE_WORKSPACE);
- ((CellLayout.LayoutParams) getLayoutParams()).canReorder = true;
+ ((CellLayoutLayoutParams) getLayoutParams()).canReorder = true;
invalidate();
}
@@ -280,7 +281,7 @@
*/
public void finishBinding(OnLongClickListener longClickListener) {
setOnLongClickListener(longClickListener);
- ((CellLayout.LayoutParams) getLayoutParams()).canReorder = false;
+ ((CellLayoutLayoutParams) getLayoutParams()).canReorder = false;
setTextVisibility(false);
verifyHighRes();
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
index 08d147f..353d817 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
@@ -34,9 +34,11 @@
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.util.ActivityOptionsWrapper;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
+import com.android.launcher3.widget.LauncherWidgetHolder;
/** Provides a Quickstep specific animation when launching an activity from an app widget. */
-class QuickstepInteractionHandler implements RemoteViews.InteractionHandler {
+class QuickstepInteractionHandler
+ implements LauncherWidgetHolder.LauncherWidgetInteractionHandler {
private static final String TAG = "QuickstepInteractionHandler";
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 21f03be..36d9686 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,41 +23,88 @@
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_QUICKSTEP_WIDGET_APP_START;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_WIDGET_PICKER_DEPTH;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
+import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID;
+import static com.android.launcher3.popup.QuickstepSystemShortcut.getSplitSelectShortcutByPosition;
+import static com.android.launcher3.taskbar.LauncherTaskbarUIController.ALL_APPS_PAGE_PROGRESS_INDEX;
+import static com.android.launcher3.taskbar.LauncherTaskbarUIController.MINUS_ONE_PAGE_PROGRESS_INDEX;
+import static com.android.launcher3.taskbar.LauncherTaskbarUIController.WIDGETS_PAGE_PROGRESS_INDEX;
import static com.android.launcher3.testing.shared.TestProtocol.HINT_STATE_ORDINAL;
import static com.android.launcher3.testing.shared.TestProtocol.HINT_STATE_TWO_BUTTON_ORDINAL;
import static com.android.launcher3.testing.shared.TestProtocol.OVERVIEW_STATE_ORDINAL;
import static com.android.launcher3.testing.shared.TestProtocol.QUICK_SWITCH_STATE_ORDINAL;
+import static com.android.launcher3.util.DisplayController.CHANGE_ACTIVE_SCREEN;
+import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
+import static com.android.launcher3.util.Executors.THREAD_POOL_EXECUTOR;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY;
+import android.animation.AnimatorSet;
+import android.animation.ValueAnimator;
+import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.content.Context;
import android.content.Intent;
+import android.content.IntentSender;
import android.content.SharedPreferences;
import android.content.res.Configuration;
+import android.hardware.SensorManager;
+import android.hardware.devicestate.DeviceStateManager;
+import android.media.permission.SafeCloseable;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.IBinder;
+import android.os.SystemProperties;
+import android.view.Display;
import android.view.HapticFeedbackConstants;
+import android.view.RemoteAnimationTarget;
import android.view.View;
+import android.view.WindowManagerGlobal;
+import android.window.SplashScreen;
-import com.android.launcher3.BaseQuickstepLauncher;
+import androidx.annotation.BinderThread;
+import androidx.annotation.Nullable;
+
+import com.android.app.viewcapture.ViewCapture;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.LauncherState;
import com.android.launcher3.QuickstepAccessibilityDelegate;
+import com.android.launcher3.QuickstepTransitionManager;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.appprediction.PredictionRowView;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.hybridhotseat.HotseatPredictionController;
import com.android.launcher3.logging.InstanceId;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.logging.StatsLogManager.StatsLogger;
import com.android.launcher3.model.BgDataModel.FixedContainerItems;
+import com.android.launcher3.model.WellbeingModel;
import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.popup.SystemShortcut;
+import com.android.launcher3.proxy.ProxyActivityStarter;
+import com.android.launcher3.proxy.StartActivityParams;
+import com.android.launcher3.statehandlers.DepthController;
+import com.android.launcher3.statehandlers.DesktopVisibilityController;
import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory;
+import com.android.launcher3.statemanager.StateManager.StateHandler;
+import com.android.launcher3.taskbar.LauncherTaskbarUIController;
+import com.android.launcher3.taskbar.TaskbarManager;
import com.android.launcher3.uioverrides.states.QuickstepAtomicAnimationFactory;
import com.android.launcher3.uioverrides.touchcontrollers.NavBarToHomeTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.NoButtonNavbarToOverviewTouchController;
@@ -68,42 +115,119 @@
import com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.TransposedQuickSwitchTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.TwoButtonNavbarTouchController;
+import com.android.launcher3.util.ActivityOptionsWrapper;
import com.android.launcher3.util.DisplayController;
-import com.android.launcher3.util.DisplayController.NavigationMode;
+import com.android.launcher3.util.IntSet;
+import com.android.launcher3.util.NavigationMode;
+import com.android.launcher3.util.ObjectWrapper;
import com.android.launcher3.util.PendingRequestArgs;
+import com.android.launcher3.util.PendingSplitSelectInfo;
+import com.android.launcher3.util.RunnableList;
+import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
import com.android.launcher3.util.TouchController;
-import com.android.launcher3.util.UiThreadHelper;
-import com.android.launcher3.util.UiThreadHelper.AsyncCommand;
-import com.android.launcher3.widget.LauncherAppWidgetHost;
+import com.android.launcher3.widget.LauncherWidgetHolder;
+import com.android.quickstep.OverviewCommandHelper;
+import com.android.quickstep.RecentsModel;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TaskUtils;
+import com.android.quickstep.TouchInteractionService.TISBinder;
+import com.android.quickstep.util.LauncherUnfoldAnimationController;
+import com.android.quickstep.util.ProxyScreenStatusProvider;
import com.android.quickstep.util.QuickstepOnboardingPrefs;
+import com.android.quickstep.util.RemoteAnimationProvider;
+import com.android.quickstep.util.RemoteFadeOutAnimationListener;
+import com.android.quickstep.util.SplitSelectStateController;
+import com.android.quickstep.util.SplitToWorkspaceController;
+import com.android.quickstep.util.SplitWithKeyboardShortcutController;
+import com.android.quickstep.util.TISBindHelper;
+import com.android.quickstep.views.OverviewActionsView;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.unfold.UnfoldSharedComponent;
+import com.android.systemui.unfold.UnfoldTransitionFactory;
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
+import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig;
+import com.android.systemui.unfold.config.UnfoldTransitionConfig;
+import com.android.systemui.unfold.system.ActivityManagerActivityTypeProvider;
+import com.android.systemui.unfold.system.DeviceStateManagerFoldProvider;
+import com.android.systemui.unfold.updates.RotationChangeProvider;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.List;
import java.util.Objects;
import java.util.function.Predicate;
import java.util.stream.Stream;
-public class QuickstepLauncher extends BaseQuickstepLauncher {
+public class QuickstepLauncher extends Launcher {
+
+ public static final boolean ENABLE_PIP_KEEP_CLEAR_ALGORITHM =
+ SystemProperties.getBoolean("persist.wm.debug.enable_pip_keep_clear_algorithm", false);
public static final boolean GO_LOW_RAM_RECENTS_ENABLED = false;
- /**
- * Reusable command for applying the shelf height on the background thread.
- */
- public static final AsyncCommand SET_SHELF_HEIGHT = (context, arg1, arg2) ->
- SystemUiProxy.INSTANCE.get(context).setShelfHeight(arg1 != 0, arg2);
private FixedContainerItems mAllAppsPredictions;
private HotseatPredictionController mHotseatPredictionController;
+ private DepthController mDepthController;
+ private DesktopVisibilityController mDesktopVisibilityController;
+ private QuickstepTransitionManager mAppTransitionManager;
+ private OverviewActionsView mActionsView;
+ private TISBindHelper mTISBindHelper;
+ private @Nullable TaskbarManager mTaskbarManager;
+ private @Nullable OverviewCommandHelper mOverviewCommandHelper;
+ private @Nullable LauncherTaskbarUIController mTaskbarUIController;
+ // Will be updated when dragging from taskbar.
+ private @Nullable DragOptions mNextWorkspaceDragOptions = null;
+ private @Nullable UnfoldTransitionProgressProvider mUnfoldTransitionProgressProvider;
+ private @Nullable RotationChangeProvider mRotationChangeProvider;
+ private @Nullable LauncherUnfoldAnimationController mLauncherUnfoldAnimationController;
+
+ private SplitSelectStateController mSplitSelectStateController;
+ private SplitWithKeyboardShortcutController mSplitWithKeyboardShortcutController;
+ private SplitToWorkspaceController mSplitToWorkspaceController;
+
+ /**
+ * If Launcher restarted while in the middle of an Overview split select, it needs this data to
+ * recover. In all other cases this will remain null.
+ */
+ private PendingSplitSelectInfo mPendingSplitSelectInfo = null;
+
+ private SafeCloseable mViewCapture;
+
+ private boolean mEnableWidgetDepth;
@Override
protected void setupViews() {
super.setupViews();
+
+ mActionsView = findViewById(R.id.overview_actions_view);
+ RecentsView overviewPanel = getOverviewPanel();
+ mSplitSelectStateController =
+ new SplitSelectStateController(this, mHandler, getStateManager(),
+ getDepthController(), getStatsLogManager());
+ overviewPanel.init(mActionsView, mSplitSelectStateController);
+ mSplitWithKeyboardShortcutController = new SplitWithKeyboardShortcutController(this,
+ mSplitSelectStateController);
+ mSplitToWorkspaceController = new SplitToWorkspaceController(this,
+ mSplitSelectStateController);
+ mActionsView.updateDimension(getDeviceProfile(), overviewPanel.getLastComputedTaskSize());
+ mActionsView.updateVerticalMargin(DisplayController.getNavigationMode(this));
+
+ mAppTransitionManager = new QuickstepTransitionManager(this);
+ mAppTransitionManager.registerRemoteAnimations();
+ mAppTransitionManager.registerRemoteTransitions();
+
+ mTISBindHelper = new TISBindHelper(this, this::onTISConnected);
+ mDepthController = new DepthController(this);
+ mDesktopVisibilityController = new DesktopVisibilityController(this);
mHotseatPredictionController = new HotseatPredictionController(this);
+
+ mEnableWidgetDepth = ENABLE_WIDGET_PICKER_DEPTH.get()
+ && SystemProperties.getBoolean("ro.launcher.depth.widget", true);
+ getWorkspace().addOverlayCallback(progress ->
+ onTaskbarInAppDisplayProgressUpdate(progress, MINUS_ONE_PAGE_PROGRESS_INDEX));
}
@Override
@@ -184,6 +308,16 @@
@Override
protected void onActivityFlagsChanged(int changeBits) {
+ if ((changeBits & ACTIVITY_STATE_STARTED) != 0) {
+ mDepthController.setActivityStarted(isStarted());
+ }
+
+ if ((changeBits & ACTIVITY_STATE_RESUMED) != 0) {
+ if (mTaskbarUIController != null) {
+ mTaskbarUIController.onLauncherResumedOrPaused(hasBeenResumed());
+ }
+ }
+
super.onActivityFlagsChanged(changeBits);
if ((changeBits & (ACTIVITY_STATE_DEFERRED_RESUMED | ACTIVITY_STATE_STARTED
| ACTIVITY_STATE_USER_ACTIVE | ACTIVITY_STATE_TRANSITION_ACTIVE)) != 0) {
@@ -202,10 +336,35 @@
super.showAllAppsFromIntent(alreadyOnHome);
}
+ protected void onItemClicked(View view) {
+ if (!mSplitToWorkspaceController.handleSecondAppSelectionForSplit(view)) {
+ QuickstepLauncher.super.getItemOnClickListener().onClick(view);
+ }
+ }
+
+ @Override
+ public View.OnClickListener getItemOnClickListener() {
+ return this::onItemClicked;
+ }
+
@Override
public Stream<SystemShortcut.Factory> getSupportedShortcuts() {
+ Stream<SystemShortcut.Factory> base = Stream.of(WellbeingModel.SHORTCUT_FACTORY);
+ if (ENABLE_SPLIT_FROM_WORKSPACE.get() && mDeviceProfile.isTablet) {
+ RecentsView recentsView = getOverviewPanel();
+ // TODO: Pull it out of PagedOrentationHandler for split from workspace.
+ List<SplitPositionOption> positions =
+ recentsView.getPagedOrientationHandler().getSplitPositionOptions(
+ mDeviceProfile);
+ List<SystemShortcut.Factory<QuickstepLauncher>> splitShortcuts = new ArrayList<>();
+ for (SplitPositionOption position : positions) {
+ splitShortcuts.add(getSplitSelectShortcutByPosition(position));
+ }
+ base = Stream.concat(base, splitShortcuts.stream());
+ }
return Stream.concat(
- Stream.of(mHotseatPredictionController), super.getSupportedShortcuts());
+ Stream.of(mHotseatPredictionController),
+ Stream.concat(base, super.getSupportedShortcuts()));
}
/**
@@ -213,16 +372,17 @@
*/
private void onStateOrResumeChanging(boolean inTransition) {
LauncherState state = getStateManager().getState();
- boolean started = ((getActivityFlags() & ACTIVITY_STATE_STARTED)) != 0;
- if (started) {
- DeviceProfile profile = getDeviceProfile();
- boolean willUserBeActive =
- (getActivityFlags() & ACTIVITY_STATE_USER_WILL_BE_ACTIVE) != 0;
- boolean visible = (state == NORMAL || state == OVERVIEW)
- && (willUserBeActive || isUserActive())
- && !profile.isVerticalBarLayout();
- UiThreadHelper.runAsyncCommand(this, SET_SHELF_HEIGHT, visible ? 1 : 0,
- profile.hotseatBarSizePx);
+ if (!ENABLE_PIP_KEEP_CLEAR_ALGORITHM) {
+ boolean started = ((getActivityFlags() & ACTIVITY_STATE_STARTED)) != 0;
+ if (started) {
+ DeviceProfile profile = getDeviceProfile();
+ boolean willUserBeActive =
+ (getActivityFlags() & ACTIVITY_STATE_USER_WILL_BE_ACTIVE) != 0;
+ boolean visible = (state == NORMAL || state == OVERVIEW)
+ && (willUserBeActive || isUserActive())
+ && !profile.isVerticalBarLayout();
+ SystemUiProxy.INSTANCE.get(this).setShelfHeight(visible, profile.hotseatBarSizePx);
+ }
}
if (state == NORMAL && !inTransition) {
((RecentsView) getOverviewPanel()).setSwipeDownShouldLaunchApp(false);
@@ -252,13 +412,30 @@
@Override
public void onDestroy() {
+ mAppTransitionManager.onActivityDestroyed();
+ if (mUnfoldTransitionProgressProvider != null) {
+ mUnfoldTransitionProgressProvider.destroy();
+ }
+
+ mTISBindHelper.onDestroy();
+ if (mTaskbarManager != null) {
+ mTaskbarManager.clearActivity(this);
+ }
+
+ if (mLauncherUnfoldAnimationController != null) {
+ mLauncherUnfoldAnimationController.onDestroy();
+ }
+
super.onDestroy();
mHotseatPredictionController.destroy();
+ mSplitWithKeyboardShortcutController.onDestroy();
+ if (mViewCapture != null) mViewCapture.close();
}
@Override
public void onStateSetEnd(LauncherState state) {
super.onStateSetEnd(state);
+ handlePendingActivityRequest();
switch (state.ordinal) {
case HINT_STATE_ORDINAL: {
@@ -337,12 +514,468 @@
return new QuickstepAtomicAnimationFactory(this);
}
- protected LauncherAppWidgetHost createAppWidgetHost() {
- LauncherAppWidgetHost appWidgetHost = super.createAppWidgetHost();
- if (ENABLE_QUICKSTEP_WIDGET_APP_START.get()) {
- appWidgetHost.setInteractionHandler(new QuickstepInteractionHandler(this));
+ @Override
+ protected LauncherWidgetHolder createAppWidgetHolder() {
+ LauncherWidgetHolder appWidgetHolder = super.createAppWidgetHolder();
+ appWidgetHolder.setInteractionHandler(new QuickstepInteractionHandler(this));
+ return appWidgetHolder;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (savedInstanceState != null) {
+ mPendingSplitSelectInfo = ObjectWrapper.unwrap(
+ savedInstanceState.getIBinder(PENDING_SPLIT_SELECT_INFO));
}
- return appWidgetHost;
+ addMultiWindowModeChangedListener(mDepthController);
+ initUnfoldTransitionProgressProvider();
+ if (FeatureFlags.CONTINUOUS_VIEW_TREE_CAPTURE.get()) {
+ mViewCapture = ViewCapture.getInstance().startCapture(getWindow());
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ if (mLauncherUnfoldAnimationController != null) {
+ mLauncherUnfoldAnimationController.onResume();
+ }
+ }
+
+ @Override
+ protected void onPause() {
+ if (mLauncherUnfoldAnimationController != null) {
+ mLauncherUnfoldAnimationController.onPause();
+ }
+
+ super.onPause();
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+
+ if (mOverviewCommandHelper != null) {
+ mOverviewCommandHelper.clearPendingCommands();
+ }
+ }
+
+ public QuickstepTransitionManager getAppTransitionManager() {
+ return mAppTransitionManager;
+ }
+
+ @Override
+ public void onEnterAnimationComplete() {
+ super.onEnterAnimationComplete();
+ // After the transition to home, enable the high-res thumbnail loader if it wasn't enabled
+ // as a part of quickstep, so that high-res thumbnails can load the next time we enter
+ // overview
+ RecentsModel.INSTANCE.get(this).getThumbnailCache()
+ .getHighResLoadingState().setVisible(true);
+ }
+
+ @Override
+ protected void handleGestureContract(Intent intent) {
+ if (FeatureFlags.SEPARATE_RECENTS_ACTIVITY.get()) {
+ super.handleGestureContract(intent);
+ }
+ }
+
+ @Override
+ public void onTrimMemory(int level) {
+ super.onTrimMemory(level);
+ RecentsModel.INSTANCE.get(this).onTrimMemory(level);
+ }
+
+ @Override
+ public void onUiChangedWhileSleeping() {
+ // Remove the snapshot because the content view may have obvious changes.
+ UI_HELPER_EXECUTOR.execute(
+ () -> ActivityManagerWrapper.getInstance().invalidateHomeTaskSnapshot(this));
+ }
+
+ @Override
+ protected void onScreenOff() {
+ super.onScreenOff();
+ RecentsView recentsView = getOverviewPanel();
+ recentsView.finishRecentsAnimation(true /* toRecents */, null);
+ }
+
+ @Override
+ public void onAllAppsTransition(float progress) {
+ super.onAllAppsTransition(progress);
+ onTaskbarInAppDisplayProgressUpdate(progress, ALL_APPS_PAGE_PROGRESS_INDEX);
+ }
+
+ @Override
+ public void onWidgetsTransition(float progress) {
+ super.onWidgetsTransition(progress);
+ onTaskbarInAppDisplayProgressUpdate(progress, WIDGETS_PAGE_PROGRESS_INDEX);
+ if (mEnableWidgetDepth) {
+ getDepthController().widgetDepth.setValue(Utilities.mapToRange(
+ progress, 0f, 1f, 0f, getDeviceProfile().bottomSheetDepth, EMPHASIZED));
+ }
+ }
+
+ private void onTaskbarInAppDisplayProgressUpdate(float progress, int flag) {
+ if (mTaskbarManager == null
+ || mTaskbarManager.getCurrentActivityContext() == null
+ || mTaskbarUIController == null) {
+ return;
+ }
+ mTaskbarUIController.onTaskbarInAppDisplayProgressUpdate(progress, flag);
+ }
+
+ @Override
+ public void startIntentSenderForResult(IntentSender intent, int requestCode,
+ Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags, Bundle options) {
+ if (requestCode != -1) {
+ mPendingActivityRequestCode = requestCode;
+ StartActivityParams params = new StartActivityParams(this, requestCode);
+ params.intentSender = intent;
+ params.fillInIntent = fillInIntent;
+ params.flagsMask = flagsMask;
+ params.flagsValues = flagsValues;
+ params.extraFlags = extraFlags;
+ params.options = options;
+ startActivity(ProxyActivityStarter.getLaunchIntent(this, params));
+ } else {
+ super.startIntentSenderForResult(intent, requestCode, fillInIntent, flagsMask,
+ flagsValues, extraFlags, options);
+ }
+ }
+
+ @Override
+ public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
+ if (requestCode != -1) {
+ mPendingActivityRequestCode = requestCode;
+ StartActivityParams params = new StartActivityParams(this, requestCode);
+ params.intent = intent;
+ params.options = options;
+ startActivity(ProxyActivityStarter.getLaunchIntent(this, params));
+ } else {
+ super.startActivityForResult(intent, requestCode, options);
+ }
+ }
+
+ @Override
+ protected void onDeferredResumed() {
+ super.onDeferredResumed();
+ handlePendingActivityRequest();
+ }
+
+ private void handlePendingActivityRequest() {
+ if (mPendingActivityRequestCode != -1 && isInState(NORMAL)
+ && ((getActivityFlags() & ACTIVITY_STATE_DEFERRED_RESUMED) != 0)) {
+ // Remove any active ProxyActivityStarter task and send RESULT_CANCELED to Launcher.
+ onActivityResult(mPendingActivityRequestCode, RESULT_CANCELED, null);
+ // ProxyActivityStarter is started with clear task to reset the task after which it
+ // removes the task itself.
+ startActivity(ProxyActivityStarter.getLaunchIntent(this, null));
+ }
+ }
+
+ private void onTISConnected(TISBinder binder) {
+ mTaskbarManager = binder.getTaskbarManager();
+ mTaskbarManager.setActivity(this);
+ mOverviewCommandHelper = binder.getOverviewCommandHelper();
+ }
+
+ @Override
+ public void runOnBindToTouchInteractionService(Runnable r) {
+ mTISBindHelper.runOnBindToTouchInteractionService(r);
+ }
+
+ private void initUnfoldTransitionProgressProvider() {
+ final UnfoldTransitionConfig config = new ResourceUnfoldTransitionConfig();
+ if (config.isEnabled()) {
+ UnfoldSharedComponent unfoldComponent =
+ UnfoldTransitionFactory.createUnfoldSharedComponent(
+ /* context= */ this,
+ config,
+ ProxyScreenStatusProvider.INSTANCE,
+ new DeviceStateManagerFoldProvider(
+ getSystemService(DeviceStateManager.class), /* context */this),
+ new ActivityManagerActivityTypeProvider(
+ getSystemService(ActivityManager.class)),
+ getSystemService(SensorManager.class),
+ getMainThreadHandler(),
+ getMainExecutor(),
+ /* backgroundExecutor= */ THREAD_POOL_EXECUTOR,
+ /* tracingTagPrefix= */ "launcher",
+ WindowManagerGlobal.getWindowManagerService()
+ );
+
+ mUnfoldTransitionProgressProvider = unfoldComponent.getUnfoldTransitionProvider()
+ .orElseThrow(() -> new IllegalStateException(
+ "Trying to create UnfoldTransitionProgressProvider when the "
+ + "transition is disabled"));
+
+ mRotationChangeProvider = unfoldComponent.getRotationChangeProvider();
+ mLauncherUnfoldAnimationController = new LauncherUnfoldAnimationController(
+ /* launcher= */ this,
+ getWindowManager(),
+ mUnfoldTransitionProgressProvider,
+ mRotationChangeProvider
+ );
+ }
+ }
+
+ public void setTaskbarUIController(LauncherTaskbarUIController taskbarUIController) {
+ mTaskbarUIController = taskbarUIController;
+ }
+
+ public @Nullable LauncherTaskbarUIController getTaskbarUIController() {
+ return mTaskbarUIController;
+ }
+
+ public SplitSelectStateController getSplitSelectStateController() {
+ return mSplitSelectStateController;
+ }
+
+ public <T extends OverviewActionsView> T getActionsView() {
+ return (T) mActionsView;
+ }
+
+ @Override
+ protected void closeOpenViews(boolean animate) {
+ super.closeOpenViews(animate);
+ TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY);
+ }
+
+ @Override
+ protected void collectStateHandlers(List<StateHandler> out) {
+ super.collectStateHandlers(out);
+ out.add(getDepthController());
+ out.add(new RecentsViewStateController(this));
+ }
+
+ public DepthController getDepthController() {
+ return mDepthController;
+ }
+
+ public DesktopVisibilityController getDesktopVisibilityController() {
+ return mDesktopVisibilityController;
+ }
+
+ @Nullable
+ public UnfoldTransitionProgressProvider getUnfoldTransitionProgressProvider() {
+ return mUnfoldTransitionProgressProvider;
+ }
+
+ @Override
+ public boolean supportsAdaptiveIconAnimation(View clickedView) {
+ return mAppTransitionManager.hasControlRemoteAppTransitionPermission();
+ }
+
+ @Override
+ public DragOptions getDefaultWorkspaceDragOptions() {
+ if (mNextWorkspaceDragOptions != null) {
+ DragOptions options = mNextWorkspaceDragOptions;
+ mNextWorkspaceDragOptions = null;
+ return options;
+ }
+ return super.getDefaultWorkspaceDragOptions();
+ }
+
+ public void setNextWorkspaceDragOptions(DragOptions dragOptions) {
+ mNextWorkspaceDragOptions = dragOptions;
+ }
+
+ @Override
+ public void useFadeOutAnimationForLauncherStart(CancellationSignal signal) {
+ QuickstepTransitionManager appTransitionManager = getAppTransitionManager();
+ appTransitionManager.setRemoteAnimationProvider(new RemoteAnimationProvider() {
+ @Override
+ public AnimatorSet createWindowAnimation(RemoteAnimationTarget[] appTargets,
+ RemoteAnimationTarget[] wallpaperTargets) {
+
+ // On the first call clear the reference.
+ signal.cancel();
+
+ ValueAnimator fadeAnimation = ValueAnimator.ofFloat(1, 0);
+ fadeAnimation.addUpdateListener(new RemoteFadeOutAnimationListener(appTargets,
+ wallpaperTargets));
+ AnimatorSet anim = new AnimatorSet();
+ anim.play(fadeAnimation);
+ return anim;
+ }
+ }, signal);
+ }
+
+ @Override
+ public float[] getNormalOverviewScaleAndOffset() {
+ return DisplayController.getNavigationMode(this).hasGestures
+ ? new float[] {1, 1} : new float[] {1.1f, NO_OFFSET};
+ }
+
+ @Override
+ public void finishBindingItems(IntSet pagesBoundFirst) {
+ super.finishBindingItems(pagesBoundFirst);
+ // Instantiate and initialize WellbeingModel now that its loading won't interfere with
+ // populating workspace.
+ // TODO: Find a better place for this
+ WellbeingModel.INSTANCE.get(this);
+ }
+
+ @Override
+ public void onInitialBindComplete(IntSet boundPages, RunnableList pendingTasks) {
+ pendingTasks.add(() -> {
+ // This is added in pending task as we need to wait for views to be positioned
+ // correctly before registering them for the animation.
+ if (mLauncherUnfoldAnimationController != null) {
+ // This is needed in case items are rebound while the unfold animation is in
+ // progress.
+ mLauncherUnfoldAnimationController.updateRegisteredViewsIfNeeded();
+ }
+ });
+ super.onInitialBindComplete(boundPages, pendingTasks);
+ }
+
+ @Override
+ public ActivityOptionsWrapper getActivityLaunchOptions(View v, @Nullable ItemInfo item) {
+ ActivityOptionsWrapper activityOptions =
+ mAppTransitionManager.hasControlRemoteAppTransitionPermission()
+ ? mAppTransitionManager.getActivityLaunchOptions(v)
+ : super.getActivityLaunchOptions(v, item);
+ if (mLastTouchUpTime > 0) {
+ activityOptions.options.setSourceInfo(ActivityOptions.SourceInfo.TYPE_LAUNCHER,
+ mLastTouchUpTime);
+ }
+ activityOptions.options.setSplashScreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_ICON);
+ activityOptions.options.setLaunchDisplayId(
+ (v != null && v.getDisplay() != null) ? v.getDisplay().getDisplayId()
+ : Display.DEFAULT_DISPLAY);
+ addLaunchCookie(item, activityOptions.options);
+ return activityOptions;
+ }
+
+ @Override
+ @BinderThread
+ public void enterStageSplitFromRunningApp(boolean leftOrTop) {
+ mSplitWithKeyboardShortcutController.enterStageSplit(leftOrTop);
+ }
+
+ /**
+ * Adds a new launch cookie for the activity launch if supported.
+ *
+ * @param info the item info for the launch
+ * @param opts the options to set the launchCookie on.
+ */
+ public void addLaunchCookie(ItemInfo info, ActivityOptions opts) {
+ IBinder launchCookie = getLaunchCookie(info);
+ if (launchCookie != null) {
+ opts.setLaunchCookie(launchCookie);
+ }
+ }
+
+ /**
+ * Return a new launch cookie for the activity launch if supported.
+ *
+ * @param info the item info for the launch
+ */
+ public IBinder getLaunchCookie(ItemInfo info) {
+ if (info == null) {
+ return null;
+ }
+ switch (info.container) {
+ case Favorites.CONTAINER_DESKTOP:
+ case Favorites.CONTAINER_HOTSEAT:
+ // Fall through and continue it's on the workspace (we don't support swiping back
+ // to other containers like all apps or the hotseat predictions (which can change)
+ break;
+ default:
+ if (info.container >= 0) {
+ // Also allow swiping to folders
+ break;
+ }
+ // Reset any existing launch cookies associated with the cookie
+ return ObjectWrapper.wrap(NO_MATCHING_ID);
+ }
+ 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
+ break;
+ default:
+ // Reset any existing launch cookies associated with the cookie
+ return ObjectWrapper.wrap(NO_MATCHING_ID);
+ }
+ return ObjectWrapper.wrap(new Integer(info.id));
+ }
+
+ public void setHintUserWillBeActive() {
+ addActivityFlags(ACTIVITY_STATE_USER_WILL_BE_ACTIVE);
+ }
+
+ @Override
+ public void onDisplayInfoChanged(Context context, DisplayController.Info info, int flags) {
+ super.onDisplayInfoChanged(context, info, flags);
+ // When changing screens, force moving to rest state similar to StatefulActivity.onStop, as
+ // StatefulActivity isn't called consistently.
+ if ((flags & CHANGE_ACTIVE_SCREEN) != 0) {
+ getStateManager().moveToRestState();
+ }
+
+ if ((flags & CHANGE_NAVIGATION_MODE) != 0) {
+ getDragLayer().recreateControllers();
+ if (mActionsView != null) {
+ mActionsView.updateVerticalMargin(info.navigationMode);
+ }
+ }
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+
+ // If Launcher shuts downs during split select, we save some extra data in the recovery
+ // bundle to allow graceful recovery. The normal LauncherState restore mechanism doesn't
+ // work in this case because restoring straight to OverviewSplitSelect without staging data,
+ // or before the tasks themselves have loaded into Overview, causes a crash. So we tell
+ // Launcher to first restore into Overview state, wait for the relevant tasks and icons to
+ // load in, and then proceed to OverviewSplitSelect.
+ if (isInState(OVERVIEW_SPLIT_SELECT)) {
+ SplitSelectStateController splitSelectStateController =
+ ((RecentsView) getOverviewPanel()).getSplitSelectController();
+ // Launcher will restart in Overview and then transition to OverviewSplitSelect.
+ outState.putIBinder(PENDING_SPLIT_SELECT_INFO, ObjectWrapper.wrap(
+ new PendingSplitSelectInfo(
+ splitSelectStateController.getInitialTaskId(),
+ splitSelectStateController.getActiveSplitStagePosition(),
+ splitSelectStateController.getSplitEvent())
+ ));
+ outState.putInt(RUNTIME_STATE, OVERVIEW.ordinal);
+ }
+ }
+
+ /**
+ * When Launcher restarts, it sometimes needs to recover to a split selection state.
+ * This function checks if such a recovery is needed.
+ * @return a boolean representing whether the launcher is waiting to recover to
+ * OverviewSplitSelect state.
+ */
+ public boolean hasPendingSplitSelectInfo() {
+ return mPendingSplitSelectInfo != null;
+ }
+
+ /**
+ * See {@link #hasPendingSplitSelectInfo()}
+ */
+ public @Nullable PendingSplitSelectInfo getPendingSplitSelectInfo() {
+ return mPendingSplitSelectInfo;
+ }
+
+ /**
+ * When the launcher has successfully recovered to OverviewSplitSelect state, this function
+ * deletes the recovery data, returning it to a null state.
+ */
+ public void finishSplitSelectRecovery() {
+ mPendingSplitSelectInfo = null;
}
private static final class LauncherTaskViewController extends
@@ -371,6 +1004,9 @@
@Override
public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
super.dump(prefix, fd, writer, args);
+ if (mDepthController != null) {
+ mDepthController.dump(prefix, writer);
+ }
RecentsView recentsView = getOverviewPanel();
writer.println("\nQuickstepLauncher:");
writer.println(prefix + "\tmOrientationState: " + (recentsView == null ? "recentsNull" :
diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index 00a98c0..e8e8328 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -20,6 +20,7 @@
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;
import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
import static com.android.quickstep.views.RecentsView.TASK_MODALNESS;
@@ -27,22 +28,24 @@
import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_SPLIT_TRANSLATION;
import static com.android.quickstep.views.TaskView.FLAG_UPDATE_ALL;
+import android.animation.AnimatorSet;
import android.annotation.TargetApi;
import android.os.Build;
import android.util.FloatProperty;
+import android.util.Log;
import android.util.Pair;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.LauncherState;
+import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.PropertySetter;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.touch.PagedOrientationHandler;
-import com.android.launcher3.util.MultiValueAlpha;
+import com.android.quickstep.util.AnimUtils;
+import com.android.quickstep.util.SplitAnimationTimings;
import com.android.quickstep.views.ClearAllButton;
import com.android.quickstep.views.LauncherRecentsView;
import com.android.quickstep.views.RecentsView;
@@ -55,7 +58,7 @@
public final class RecentsViewStateController extends
BaseRecentsViewStateController<LauncherRecentsView> {
- public RecentsViewStateController(BaseQuickstepLauncher launcher) {
+ public RecentsViewStateController(QuickstepLauncher launcher) {
super(launcher);
}
@@ -72,7 +75,10 @@
// DepthController to prevent optimizations which might occlude the layers behind
mLauncher.getDepthController().setHasContentBehindLauncher(state.overviewUi);
- handleSplitSelectionState(state, null);
+ PendingAnimation builder =
+ new PendingAnimation(state.getTransitionDuration(mLauncher, true));
+
+ handleSplitSelectionState(state, builder, /* animate */false);
}
@Override
@@ -84,6 +90,13 @@
// While animating into recents, update the visible task data as needed
builder.addOnFrameCallback(() -> mRecentsView.loadVisibleTaskData(FLAG_UPDATE_ALL));
mRecentsView.updateEmptyMessage();
+ // TODO(b/246283207): Remove logging once root cause of flake detected.
+ if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+ Log.d("b/246283207", "RecentsView#setStateWithAnimationInternal getCurrentPage(): "
+ + mRecentsView.getCurrentPage()
+ + ", getScrollForPage(getCurrentPage())): "
+ + mRecentsView.getScrollForPage(mRecentsView.getCurrentPage()));
+ }
} else {
builder.addListener(
AnimatorListeners.forSuccessCallback(mRecentsView::resetTaskVisuals));
@@ -93,7 +106,7 @@
builder.addListener(AnimatorListeners.forSuccessCallback(() ->
mLauncher.getDepthController().setHasContentBehindLauncher(toState.overviewUi)));
- handleSplitSelectionState(toState, builder);
+ handleSplitSelectionState(toState, builder, /* animate */true);
setAlphas(builder, config, toState);
builder.setFloat(mRecentsView, FULLSCREEN_PROGRESS,
@@ -106,8 +119,14 @@
* will add animations to builder.
*/
private void handleSplitSelectionState(@NonNull LauncherState toState,
- @Nullable PendingAnimation builder) {
- boolean animate = builder != null;
+ @NonNull PendingAnimation builder, boolean animate) {
+ if (toState != OVERVIEW_SPLIT_SELECT) {
+ // Not going to split, nothing to do but ensure taskviews are at correct offset
+ mRecentsView.resetSplitPrimaryScrollOffset();
+ return;
+ }
+
+ // Create transition animations to split select
PagedOrientationHandler orientationHandler =
((RecentsView) mLauncher.getOverviewPanel()).getPagedOrientationHandler();
Pair<FloatProperty, FloatProperty> taskViewsFloat =
@@ -115,25 +134,27 @@
TASK_PRIMARY_SPLIT_TRANSLATION, TASK_SECONDARY_SPLIT_TRANSLATION,
mLauncher.getDeviceProfile());
- if (toState == OVERVIEW_SPLIT_SELECT) {
- // Animation to "dismiss" selected taskView
- PendingAnimation splitSelectInitAnimation = mRecentsView.createSplitSelectInitAnimation(
- toState.getTransitionDuration(mLauncher, true /* isToState */));
- // Add properties to shift remaining taskViews to get out of placeholder view
- splitSelectInitAnimation.setFloat(mRecentsView, taskViewsFloat.first,
- toState.getSplitSelectTranslation(mLauncher), LINEAR);
- splitSelectInitAnimation.setFloat(mRecentsView, taskViewsFloat.second, 0, LINEAR);
+ SplitAnimationTimings timings =
+ AnimUtils.getDeviceOverviewToSplitTimings(mLauncher.getDeviceProfile().isTablet);
- if (!animate) {
- splitSelectInitAnimation.buildAnim().start();
- } else {
- builder.add(splitSelectInitAnimation.buildAnim());
- }
+ mRecentsView.createSplitSelectInitAnimation(builder,
+ toState.getTransitionDuration(mLauncher, true /* isToState */));
+ // Shift tasks vertically downward to get out of placeholder view
+ builder.setFloat(mRecentsView, taskViewsFloat.first,
+ toState.getSplitSelectTranslation(mLauncher),
+ timings.getGridSlidePrimaryInterpolator());
+ // Zero out horizontal translation
+ builder.setFloat(mRecentsView, taskViewsFloat.second,
+ 0,
+ timings.getGridSlideSecondaryInterpolator());
- mRecentsView.applySplitPrimaryScrollOffset();
- } else {
- mRecentsView.resetSplitPrimaryScrollOffset();
+ if (!animate) {
+ AnimatorSet as = builder.buildAnim();
+ as.start();
+ as.end();
}
+
+ mRecentsView.applySplitPrimaryScrollOffset();
}
private void setAlphas(PropertySetter propertySetter, StateAnimationConfig config,
@@ -143,7 +164,7 @@
clearAllButtonAlpha, LINEAR);
float overviewButtonAlpha = state.areElementsVisible(mLauncher, OVERVIEW_ACTIONS) ? 1 : 0;
propertySetter.setFloat(mLauncher.getActionsView().getVisibilityAlpha(),
- MultiValueAlpha.VALUE, overviewButtonAlpha, config.getInterpolator(
+ MULTI_PROPERTY_VALUE, overviewButtonAlpha, config.getInterpolator(
ANIM_OVERVIEW_ACTIONS_FADE, LINEAR));
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
index 9f2efc4..fd184c6 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -20,6 +20,7 @@
import android.content.Context;
+import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DeviceProfile.DeviceProfileListenable;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
@@ -41,9 +42,9 @@
@Override
public <DEVICE_PROFILE_CONTEXT extends Context & DeviceProfileListenable>
int getTransitionDuration(DEVICE_PROFILE_CONTEXT context, boolean isToState) {
- return !context.getDeviceProfile().isTablet && isToState
- ? 600
- : isToState ? 500 : 300;
+ return isToState
+ ? context.getDeviceProfile().allAppsOpenDuration
+ : context.getDeviceProfile().allAppsCloseDuration;
}
@Override
@@ -77,10 +78,15 @@
}
@Override
- protected float getDepthUnchecked(Context context) {
- // The scrim fades in at approximately 50% of the swipe gesture.
- // This means that the depth should be greater than 1, in order to fully zoom out.
- return 2f;
+ protected <DEVICE_PROFILE_CONTEXT extends Context & DeviceProfile.DeviceProfileListenable>
+ float getDepthUnchecked(DEVICE_PROFILE_CONTEXT context) {
+ if (context.getDeviceProfile().isTablet) {
+ return context.getDeviceProfile().bottomSheetDepth;
+ } else {
+ // The scrim fades in at approximately 50% of the swipe gesture.
+ // This means that the depth should be greater than 1, in order to fully zoom out.
+ return 2f;
+ }
}
@Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
index b733007..733c6a8 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
@@ -16,6 +16,7 @@
package com.android.launcher3.uioverrides.states;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
+import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS;
import android.content.Context;
import android.graphics.Color;
@@ -25,6 +26,7 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.allapps.AllAppsTransitionController;
+import com.android.launcher3.config.FeatureFlags;
import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.views.RecentsView;
@@ -83,6 +85,11 @@
}
@Override
+ public boolean showTaskThumbnailSplash() {
+ return true;
+ }
+
+ @Override
protected float getDepthUnchecked(Context context) {
return 1;
}
@@ -90,12 +97,18 @@
@Override
public int getWorkspaceScrimColor(Launcher launcher) {
DeviceProfile dp = launcher.getDeviceProfile();
- if (dp.isTaskbarPresentInApps) {
+ if (dp.isTaskbarPresentInApps && !FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get()) {
return launcher.getColor(R.color.taskbar_background);
}
return Color.TRANSPARENT;
}
+ @Override
+ public boolean isTaskbarAlignedWithHotseat(Launcher launcher) {
+ if (ENABLE_SHELL_TRANSITIONS) return false;
+ return super.isTaskbarAlignedWithHotseat(launcher);
+ }
+
public static float[] getOverviewScaleAndOffsetForBackgroundState(
BaseDraggingActivity activity) {
return new float[] {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
index 6f07568..d075750 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -26,6 +26,7 @@
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.DisplayController;
import com.android.launcher3.util.Themes;
import com.android.quickstep.util.LayoutUtils;
@@ -104,7 +105,12 @@
@Override
public boolean isTaskbarStashed(Launcher launcher) {
- return true;
+ return !FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get();
+ }
+
+ @Override
+ public boolean isTaskbarAlignedWithHotseat(Launcher launcher) {
+ return !FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get();
}
@Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
index cb1da38..5eeeb36 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
@@ -34,6 +34,7 @@
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;
@@ -62,6 +63,7 @@
import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.launcher3.util.DisplayController;
import com.android.quickstep.util.RecentsAtomicAnimationFactory;
+import com.android.quickstep.util.SplitAnimationTimings;
import com.android.quickstep.views.RecentsView;
/**
@@ -111,19 +113,19 @@
config.setInterpolator(ANIM_OVERVIEW_FADE, FINAL_FRAME);
config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, EMPHASIZED_DECELERATE);
config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, FINAL_FRAME);
+
+ // Scroll RecentsView to page 0 as it goes offscreen, if necessary.
+ int numPagesToScroll = overview.getNextPage() - DEFAULT_PAGE;
+ long scrollDuration = Math.min(MAX_PAGE_SCROLL_DURATION,
+ numPagesToScroll * PER_PAGE_SCROLL_DURATION);
+ config.duration = Math.max(config.duration, scrollDuration);
+ 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);
}
- // Scroll RecentsView to page 0 as it goes offscreen, if necessary.
- int numPagesToScroll = overview.getNextPage() - DEFAULT_PAGE;
- long scrollDuration = Math.min(MAX_PAGE_SCROLL_DURATION,
- numPagesToScroll * PER_PAGE_SCROLL_DURATION);
- config.duration = Math.max(config.duration, scrollDuration);
- overview.snapToPage(DEFAULT_PAGE, Math.toIntExact(config.duration));
-
Workspace<?> workspace = mActivity.getWorkspace();
// Start from a higher workspace scale, but only if we're invisible so we don't jump.
boolean isWorkspaceVisible = workspace.getVisibility() == VISIBLE;
@@ -189,8 +191,21 @@
} else if (fromState == NORMAL && toState == ALL_APPS) {
AllAppsSwipeController.applyNormalToAllAppsAnimConfig(mActivity, config);
} else if (fromState == OVERVIEW && toState == OVERVIEW_SPLIT_SELECT) {
- config.setInterpolator(ANIM_OVERVIEW_ACTIONS_FADE,
- clampToProgress(LINEAR, 0, 0.167f));
+ SplitAnimationTimings timings = mActivity.getDeviceProfile().isTablet
+ ? SplitAnimationTimings.TABLET_OVERVIEW_TO_SPLIT
+ : SplitAnimationTimings.PHONE_OVERVIEW_TO_SPLIT;
+ config.setInterpolator(ANIM_OVERVIEW_ACTIONS_FADE, clampToProgress(LINEAR,
+ timings.getActionsFadeStartOffset(),
+ timings.getActionsFadeEndOffset()));
+ } else if ((fromState == NORMAL || fromState == ALL_APPS)
+ && toState == OVERVIEW_SPLIT_SELECT) {
+ // Splitting from Home is currently only available on tablets
+ SplitAnimationTimings timings = SplitAnimationTimings.TABLET_HOME_TO_SPLIT;
+ config.setInterpolator(ANIM_SCRIM_FADE, clampToProgress(LINEAR,
+ timings.getScrimFadeInStartOffset(),
+ timings.getScrimFadeInEndOffset()));
+ config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, OVERSHOOT_0_75);
+ config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, OVERSHOOT_0_75);
}
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java b/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java
index cb08ac8..8babd34 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java
@@ -19,6 +19,7 @@
import android.content.Context;
import com.android.launcher3.Launcher;
+import com.android.quickstep.util.SplitAnimationTimings;
import com.android.quickstep.views.RecentsView;
/**
@@ -26,8 +27,6 @@
* pinned and user is selecting the second one
*/
public class SplitScreenSelectState extends OverviewState {
- private static final int OVERVIEW_SPLIT_SELECT_SLIDE_IN_DURATION = 500;
-
public SplitScreenSelectState(int id) {
super(id);
}
@@ -45,6 +44,13 @@
@Override
public int getTransitionDuration(Context context, boolean isToState) {
- return OVERVIEW_SPLIT_SELECT_SLIDE_IN_DURATION;
+ boolean isTablet = ((Launcher) context).getDeviceProfile().isTablet;
+ if (isToState && isTablet) {
+ return SplitAnimationTimings.TABLET_ENTER_DURATION;
+ } else if (isToState && !isTablet) {
+ return SplitAnimationTimings.PHONE_ENTER_DURATION;
+ } else {
+ return SplitAnimationTimings.ABORT_DURATION;
+ }
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
index 34a6821..40dfd82 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
@@ -25,8 +25,6 @@
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.config.FeatureFlags.ENABLE_ALL_APPS_EDU;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_GESTURE;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
@@ -106,7 +104,7 @@
if (mStartState.overviewUi || mStartState == ALL_APPS) {
return true;
}
- int typeToClose = ENABLE_ALL_APPS_EDU.get() ? TYPE_ALL & ~TYPE_ALL_APPS_EDU : TYPE_ALL;
+ int typeToClose = TYPE_ALL & ~TYPE_ALL_APPS_EDU;
if (AbstractFloatingView.getTopOpenViewWithType(mLauncher, typeToClose) != null) {
return true;
}
@@ -140,9 +138,7 @@
AnimatorControllerWithResistance.createRecentsResistanceFromOverviewAnim(mLauncher,
builder);
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- builder.addOnFrameCallback(recentsView::redrawLiveTile);
- }
+ builder.addOnFrameCallback(recentsView::redrawLiveTile);
AbstractFloatingView.closeOpenContainer(mLauncher, AbstractFloatingView.TYPE_TASK_MENU);
} else if (mStartState == ALL_APPS) {
@@ -183,11 +179,9 @@
boolean success = interpolatedProgress >= SUCCESS_TRANSITION_PROGRESS
|| (velocity < 0 && fling);
if (success) {
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- RecentsView recentsView = mLauncher.getOverviewPanel();
- recentsView.switchToScreenshot(null,
- () -> recentsView.finishRecentsAnimation(true /* toRecents */, null));
- }
+ RecentsView recentsView = mLauncher.getOverviewPanel();
+ recentsView.switchToScreenshot(null,
+ () -> recentsView.finishRecentsAnimation(true /* toRecents */, null));
if (mStartState.overviewUi) {
new OverviewToHomeAnim(mLauncher, () -> onSwipeInteractionCompleted(mEndState))
.animateWithVelocity(velocity);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
index 8faabc9..f343f52 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
@@ -26,6 +26,7 @@
import static com.android.launcher3.anim.AnimatorListeners.forSuccessCallback;
import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
import static com.android.quickstep.util.VibratorWrapper.OVERVIEW_HAPTIC;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ONE_HANDED_ACTIVE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
import android.animation.ObjectAnimator;
@@ -34,13 +35,13 @@
import android.view.MotionEvent;
import android.view.ViewConfiguration;
-import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.taskbar.LauncherTaskbarUIController;
+import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.MotionPauseDetector;
@@ -86,6 +87,11 @@
@Override
protected boolean canInterceptTouch(MotionEvent ev) {
mDidTouchStartInNavBar = (ev.getEdgeFlags() & EDGE_NAV_BAR) != 0;
+ boolean isOneHandedModeActive = (SystemUiProxy.INSTANCE.get(mLauncher)
+ .getLastSystemUiStateFlags() & SYSUI_STATE_ONE_HANDED_ACTIVE) != 0;
+ // Reset touch slop multiplier to default 1.0f if one-handed-mode is not active
+ mDetector.setTouchSlopMultiplier(
+ isOneHandedModeActive ? ONE_HANDED_ACTIVATED_SLOP_MULTIPLIER : 1f /* default */);
return super.canInterceptTouch(ev) && !mLauncher.isInState(HINT_STATE);
}
@@ -114,7 +120,7 @@
public void onDragStart(boolean start, float startDisplacement) {
if (mLauncher.isInState(ALL_APPS)) {
LauncherTaskbarUIController controller =
- ((BaseQuickstepLauncher) mLauncher).getTaskbarUIController();
+ ((QuickstepLauncher) mLauncher).getTaskbarUIController();
if (controller != null) {
controller.setShouldDelayLauncherStateAnim(true);
}
@@ -151,7 +157,7 @@
@Override
public void onDragEnd(float velocity) {
LauncherTaskbarUIController controller =
- ((BaseQuickstepLauncher) mLauncher).getTaskbarUIController();
+ ((QuickstepLauncher) mLauncher).getTaskbarUIController();
if (controller != null) {
controller.setShouldDelayLauncherStateAnim(false);
}
@@ -277,14 +283,4 @@
private float dpiFromPx(float pixels) {
return Utilities.dpiFromPx(pixels, mLauncher.getResources().getDisplayMetrics().densityDpi);
}
-
- @Override
- public void onOneHandedModeStateChanged(boolean activated) {
- if (activated) {
- mDetector.setTouchSlopMultiplier(ONE_HANDED_ACTIVATED_SLOP_MULTIPLIER);
- } else {
- // Reset touch slop multiplier to default 1.0f
- mDetector.setTouchSlopMultiplier(1f /* default */);
- }
- }
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
index bc76487..922679b 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
@@ -48,6 +48,7 @@
import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION;
+import static com.android.quickstep.views.RecentsView.TASK_THUMBNAIL_SPLASH_ALPHA;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
import android.animation.Animator;
@@ -58,7 +59,6 @@
import android.view.MotionEvent;
import android.view.animation.Interpolator;
-import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
@@ -67,6 +67,7 @@
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.touch.BaseSwipeDetector;
import com.android.launcher3.touch.BothAxesSwipeDetector;
+import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.launcher3.util.TouchController;
import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.SystemUiProxy;
@@ -91,7 +92,7 @@
private static final Interpolator SCALE_DOWN_INTERPOLATOR = LINEAR;
private static final long ATOMIC_DURATION_FROM_PAUSED_TO_OVERVIEW = 300;
- private final BaseQuickstepLauncher mLauncher;
+ private final QuickstepLauncher mLauncher;
private final BothAxesSwipeDetector mSwipeDetector;
private final float mXRange;
private final float mYRange;
@@ -113,7 +114,7 @@
private AnimatorPlaybackController mXOverviewAnim;
private AnimatedFloat mYOverviewAnim;
- public NoButtonQuickSwitchTouchController(BaseQuickstepLauncher launcher) {
+ public NoButtonQuickSwitchTouchController(QuickstepLauncher launcher) {
mLauncher = launcher;
mSwipeDetector = new BothAxesSwipeDetector(mLauncher, this);
mRecentsView = mLauncher.getOverviewPanel();
@@ -225,6 +226,7 @@
// Set RecentView's initial properties.
RECENTS_SCALE_PROPERTY.set(mRecentsView, fromState.getOverviewScaleAndOffset(mLauncher)[0]);
ADJACENT_PAGE_HORIZONTAL_OFFSET.set(mRecentsView, 1f);
+ TASK_THUMBNAIL_SPLASH_ALPHA.set(mRecentsView, fromState.showTaskThumbnailSplash() ? 1f : 0);
mRecentsView.setContentAlpha(1);
mRecentsView.setFullscreenProgress(fromState.getOverviewFullscreenProgress());
mLauncher.getActionsView().getVisibilityAlpha().setValue(
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
index e5cd53a..56ac4c5 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
@@ -45,7 +45,7 @@
import com.android.launcher3.touch.AbstractStateChangeTouchController;
import com.android.launcher3.touch.SingleAxisSwipeDetector;
import com.android.launcher3.util.DisplayController;
-import com.android.launcher3.util.DisplayController.NavigationMode;
+import com.android.launcher3.util.NavigationMode;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TaskUtils;
import com.android.quickstep.views.RecentsView;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
index ca7f633..c49848a 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
@@ -236,7 +236,8 @@
PendingAnimation pa;
if (goingUp) {
currentInterpolator = Interpolators.LINEAR;
- pa = mRecentsView.createTaskDismissAnimation(mTaskBeingDragged,
+ pa = new PendingAnimation(maxDuration);
+ mRecentsView.createTaskDismissAnimation(pa, mTaskBeingDragged,
true /* animateTaskView */, true /* removeTask */, maxDuration,
false /* dismissingForSplitSelection*/);
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 8dee10a..b09e531 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -15,6 +15,7 @@
*/
package com.android.quickstep;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
@@ -26,13 +27,13 @@
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.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.IGNORE;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_GESTURE;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_OVERVIEW_GESTURE;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_QUICKSWITCH_LEFT;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_QUICKSWITCH_RIGHT;
+import static com.android.launcher3.uioverrides.QuickstepLauncher.ENABLE_PIP_KEEP_CLEAR_ALGORITHM;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK;
@@ -46,10 +47,12 @@
import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_CANCELED;
import static com.android.quickstep.GestureState.STATE_RECENTS_SCROLLING_FINISHED;
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.CANCEL_RECENTS_ANIMATION;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.EXPECTING_TASK_APPEARED;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.ON_SETTLED_ON_END_TARGET;
import static com.android.quickstep.util.VibratorWrapper.OVERVIEW_HAPTIC;
import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -61,6 +64,7 @@
import android.app.WindowConfiguration;
import android.content.Context;
import android.content.Intent;
+import android.content.res.Resources;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.graphics.Rect;
@@ -70,8 +74,10 @@
import android.os.SystemClock;
import android.util.Log;
import android.view.MotionEvent;
+import android.view.RemoteAnimationTarget;
import android.view.View;
import android.view.View.OnApplyWindowInsetsListener;
+import android.view.ViewGroup;
import android.view.ViewTreeObserver.OnDrawListener;
import android.view.ViewTreeObserver.OnScrollChangedListener;
import android.view.WindowInsets;
@@ -82,6 +88,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
+import com.android.internal.util.LatencyTracker;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
@@ -92,14 +99,17 @@
import com.android.launcher3.logging.StatsLogManager.StatsLogger;
import com.android.launcher3.statemanager.BaseState;
import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.launcher3.taskbar.TaskbarUIController;
import com.android.launcher3.tracing.InputConsumerProto;
import com.android.launcher3.tracing.SwipeHandlerProto;
import com.android.launcher3.util.ActivityLifecycleCallbacksAdapter;
+import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.TraceHelper;
import com.android.launcher3.util.WindowBounds;
import com.android.quickstep.BaseActivityInterface.AnimationFactory;
import com.android.quickstep.GestureState.GestureEndTarget;
import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
+import com.android.quickstep.util.ActiveGestureErrorDetector;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.AnimatorControllerWithResistance;
@@ -110,6 +120,7 @@
import com.android.quickstep.util.RecentsOrientedState;
import com.android.quickstep.util.RectFSpringAnim;
import com.android.quickstep.util.StaggeredWorkspaceAnim;
+import com.android.quickstep.util.SurfaceTransaction;
import com.android.quickstep.util.SurfaceTransactionApplier;
import com.android.quickstep.util.SwipePipToHomeAnimator;
import com.android.quickstep.util.TaskViewSimulator;
@@ -121,14 +132,15 @@
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
-import com.android.systemui.shared.system.LatencyTrackerCompat;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
+import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.startingsurface.SplashScreenExitAnimationUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
+import java.util.Optional;
import java.util.function.Consumer;
/**
@@ -244,6 +256,13 @@
private static final float MAX_QUICK_SWITCH_RECENTS_SCALE_PROGRESS = 0.07f;
+ // Controls task thumbnail splash's reveal animation after landing on a task from quickswitch.
+ // These values match WindowManager/Shell starting_window_app_reveal_* config values.
+ private static final int SPLASH_FADE_OUT_DURATION = 133;
+ private static final int SPLASH_APP_REVEAL_DELAY = 83;
+ private static final int SPLASH_APP_REVEAL_DURATION = 266;
+ private static final int SPLASH_ANIMATION_DURATION = 349;
+
/**
* Used as the page index for logging when we return to the last task at the end of the gesture.
*/
@@ -268,7 +287,7 @@
private AnimatorControllerWithResistance mLauncherTransitionController;
private boolean mHasEndedLauncherTransition;
- private AnimationFactory mAnimationFactory = (t) -> { };
+ private AnimationFactory mAnimationFactory = (t, s) -> { };
private boolean mWasLauncherAlreadyVisible;
@@ -281,6 +300,8 @@
private final long mTouchTimeMs;
private long mLauncherFrameDrawnTime;
+ private final int mSplashMainWindowShiftLength;
+
private final Runnable mOnDeferredActivityLaunch = this::onDeferredActivityLaunch;
private SwipePipToHomeAnimator mSwipePipToHomeAnimator;
@@ -293,6 +314,10 @@
// Interpolate RecentsView scale from start of quick switch scroll until this scroll threshold
private final float mQuickSwitchScaleScrollThreshold;
+ private final int mTaskbarAppWindowThreshold;
+ private final int mTaskbarCatchUpThreshold;
+ private boolean mTaskbarAlreadyOpen;
+
public AbsSwipeUpHandler(Context context, RecentsAnimationDeviceState deviceState,
TaskAnimationManager taskAnimationManager, GestureState gestureState,
long touchTimeMs, boolean continuingLastGesture,
@@ -313,15 +338,45 @@
mTaskAnimationManager = taskAnimationManager;
mTouchTimeMs = touchTimeMs;
mContinuingLastGesture = continuingLastGesture;
- mQuickSwitchScaleScrollThreshold = context.getResources().getDimension(
- R.dimen.quick_switch_scaling_scroll_threshold);
+
+ Resources res = context.getResources();
+ mTaskbarAppWindowThreshold = res
+ .getDimensionPixelSize(R.dimen.taskbar_app_window_threshold);
+ mTaskbarCatchUpThreshold = res.getDimensionPixelSize(R.dimen.taskbar_catch_up_threshold);
+
+ mQuickSwitchScaleScrollThreshold = res
+ .getDimension(R.dimen.quick_switch_scaling_scroll_threshold);
+
+ mSplashMainWindowShiftLength = -res
+ .getDimensionPixelSize(R.dimen.starting_surface_exit_animation_window_shift_length);
initAfterSubclassConstructor();
initStateCallbacks();
}
+ @Nullable
+ private static ActiveGestureErrorDetector.GestureEvent getTrackedEventForState(int stateFlag) {
+ if (stateFlag == STATE_GESTURE_STARTED) {
+ return ActiveGestureErrorDetector.GestureEvent.STATE_GESTURE_STARTED;
+ } else if (stateFlag == STATE_GESTURE_COMPLETED) {
+ return ActiveGestureErrorDetector.GestureEvent.STATE_GESTURE_COMPLETED;
+ } else if (stateFlag == STATE_GESTURE_CANCELLED) {
+ return ActiveGestureErrorDetector.GestureEvent.STATE_GESTURE_CANCELLED;
+ } else if (stateFlag == STATE_SCREENSHOT_CAPTURED) {
+ return ActiveGestureErrorDetector.GestureEvent.STATE_SCREENSHOT_CAPTURED;
+ } else if (stateFlag == STATE_CAPTURE_SCREENSHOT) {
+ return ActiveGestureErrorDetector.GestureEvent.STATE_CAPTURE_SCREENSHOT;
+ } else if (stateFlag == STATE_HANDLER_INVALIDATED) {
+ return ActiveGestureErrorDetector.GestureEvent.STATE_HANDLER_INVALIDATED;
+ } else if (stateFlag == STATE_LAUNCHER_DRAWN) {
+ return ActiveGestureErrorDetector.GestureEvent.STATE_LAUNCHER_DRAWN;
+ }
+ return null;
+ }
+
private void initStateCallbacks() {
- mStateCallback = new MultiStateCallback(STATE_NAMES.toArray(new String[0]));
+ mStateCallback = new MultiStateCallback(
+ STATE_NAMES.toArray(new String[0]), AbsSwipeUpHandler::getTrackedEventForState);
mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_GESTURE_STARTED,
this::onLauncherPresentAndGestureStarted);
@@ -374,12 +429,6 @@
this::resetStateForAnimationCancel);
mStateCallback.runOnceAtState(STATE_HANDLER_INVALIDATED | STATE_FINISH_WITH_NO_END,
this::resetStateForAnimationCancel);
-
- if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- mStateCallback.addChangeListener(STATE_APP_CONTROLLER_RECEIVED | STATE_LAUNCHER_PRESENT
- | STATE_SCREENSHOT_VIEW_SHOWN | STATE_CAPTURE_SCREENSHOT,
- (b) -> mRecentsView.setRunningTaskHidden(!b));
- }
}
protected boolean onActivityInit(Boolean alreadyOnHome) {
@@ -482,7 +531,9 @@
Runnable initAnimFactory = () -> {
mAnimationFactory = mActivityInterface.prepareRecentsUI(mDeviceState,
mWasLauncherAlreadyVisible, this::onAnimatorPlaybackControllerCreated);
- maybeUpdateRecentsAttachedState(false /* animate */);
+ maybeUpdateRecentsAttachedState(
+ false /* animate */,
+ new ActiveGestureLog.CompoundString("on Launcher start (animate=false)"));
if (mGestureState.getEndTarget() != null) {
// Update the end target in case the gesture ended before we init.
mAnimationFactory.setEndTarget(mGestureState.getEndTarget());
@@ -557,14 +608,10 @@
}
private void onDeferredActivityLaunch() {
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- mActivityInterface.switchRunningTaskViewToScreenshot(
- null, () -> {
- mTaskAnimationManager.finishRunningRecentsAnimation(true /* toHome */);
- });
- } else {
- mTaskAnimationManager.finishRunningRecentsAnimation(true /* toHome */);
- }
+ mActivityInterface.switchRunningTaskViewToScreenshot(
+ null, () -> {
+ mTaskAnimationManager.finishRunningRecentsAnimation(true /* toHome */);
+ });
}
private void setupRecentsViewUi() {
@@ -591,12 +638,13 @@
}
private void initializeLauncherAnimationController() {
- buildAnimationController();
+ buildAnimationController(new ActiveGestureLog.CompoundString(
+ "initializing launcher animation controller"));
Object traceToken = TraceHelper.INSTANCE.beginSection("logToggleRecents",
TraceHelper.FLAG_IGNORE_BINDERS);
- LatencyTrackerCompat.logToggleRecents(
- mContext, (int) (mLauncherFrameDrawnTime - mTouchTimeMs));
+ LatencyTracker.getInstance(mContext).logAction(LatencyTracker.ACTION_TOGGLE_RECENTS,
+ (int) (mLauncherFrameDrawnTime - mTouchTimeMs));
TraceHelper.INSTANCE.endSection(traceToken);
// This method is only called when STATE_GESTURE_STARTED is set, so we can enable the
@@ -610,7 +658,13 @@
@Override
public void onMotionPauseDetected() {
mHasMotionEverBeenPaused = true;
- maybeUpdateRecentsAttachedState(true/* animate */, true/* moveFocusedTask */);
+ maybeUpdateRecentsAttachedState(
+ true/* animate */,
+ true/* moveFocusedTask */,
+ new ActiveGestureLog.CompoundString(
+ "motion pause detected (animate=true)"));
+ Optional.ofNullable(mActivityInterface.getTaskbarController())
+ .ifPresent(TaskbarUIController::startTranslationSpring);
performHapticFeedback();
}
@@ -621,12 +675,13 @@
};
}
- private void maybeUpdateRecentsAttachedState() {
- maybeUpdateRecentsAttachedState(true /* animate */);
+ private void maybeUpdateRecentsAttachedState(ActiveGestureLog.CompoundString reason) {
+ maybeUpdateRecentsAttachedState(true /* animate */, reason.append(" (animate=true)"));
}
- private void maybeUpdateRecentsAttachedState(boolean animate) {
- maybeUpdateRecentsAttachedState(animate, false /* moveFocusedTask */);
+ private void maybeUpdateRecentsAttachedState(
+ boolean animate, ActiveGestureLog.CompoundString reason) {
+ maybeUpdateRecentsAttachedState(animate, false /* moveFocusedTask */, reason);
}
/**
@@ -638,24 +693,36 @@
* @param animate whether to animate when attaching RecentsView
* @param moveFocusedTask whether to move focused task to front when attaching
*/
- private void maybeUpdateRecentsAttachedState(boolean animate, boolean moveFocusedTask) {
+ private void maybeUpdateRecentsAttachedState(
+ boolean animate, boolean moveFocusedTask, ActiveGestureLog.CompoundString reason) {
if (!mDeviceState.isFullyGesturalNavMode() || mRecentsView == null) {
return;
}
- RemoteAnimationTargetCompat runningTaskTarget = mRecentsAnimationTargets != null
+ RemoteAnimationTarget runningTaskTarget = mRecentsAnimationTargets != null
? mRecentsAnimationTargets.findTask(mGestureState.getRunningTaskId())
: null;
final boolean recentsAttachedToAppWindow;
if (mGestureState.getEndTarget() != null) {
recentsAttachedToAppWindow = mGestureState.getEndTarget().recentsAttachedToAppWindow;
+ reason.append("; gesture state end target != null (attached=")
+ .append(Boolean.toString(recentsAttachedToAppWindow))
+ .append(")");
} else if (mContinuingLastGesture
&& mRecentsView.getRunningTaskIndex() != mRecentsView.getNextPage()) {
recentsAttachedToAppWindow = true;
+ reason.append("; continuing last gesture (attached=true)");
} else if (runningTaskTarget != null && isNotInRecents(runningTaskTarget)) {
// The window is going away so make sure recents is always visible in this case.
recentsAttachedToAppWindow = true;
+ reason.append("; make sure recents is always visible (attached=true)");
} else {
recentsAttachedToAppWindow = mHasMotionEverBeenPaused || mIsLikelyToStartNewTask;
+ reason.append(mHasMotionEverBeenPaused
+ ? "; motion has been paused"
+ : "; gesture is likely to start a new task")
+ .append(" (attached=")
+ .append(Boolean.toString(recentsAttachedToAppWindow))
+ .append(")");
}
if (moveFocusedTask && !mAnimationFactory.hasRecentsEverAttachedToAppWindow()
&& recentsAttachedToAppWindow) {
@@ -663,7 +730,8 @@
// TaskView jumping to new position as we move the tasks.
mRecentsView.moveFocusedTaskToFront();
}
- mAnimationFactory.setRecentsAttachedToAppWindow(recentsAttachedToAppWindow, animate);
+ mAnimationFactory.setRecentsAttachedToAppWindow(
+ recentsAttachedToAppWindow, animate, reason);
// Reapply window transform throughout the attach animation, as the animation affects how
// much the window is bound by overscroll (vs moving freely).
@@ -683,22 +751,29 @@
}
public void setIsLikelyToStartNewTask(boolean isLikelyToStartNewTask) {
- setIsLikelyToStartNewTask(isLikelyToStartNewTask, true /* animate */);
+ setIsLikelyToStartNewTask(
+ isLikelyToStartNewTask,
+ true /* animate */,
+ new ActiveGestureLog.CompoundString(
+ "setting gesture likely to start (animate=true)"));
}
- private void setIsLikelyToStartNewTask(boolean isLikelyToStartNewTask, boolean animate) {
+ private void setIsLikelyToStartNewTask(
+ boolean isLikelyToStartNewTask,
+ boolean animate,
+ ActiveGestureLog.CompoundString reason) {
if (mIsLikelyToStartNewTask != isLikelyToStartNewTask) {
mIsLikelyToStartNewTask = isLikelyToStartNewTask;
- maybeUpdateRecentsAttachedState(animate);
+ maybeUpdateRecentsAttachedState(animate, reason);
}
}
- private void buildAnimationController() {
+ private void buildAnimationController(ActiveGestureLog.CompoundString reason) {
if (!canCreateNewOrUpdateExistingLauncherTransitionController()) {
return;
}
initTransitionEndpoints(mActivity.getDeviceProfile());
- mAnimationFactory.createActivityInterface(mTransitionDragLength);
+ mAnimationFactory.createActivityInterface(mTransitionDragLength, reason);
}
/**
@@ -713,7 +788,7 @@
@Override
public WindowInsets onApplyWindowInsets(View view, WindowInsets windowInsets) {
WindowInsets result = view.onApplyWindowInsets(windowInsets);
- buildAnimationController();
+ buildAnimationController(new ActiveGestureLog.CompoundString("applying window insets"));
// Reapply the current shift to ensure it takes new insets into account, e.g. when long
// pressing to stash taskbar without moving the finger.
updateFinalShift();
@@ -764,7 +839,7 @@
return;
}
mLauncherTransitionController.setProgress(
- Math.max(mCurrentShift.value, getScaleProgressDueToScroll()), mDragLengthFactor);
+ Math.max(getTaskbarProgress(), getScaleProgressDueToScroll()), mDragLengthFactor);
}
/**
@@ -800,7 +875,6 @@
public void onRecentsAnimationStart(RecentsAnimationController controller,
RecentsAnimationTargets targets) {
super.onRecentsAnimationStart(controller, targets);
- ActiveGestureLog.INSTANCE.addLog("startRecentsAnimationCallback", targets.apps.length);
mRemoteTargetHandles = mTargetGluer.assignTargetsForSplitScreen(mContext, targets);
mRecentsAnimationController = controller;
mRecentsAnimationTargets = targets;
@@ -808,7 +882,7 @@
// Only initialize the device profile, if it has not been initialized before, as in some
// configurations targets.homeContentInsets may not be correct.
if (mActivity == null) {
- RemoteAnimationTargetCompat primaryTaskTarget = targets.apps[0];
+ RemoteAnimationTarget primaryTaskTarget = targets.apps[0];
// orientation state is independent of which remote target handle we use since both
// should be pointing to the same one. Just choose index 0 for now since that works for
// both split and non-split
@@ -843,17 +917,14 @@
@Override
public void onRecentsAnimationCanceled(HashMap<Integer, ThumbnailData> thumbnailDatas) {
- ActiveGestureLog.INSTANCE.addLog("cancelRecentsAnimation");
+ ActiveGestureLog.INSTANCE.addLog(
+ /* event= */ "cancelRecentsAnimation",
+ /* gestureEvent= */ CANCEL_RECENTS_ANIMATION);
mActivityInitListener.unregister();
// Cache the recents animation controller so we can defer its cleanup to after having
// properly cleaned up the screenshot without accidentally using it.
mDeferredCleanupRecentsAnimationController = mRecentsAnimationController;
mStateCallback.setStateOnUiThread(STATE_GESTURE_CANCELLED | STATE_HANDLER_INVALIDATED);
-
- if (mRecentsAnimationTargets != null) {
- setDividerShown(true /* shown */, false /* immediate */);
- }
-
// Defer clearing the controller and the targets until after we've updated the state
mRecentsAnimationController = null;
mRecentsAnimationTargets = null;
@@ -883,16 +954,20 @@
InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH, 2000 /* ms timeout */);
InteractionJankMonitorWrapper.begin(mRecentsView,
InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_HOME);
+ InteractionJankMonitorWrapper.begin(mRecentsView,
+ InteractionJankMonitorWrapper.CUJ_APP_SWIPE_TO_RECENTS);
rv.post(() -> rv.getViewTreeObserver().removeOnDrawListener(this));
}
});
}
notifyGestureStartedAsync();
- setIsLikelyToStartNewTask(isLikelyToStartNewTask, false /* animate */);
+ setIsLikelyToStartNewTask(
+ isLikelyToStartNewTask,
+ false /* animate */,
+ new ActiveGestureLog.CompoundString("on gesture started (animate=false)"));
mStateCallback.setStateOnUiThread(STATE_GESTURE_STARTED);
mGestureStarted = true;
- SystemUiProxy.INSTANCE.get(mContext).notifySwipeUpGestureStarted();
}
/**
@@ -971,7 +1046,8 @@
private void onSettledOnEndTarget() {
// Fast-finish the attaching animation if it's still running.
- maybeUpdateRecentsAttachedState(false);
+ maybeUpdateRecentsAttachedState(false, new ActiveGestureLog.CompoundString(
+ "on settled on end target (animate=false)"));
final GestureEndTarget endTarget = mGestureState.getEndTarget();
// Wait until the given View (if supplied) draws before resuming the last task.
View postResumeLastTask = mActivityInterface.onSettledOnEndTarget(endTarget);
@@ -984,12 +1060,16 @@
InteractionJankMonitorWrapper.cancel(
InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_HOME);
}
+ if (endTarget != RECENTS) {
+ InteractionJankMonitorWrapper.cancel(
+ InteractionJankMonitorWrapper.CUJ_APP_SWIPE_TO_RECENTS);
+ }
switch (endTarget) {
case HOME:
mStateCallback.setState(STATE_SCALED_CONTROLLER_HOME | STATE_CAPTURE_SCREENSHOT);
- // Notify swipe-to-home (recents animation) is finished
- SystemUiProxy.INSTANCE.get(mContext).notifySwipeToHomeFinished();
+ // Notify the SysUI to use fade-in animation when entering PiP
+ SystemUiProxy.INSTANCE.get(mContext).setPipAnimationTypeToAlpha();
break;
case RECENTS:
mStateCallback.setState(STATE_SCALED_CONTROLLER_RECENTS | STATE_CAPTURE_SCREENSHOT
@@ -1010,11 +1090,13 @@
}
break;
}
- ActiveGestureLog.INSTANCE.addLog("onSettledOnEndTarget " + endTarget);
+ ActiveGestureLog.INSTANCE.addLog(
+ /* event= */ "onSettledOnEndTarget " + endTarget,
+ /* gestureEvent= */ ON_SETTLED_ON_END_TARGET);
}
/** @return Whether this was the task we were waiting to appear, and thus handled it. */
- protected boolean handleTaskAppeared(RemoteAnimationTargetCompat[] appearedTaskTarget) {
+ protected boolean handleTaskAppeared(RemoteAnimationTarget[] appearedTaskTarget) {
if (mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)) {
return false;
}
@@ -1027,77 +1109,90 @@
return false;
}
- private GestureEndTarget calculateEndTarget(PointF velocity, float endVelocity,
- boolean isFlingY, boolean isCancel) {
+ private GestureEndTarget calculateEndTarget(
+ PointF velocity, float endVelocity, boolean isFlingY, boolean isCancel) {
+
if (mGestureState.isHandlingAtomicEvent()) {
- // Button mode, this is only used to go to recents
+ // Button mode, this is only used to go to recents.
return RECENTS;
}
- final GestureEndTarget endTarget;
- final boolean goingToNewTask;
- if (mRecentsView != null) {
- if (!hasTargets()) {
- // If there are no running tasks, then we can assume that this is a continuation of
- // the last gesture, but after the recents animation has finished
- goingToNewTask = true;
- } else {
- final int runningTaskIndex = mRecentsView.getRunningTaskIndex();
- final int taskToLaunch = mRecentsView.getNextPage();
- goingToNewTask = runningTaskIndex >= 0 && taskToLaunch != runningTaskIndex;
- }
- } else {
- goingToNewTask = false;
- }
- final boolean reachedOverviewThreshold = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW;
- final boolean isFlingX = Math.abs(velocity.x) > mContext.getResources()
- .getDimension(R.dimen.quickstep_fling_threshold_speed);
- if (!isFlingY) {
- if (isCancel) {
- endTarget = LAST_TASK;
- } else if (mDeviceState.isFullyGesturalNavMode()) {
- if (goingToNewTask && isFlingX) {
- // Flinging towards new task takes precedence over mIsMotionPaused (which only
- // checks y-velocity).
- endTarget = NEW_TASK;
- } else if (mIsMotionPaused) {
- endTarget = RECENTS;
- } else if (goingToNewTask) {
- endTarget = NEW_TASK;
- } else {
- endTarget = !reachedOverviewThreshold ? LAST_TASK : HOME;
- }
- } else {
- endTarget = reachedOverviewThreshold && mGestureStarted
- ? RECENTS
- : goingToNewTask
- ? NEW_TASK
- : LAST_TASK;
- }
- } else {
- // If swiping at a diagonal, base end target on the faster velocity.
- boolean isSwipeUp = endVelocity < 0;
- boolean willGoToNewTaskOnSwipeUp =
- goingToNewTask && Math.abs(velocity.x) > Math.abs(endVelocity);
- if (mDeviceState.isFullyGesturalNavMode() && isSwipeUp && !willGoToNewTaskOnSwipeUp) {
- endTarget = HOME;
- } else if (mDeviceState.isFullyGesturalNavMode() && isSwipeUp) {
- // If swiping at a diagonal, base end target on the faster velocity.
- endTarget = NEW_TASK;
- } else if (isSwipeUp) {
- endTarget = !reachedOverviewThreshold && willGoToNewTaskOnSwipeUp
- ? NEW_TASK : RECENTS;
- } else {
- endTarget = goingToNewTask ? NEW_TASK : LAST_TASK;
- }
+ GestureEndTarget endTarget;
+ if (isCancel) {
+ endTarget = LAST_TASK;
+ } else if (isFlingY) {
+ endTarget = calculateEndTargetForFlingY(velocity, endVelocity);
+ } else {
+ endTarget = calculateEndTargetForNonFling(velocity);
}
- if (mDeviceState.isOverviewDisabled() && (endTarget == RECENTS || endTarget == LAST_TASK)) {
+ if (mDeviceState.isOverviewDisabled() && endTarget == RECENTS) {
return LAST_TASK;
}
+
return endTarget;
}
+ private GestureEndTarget calculateEndTargetForFlingY(PointF velocity, float endVelocity) {
+ // If swiping at a diagonal, base end target on the faster velocity direction.
+ final boolean willGoToNewTask =
+ isScrollingToNewTask() && Math.abs(velocity.x) > Math.abs(endVelocity);
+ final boolean isSwipeUp = endVelocity < 0;
+ if (!isSwipeUp) {
+ final boolean isCenteredOnNewTask =
+ mRecentsView.getDestinationPage() != mRecentsView.getRunningTaskIndex();
+ return willGoToNewTask || isCenteredOnNewTask ? NEW_TASK : LAST_TASK;
+ }
+
+ if (!mDeviceState.isFullyGesturalNavMode()) {
+ return (!hasReachedOverviewThreshold() && willGoToNewTask) ? NEW_TASK : RECENTS;
+ }
+ return willGoToNewTask ? NEW_TASK : HOME;
+ }
+
+ private GestureEndTarget calculateEndTargetForNonFling(PointF velocity) {
+ final boolean isScrollingToNewTask = isScrollingToNewTask();
+ final boolean reachedOverviewThreshold = hasReachedOverviewThreshold();
+ if (!mDeviceState.isFullyGesturalNavMode()) {
+ return reachedOverviewThreshold && mGestureStarted
+ ? RECENTS
+ : (isScrollingToNewTask ? NEW_TASK : LAST_TASK);
+ }
+
+ // Fully gestural mode.
+ final boolean isFlingX = Math.abs(velocity.x) > mContext.getResources()
+ .getDimension(R.dimen.quickstep_fling_threshold_speed);
+ if (isScrollingToNewTask && isFlingX) {
+ // Flinging towards new task takes precedence over mIsMotionPaused (which only
+ // checks y-velocity).
+ return NEW_TASK;
+ } else if (mIsMotionPaused) {
+ return RECENTS;
+ } else if (isScrollingToNewTask) {
+ return NEW_TASK;
+ } else if (reachedOverviewThreshold) {
+ return HOME;
+ }
+ return LAST_TASK;
+ }
+
+ private boolean isScrollingToNewTask() {
+ if (mRecentsView == null) {
+ return false;
+ }
+ if (!hasTargets()) {
+ // If there are no running tasks, then we can assume that this is a continuation of
+ // the last gesture, but after the recents animation has finished.
+ return true;
+ }
+ int runningTaskIndex = mRecentsView.getRunningTaskIndex();
+ return runningTaskIndex >= 0 && mRecentsView.getNextPage() != runningTaskIndex;
+ }
+
+ private boolean hasReachedOverviewThreshold() {
+ return getTaskbarProgress() > MIN_PROGRESS_FOR_OVERVIEW;
+ }
+
@UiThread
private void handleNormalGestureEnd(float endVelocity, boolean isFling, PointF velocity,
boolean isCancel) {
@@ -1175,11 +1270,16 @@
duration = Math.max(duration, mRecentsView.getScroller().getDuration());
}
}
+ } else if (endTarget == LAST_TASK && mRecentsView != null
+ && mRecentsView.getNextPage() != mRecentsView.getRunningTaskIndex()) {
+ mRecentsView.snapToPage(mRecentsView.getRunningTaskIndex(), Math.toIntExact(duration));
}
// Let RecentsView handle the scrolling to the task, which we launch in startNewTask()
// or resumeLastTask().
if (mRecentsView != null) {
+ ActiveGestureLog.INSTANCE.trackEvent(ActiveGestureErrorDetector.GestureEvent
+ .SET_ON_PAGE_TRANSITION_END_CALLBACK);
mRecentsView.setOnPageTransitionEndCallback(
() -> mGestureState.setState(STATE_RECENTS_SCROLLING_FINISHED));
} else {
@@ -1235,7 +1335,7 @@
protected abstract HomeAnimationFactory createHomeAnimationFactory(
ArrayList<IBinder> launchCookies, long duration, boolean isTargetTranslucent,
- boolean appCanEnterPip, RemoteAnimationTargetCompat runningTaskTarget);
+ boolean appCanEnterPip, RemoteAnimationTarget runningTaskTarget);
private final TaskStackChangeListener mActivityRestartListener = new TaskStackChangeListener() {
@Override
@@ -1257,7 +1357,8 @@
@UiThread
private void animateToProgressInternal(float start, float end, long duration,
Interpolator interpolator, GestureEndTarget target, PointF velocityPxPerMs) {
- maybeUpdateRecentsAttachedState();
+ maybeUpdateRecentsAttachedState(new ActiveGestureLog.CompoundString(
+ "animate to progress internal"));
// If we are transitioning to launcher, then listen for the activity to be restarted while
// the transition is in progress
@@ -1281,7 +1382,7 @@
if (mGestureState.getEndTarget() == HOME) {
getOrientationHandler().adjustFloatingIconStartVelocity(velocityPxPerMs);
- final RemoteAnimationTargetCompat runningTaskTarget = mRecentsAnimationTargets != null
+ final RemoteAnimationTarget runningTaskTarget = mRecentsAnimationTargets != null
? mRecentsAnimationTargets.findTask(mGestureState.getRunningTaskId())
: null;
final ArrayList<IBinder> cookies = runningTaskTarget != null
@@ -1391,7 +1492,7 @@
}
}
- private int calculateWindowRotation(RemoteAnimationTargetCompat runningTaskTarget,
+ private int calculateWindowRotation(RemoteAnimationTarget runningTaskTarget,
RecentsOrientedState orientationState) {
if (runningTaskTarget.rotationChange != 0
&& TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) {
@@ -1402,8 +1503,9 @@
}
}
+ @Nullable
private SwipePipToHomeAnimator createWindowAnimationToPip(HomeAnimationFactory homeAnimFactory,
- RemoteAnimationTargetCompat runningTaskTarget, float startProgress) {
+ RemoteAnimationTarget runningTaskTarget, float startProgress) {
// Directly animate the app to PiP (picture-in-picture) mode
final ActivityManager.RunningTaskInfo taskInfo = runningTaskTarget.taskInfo;
final RecentsOrientedState orientationState = mRemoteTargetHandles[0].getTaskViewSimulator()
@@ -1420,12 +1522,17 @@
homeToWindowPositionMap.invert(windowToHomePositionMap);
windowToHomePositionMap.mapRect(startRect);
+ final Rect hotseatKeepClearArea = getKeepClearAreaForHotseat();
final Rect destinationBounds = SystemUiProxy.INSTANCE.get(mContext)
.startSwipePipToHome(taskInfo.topActivity,
taskInfo.topActivityInfo,
runningTaskTarget.taskInfo.pictureInPictureParams,
homeRotation,
- mDp.hotseatBarSizePx);
+ hotseatKeepClearArea);
+ if (destinationBounds == null) {
+ // No destination bounds returned from SystemUI, bail early.
+ return null;
+ }
final Rect appBounds = new Rect();
final WindowConfiguration winConfig = taskInfo.configuration.windowConfiguration;
// Adjust the appBounds for TaskBar by using the calculated window crop Rect
@@ -1488,6 +1595,35 @@
return swipePipToHomeAnimator;
}
+ private Rect getKeepClearAreaForHotseat() {
+ Rect keepClearArea;
+ if (!ENABLE_PIP_KEEP_CLEAR_ALGORITHM) {
+ // make the height equal to hotseatBarSizePx only
+ keepClearArea = new Rect(0, 0, 0, mDp.hotseatBarSizePx);
+ return keepClearArea;
+ }
+ // the keep clear area in global screen coordinates, in pixels
+ if (mDp.isPhone) {
+ if (mDp.isSeascape()) {
+ // in seascape the Hotseat is on the left edge of the screen
+ keepClearArea = new Rect(0, 0, mDp.hotseatBarSizePx, mDp.heightPx);
+ } else if (mDp.isLandscape) {
+ // in landscape the Hotseat is on the right edge of the screen
+ keepClearArea = new Rect(mDp.widthPx - mDp.hotseatBarSizePx, 0,
+ mDp.widthPx, mDp.heightPx);
+ } else {
+ // in portrait mode the Hotseat is at the bottom of the screen
+ keepClearArea = new Rect(0, mDp.heightPx - mDp.hotseatBarSizePx,
+ mDp.widthPx, mDp.heightPx);
+ }
+ } else {
+ // large screens have Hotseat always at the bottom of the screen
+ keepClearArea = new Rect(0, mDp.heightPx - mDp.hotseatBarSizePx,
+ mDp.widthPx, mDp.heightPx);
+ }
+ return keepClearArea;
+ }
+
private void startInterceptingTouchesForGesture() {
if (mRecentsAnimationController == null) {
return;
@@ -1541,7 +1677,9 @@
mRecentsView.post(mRecentsView::resetTaskVisuals);
}
// Make sure recents is in its final state
- maybeUpdateRecentsAttachedState(false);
+ maybeUpdateRecentsAttachedState(
+ false, new ActiveGestureLog.CompoundString(
+ "setting up window animation (animate=false)"));
mActivityInterface.onSwipeUpToHomeComplete(mDeviceState);
}
});
@@ -1575,7 +1713,6 @@
private void resumeLastTask() {
if (mRecentsAnimationController != null) {
mRecentsAnimationController.finish(false /* toRecents */, null);
- ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", false);
}
doLogGesture(LAST_TASK, null);
reset();
@@ -1624,6 +1761,9 @@
* handler (in case of quick switch).
*/
private void cancelCurrentAnimation() {
+ ActiveGestureLog.INSTANCE.addLog(
+ "AbsSwipeUpHandler.cancelCurrentAnimation",
+ ActiveGestureErrorDetector.GestureEvent.CANCEL_CURRENT_ANIMATION);
mCanceled = true;
mCurrentShift.cancelAnimation();
@@ -1636,8 +1776,7 @@
}
private void invalidateHandler() {
- if (!ENABLE_QUICKSTEP_LIVE_TILE.get() || !mActivityInterface.isInLiveTileMode()
- || mGestureState.getEndTarget() != RECENTS) {
+ if (!mActivityInterface.isInLiveTileMode() || mGestureState.getEndTarget() != RECENTS) {
mInputConsumerProxy.destroy();
mTaskAnimationManager.setLiveTileCleanUpHandler(null);
}
@@ -1682,10 +1821,6 @@
* continued quick switch gesture, which cancels the previous handler but doesn't invalidate it.
*/
private void resetLauncherListeners() {
- // Reset the callback for deferred activity launches
- if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- mActivityInterface.setOnDeferredActivityLaunchCallback(null);
- }
mActivity.getRootView().setOnApplyWindowInsetsListener(null);
mRecentsView.removeOnScrollChangedListener(mOnRecentsScrollListener);
@@ -1695,10 +1830,6 @@
boolean wasVisible = mWasLauncherAlreadyVisible || mGestureStarted;
mActivityInterface.onTransitionCancelled(wasVisible, mGestureState.getEndTarget());
- if (mRecentsAnimationTargets != null && wasVisible) {
- setDividerShown(true /* shown */, true /* immediate */);
- }
-
// Leave the pending invisible flag, as it may be used by wallpaper open animation.
if (mActivity != null) {
mActivity.clearForceInvisibleFlag(INVISIBLE_BY_STATE_HANDLER);
@@ -1711,7 +1842,6 @@
mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
} else {
final int runningTaskId = mGestureState.getRunningTaskId();
- final boolean refreshView = !ENABLE_QUICKSTEP_LIVE_TILE.get() /* refreshView */;
boolean finishTransitionPosted = false;
if (mRecentsAnimationController != null) {
// Update the screenshot of the task
@@ -1720,16 +1850,27 @@
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]);
+ }
+ }
MAIN_EXECUTOR.execute(() -> {
mTaskSnapshot = taskSnapshot;
- if (!updateThumbnail(runningTaskId, refreshView)) {
+ if (!updateThumbnail(runningTaskId, false /* refreshView */)) {
setScreenshotCapturedState();
}
});
});
return;
}
- finishTransitionPosted = updateThumbnail(runningTaskId, refreshView);
+ finishTransitionPosted = updateThumbnail(runningTaskId, false /* refreshView */);
}
if (!finishTransitionPosted) {
setScreenshotCapturedState();
@@ -1767,19 +1908,19 @@
}
private void finishCurrentTransitionToRecents() {
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ if (mRecentsView != null
+ && mActivityInterface.getDesktopVisibilityController() != null
+ && mActivityInterface.getDesktopVisibilityController().areFreeformTasksVisible()) {
+ mRecentsView.switchToScreenshot(() -> {
+ mRecentsView.finishRecentsAnimation(true /* toRecents */, false /* shouldPip */,
+ () -> mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED));
+ });
+ } else {
mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED);
if (mRecentsAnimationController != null) {
mRecentsAnimationController.detachNavigationBarFromApp(true);
}
- } else if (!hasTargets() || mRecentsAnimationController == null) {
- // If there are no targets or the animation not started, then there is nothing to finish
- mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED);
- } else {
- mRecentsAnimationController.finish(true /* toRecents */,
- () -> mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED));
}
- ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", true);
}
private void finishCurrentTransitionToHome() {
@@ -1791,7 +1932,6 @@
finishRecentsControllerToHome(
() -> mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED));
}
- ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", true);
doLogGesture(HOME, mRecentsView == null ? null : mRecentsView.getCurrentPageTaskView());
}
@@ -1835,22 +1975,20 @@
}
endLauncherTransitionController();
mRecentsView.onSwipeUpAnimationSuccess();
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- mTaskAnimationManager.setLiveTileCleanUpHandler(() -> {
- mRecentsView.cleanupRemoteTargets();
- mInputConsumerProxy.destroy();
- });
- mTaskAnimationManager.enableLiveTileRestartListener();
- }
+ mTaskAnimationManager.setLiveTileCleanUpHandler(() -> {
+ mRecentsView.cleanupRemoteTargets();
+ mInputConsumerProxy.destroy();
+ });
+ mTaskAnimationManager.enableLiveTileRestartListener();
SystemUiProxy.INSTANCE.get(mContext).onOverviewShown(false, TAG);
doLogGesture(RECENTS, mRecentsView.getCurrentPageTaskView());
reset();
}
- private static boolean isNotInRecents(RemoteAnimationTargetCompat app) {
+ private static boolean isNotInRecents(RemoteAnimationTarget app) {
return app.isNotInRecents
- || app.activityType == ACTIVITY_TYPE_HOME;
+ || app.windowConfiguration.getActivityType() == ACTIVITY_TYPE_HOME;
}
/**
@@ -1904,6 +2042,9 @@
mGestureState.updateLastStartedTaskId(taskId);
boolean hasTaskPreviouslyAppeared = mGestureState.getPreviouslyAppearedTaskIds()
.contains(taskId);
+ if (!hasTaskPreviouslyAppeared) {
+ ActiveGestureLog.INSTANCE.trackEvent(EXPECTING_TASK_APPEARED);
+ }
nextTask.launchTask(success -> {
resultCallback.accept(success);
if (success) {
@@ -1973,17 +2114,59 @@
}
@Override
- public void onTasksAppeared(RemoteAnimationTargetCompat[] appearedTaskTargets) {
+ public void onTasksAppeared(RemoteAnimationTarget[] appearedTaskTargets) {
if (mRecentsAnimationController != null) {
if (handleTaskAppeared(appearedTaskTargets)) {
- mRecentsAnimationController.finish(false /* toRecents */,
- null /* onFinishComplete */);
- mActivityInterface.onLaunchTaskSuccess();
- ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", false);
+ Optional<RemoteAnimationTarget> taskTargetOptional =
+ Arrays.stream(appearedTaskTargets)
+ .filter(targetCompat ->
+ targetCompat.taskId == mGestureState.getLastStartedTaskId())
+ .findFirst();
+ if (!taskTargetOptional.isPresent()) {
+ finishRecentsAnimationOnTasksAppeared();
+ return;
+ }
+ RemoteAnimationTarget taskTarget = taskTargetOptional.get();
+ TaskView taskView = mRecentsView.getTaskViewByTaskId(taskTarget.taskId);
+ if (taskView == null || !taskView.getThumbnail().shouldShowSplashView()) {
+ finishRecentsAnimationOnTasksAppeared();
+ return;
+ }
+
+ ViewGroup splashView = mActivity.getDragLayer();
+
+ // When revealing the app with launcher splash screen, make the app visible
+ // and behind the splash view before the splash is animated away.
+ SurfaceTransactionApplier surfaceApplier =
+ new SurfaceTransactionApplier(splashView);
+ SurfaceTransaction transaction = new SurfaceTransaction();
+ for (RemoteAnimationTarget target : appearedTaskTargets) {
+ transaction.forSurface(target.leash).setAlpha(1).setLayer(-1);
+ }
+ surfaceApplier.scheduleApply(transaction);
+
+ SplashScreenExitAnimationUtils.startAnimations(splashView, taskTarget.leash,
+ mSplashMainWindowShiftLength, new TransactionPool(), new Rect(),
+ SPLASH_ANIMATION_DURATION, SPLASH_FADE_OUT_DURATION,
+ /* iconStartAlpha= */ 0, /* brandingStartAlpha= */ 0,
+ SPLASH_APP_REVEAL_DELAY, SPLASH_APP_REVEAL_DURATION,
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ finishRecentsAnimationOnTasksAppeared();
+ }
+ });
}
}
}
+ private void finishRecentsAnimationOnTasksAppeared() {
+ if (mRecentsAnimationController != null) {
+ mRecentsAnimationController.finish(false /* toRecents */, null /* onFinishComplete */);
+ }
+ ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", false);
+ }
+
/**
* @return The index of the TaskView in RecentsView whose taskId matches the task that will
* resume if we finish the controller.
@@ -2026,7 +2209,7 @@
AnimatorControllerWithResistance playbackController =
remoteHandle.getPlaybackController();
if (playbackController != null) {
- playbackController.setProgress(Math.max(mCurrentShift.value,
+ playbackController.setProgress(Math.max(getTaskbarProgress(),
getScaleProgressDueToScroll()), mDragLengthFactor);
}
@@ -2070,6 +2253,41 @@
return scaleProgress;
}
+ /**
+ * Updates the current status of taskbar during this swipe.
+ */
+ public void setTaskbarAlreadyOpen(boolean taskbarAlreadyOpen) {
+ mTaskbarAlreadyOpen = taskbarAlreadyOpen;
+ }
+
+ /**
+ * Overrides the current shift progress to keep the app window at the bottom of the screen
+ * while the transient taskbar is being swiped in.
+ *
+ * There is also a catch up period so that the window can start moving 1:1 with the swipe.
+ */
+ private float getTaskbarProgress() {
+ if (!DisplayController.isTransientTaskbar(mContext)) {
+ return mCurrentShift.value;
+ }
+
+ if (mTaskbarAlreadyOpen) {
+ return mCurrentShift.value;
+ }
+
+ if (mCurrentDisplacement < mTaskbarAppWindowThreshold) {
+ return 0;
+ }
+
+ // "Catch up" with `mCurrentShift.value`.
+ if (mCurrentDisplacement < mTaskbarCatchUpThreshold) {
+ return Utilities.mapToRange(mCurrentDisplacement, mTaskbarAppWindowThreshold,
+ mTaskbarCatchUpThreshold, 0, mCurrentShift.value, ACCEL_DEACCEL);
+ }
+
+ return mCurrentShift.value;
+ }
+
private void setDividerShown(boolean shown, boolean immediate) {
if (mDividerAnimator != null) {
mDividerAnimator.cancel();
diff --git a/quickstep/src/com/android/quickstep/AnimatedFloat.java b/quickstep/src/com/android/quickstep/AnimatedFloat.java
index a166553..5ab3c58 100644
--- a/quickstep/src/com/android/quickstep/AnimatedFloat.java
+++ b/quickstep/src/com/android/quickstep/AnimatedFloat.java
@@ -98,15 +98,6 @@
}
}
- /**
- * Starts the animation.
- */
- public void startAnimation() {
- if (mValueAnimator != null) {
- mValueAnimator.start();
- }
- }
-
public void cancelAnimation() {
if (mValueAnimator != null) {
mValueAnimator.cancel();
@@ -119,10 +110,6 @@
}
}
- public ObjectAnimator getCurrentAnimation() {
- return mValueAnimator;
- }
-
public boolean isAnimating() {
return mValueAnimator != null;
}
@@ -135,9 +122,9 @@
}
/**
- * Returns the value we are animating to, or {@code null} if we are not currently animating.
+ * Returns whether we are currently not animating, and the animation's value matches the given.
*/
- public Float getEndValue() {
- return mEndValue;
+ public boolean isSettledOnValue(float endValue) {
+ return !isAnimating() && value == endValue;
}
}
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index 6354282..2c3ba85 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -30,6 +30,7 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.annotation.TargetApi;
import android.content.Context;
@@ -40,6 +41,7 @@
import android.os.Build;
import android.view.Gravity;
import android.view.MotionEvent;
+import android.view.RemoteAnimationTarget;
import android.view.View;
import androidx.annotation.Nullable;
@@ -50,18 +52,19 @@
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.statehandlers.DepthController;
+import com.android.launcher3.statehandlers.DesktopVisibilityController;
import com.android.launcher3.statemanager.BaseState;
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.taskbar.TaskbarUIController;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.util.DisplayController;
-import com.android.launcher3.util.DisplayController.NavigationMode;
+import com.android.launcher3.util.NavigationMode;
import com.android.launcher3.views.ScrimView;
+import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.shared.recents.model.ThumbnailData;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.util.HashMap;
import java.util.Optional;
@@ -118,9 +121,6 @@
public abstract void onAssistantVisibilityChanged(float visibility);
- /** Called when one handed mode activated or deactivated. */
- public abstract void onOneHandedModeStateChanged(boolean activated);
-
public abstract AnimationFactory prepareRecentsUI(RecentsAnimationDeviceState deviceState,
boolean activityVisible, Consumer<AnimatorControllerWithResistance> callback);
@@ -141,6 +141,11 @@
}
@Nullable
+ public DesktopVisibilityController getDesktopVisibilityController() {
+ return null;
+ }
+
+ @Nullable
public abstract TaskbarUIController getTaskbarController();
public final boolean isResumed() {
@@ -161,7 +166,7 @@
public abstract boolean switchToRecentsIfVisible(Runnable onCompleteCallback);
public abstract Rect getOverviewWindowBounds(
- Rect homeBounds, RemoteAnimationTargetCompat target);
+ Rect homeBounds, RemoteAnimationTarget target);
public abstract boolean allowMinimizeSplitScreen();
@@ -183,19 +188,12 @@
public abstract void onLaunchTaskFailed();
- public void onLaunchTaskSuccess() {
- ACTIVITY_TYPE activity = getCreatedActivity();
- if (activity == null) {
- return;
- }
- activity.getStateManager().moveToRestState();
- }
-
/**
* Closes any overlays.
*/
public void closeOverlay() {
- Optional.ofNullable(getTaskbarController()).ifPresent(TaskbarUIController::hideAllApps);
+ Optional.ofNullable(getTaskbarController()).ifPresent(
+ TaskbarUIController::hideOverlayWindow);
}
public void switchRunningTaskViewToScreenshot(HashMap<Integer, ThumbnailData> thumbnailDatas,
@@ -401,14 +399,16 @@
public interface AnimationFactory {
- void createActivityInterface(long transitionLength);
+ void createActivityInterface(long transitionLength, ActiveGestureLog.CompoundString reason);
/**
* @param attached Whether to show RecentsView alongside the app window. If false, recents
* will be hidden by some property we can animate, e.g. alpha.
* @param animate Whether to animate recents to/from its new attached state.
+ * @param reason Explanation for why this method is being called with the given param values
*/
- default void setRecentsAttachedToAppWindow(boolean attached, boolean animate) { }
+ default void setRecentsAttachedToAppWindow(
+ boolean attached, boolean animate, ActiveGestureLog.CompoundString reason) { }
default boolean isRecentsAttachedToAppWindow() {
return false;
@@ -450,7 +450,8 @@
}
@Override
- public void createActivityInterface(long transitionLength) {
+ public void createActivityInterface(
+ long transitionLength, ActiveGestureLog.CompoundString reason) {
PendingAnimation pa = new PendingAnimation(transitionLength * 2);
createBackgroundToOverviewAnim(mActivity, pa);
AnimatorPlaybackController controller = pa.createPlaybackController();
@@ -473,42 +474,68 @@
// (because we set the animation as the current state animation), so we reapply the
// attached state here as well to ensure recents is shown/hidden appropriately.
if (DisplayController.getNavigationMode(mActivity) == NavigationMode.NO_BUTTON) {
- setRecentsAttachedToAppWindow(mIsAttachedToWindow, false);
+ setRecentsAttachedToAppWindow(
+ mIsAttachedToWindow,
+ false,
+ reason.append("; reapplying the attached state (attached=")
+ .append(Boolean.toString(mIsAttachedToWindow))
+ .append(", animate=false)"));
}
}
@Override
- public void setRecentsAttachedToAppWindow(boolean attached, boolean animate) {
+ public void setRecentsAttachedToAppWindow(
+ boolean attached, boolean animate, ActiveGestureLog.CompoundString reason) {
+ // TODO(b/244593270): remove these logs; too verbose
+ ActiveGestureLog.INSTANCE.addLog(
+ new ActiveGestureLog.CompoundString("setRecentsAttachedToAppWindow: attached=")
+ .append(Boolean.toString(attached))
+ .append(", animate=")
+ .append(Boolean.toString(animate))
+ .append(", reason=")
+ .append(reason));
if (mIsAttachedToWindow == attached && animate) {
+ ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
+ "setRecentsAttachedToAppWindow: exiting early"));
return;
}
- mIsAttachedToWindow = attached;
- RecentsView recentsView = mActivity.getOverviewPanel();
- if (attached) {
- mHasEverAttachedToWindow = true;
- }
- Animator fadeAnim = mActivity.getStateManager()
- .createStateElementAnimation(INDEX_RECENTS_FADE_ANIM, attached ? 1 : 0);
-
- float fromTranslation = attached ? 1 : 0;
- float toTranslation = attached ? 0 : 1;
+ mActivity.getStateManager()
+ .cancelStateElementAnimation(INDEX_RECENTS_FADE_ANIM);
mActivity.getStateManager()
.cancelStateElementAnimation(INDEX_RECENTS_TRANSLATE_X_ANIM);
- if (!recentsView.isShown() && animate) {
- ADJACENT_PAGE_HORIZONTAL_OFFSET.set(recentsView, fromTranslation);
- } else {
- fromTranslation = ADJACENT_PAGE_HORIZONTAL_OFFSET.get(recentsView);
- }
- if (!animate) {
- ADJACENT_PAGE_HORIZONTAL_OFFSET.set(recentsView, toTranslation);
- } else {
- mActivity.getStateManager().createStateElementAnimation(
- INDEX_RECENTS_TRANSLATE_X_ANIM,
- fromTranslation, toTranslation).start();
- }
+ AnimatorSet animatorSet = new AnimatorSet();
+ animatorSet.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ super.onAnimationStart(animation);
+ mIsAttachedToWindow = attached;
+ if (attached) {
+ mHasEverAttachedToWindow = true;
+ }
+ }});
+
+ 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.setDuration(animate ? RECENTS_ATTACH_DURATION : 0).start();
+ fadeAnim.setDuration(animationDuration);
+ animatorSet.play(fadeAnim);
+
+ float fromTranslation = ADJACENT_PAGE_HORIZONTAL_OFFSET.get(
+ mActivity.getOverviewPanel());
+ float toTranslation = attached ? 0 : 1;
+ ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
+ "setRecentsAttachedToAppWindow: fromTranslation=")
+ .append(Float.toString(fromTranslation))
+ .append(", toTranslation=")
+ .append(Float.toString(toTranslation)));
+
+ Animator translationAnimator = mActivity.getStateManager().createStateElementAnimation(
+ INDEX_RECENTS_TRANSLATE_X_ANIM, fromTranslation, toTranslation);
+ translationAnimator.setDuration(animationDuration);
+ animatorSet.play(translationAnimator);
+ animatorSet.start();
}
@Override
diff --git a/quickstep/src/com/android/quickstep/FallbackActivityInterface.java b/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
index ba61574..ae9fb0b 100644
--- a/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
@@ -15,8 +15,7 @@
*/
package com.android.quickstep;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
-import static com.android.launcher3.util.DisplayController.NavigationMode.NO_BUTTON;
+import static com.android.launcher3.util.NavigationMode.NO_BUTTON;
import static com.android.quickstep.fallback.RecentsState.BACKGROUND_APP;
import static com.android.quickstep.fallback.RecentsState.DEFAULT;
import static com.android.quickstep.fallback.RecentsState.HOME;
@@ -26,6 +25,7 @@
import android.content.Context;
import android.graphics.Rect;
import android.view.MotionEvent;
+import android.view.RemoteAnimationTarget;
import androidx.annotation.Nullable;
@@ -39,7 +39,6 @@
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.views.RecentsView;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -78,11 +77,6 @@
// set to zero prior to this class becoming active.
}
- @Override
- public void onOneHandedModeStateChanged(boolean activated) {
- // Do nothing for FallbackActivityInterface
- }
-
/** 6 */
@Override
public AnimationFactory prepareRecentsUI(RecentsAnimationDeviceState deviceState,
@@ -120,8 +114,7 @@
public RecentsView getVisibleRecentsView() {
RecentsActivity activity = getCreatedActivity();
if (activity != null) {
- if (activity.hasBeenResumed()
- || (ENABLE_QUICKSTEP_LIVE_TILE.get() && isInLiveTileMode())) {
+ if (activity.hasBeenResumed() || isInLiveTileMode()) {
return activity.getOverviewPanel();
}
}
@@ -134,7 +127,7 @@
}
@Override
- public Rect getOverviewWindowBounds(Rect homeBounds, RemoteAnimationTargetCompat target) {
+ public Rect getOverviewWindowBounds(Rect homeBounds, RemoteAnimationTarget target) {
// TODO: Remove this once b/77875376 is fixed
return target.screenSpaceBounds;
}
diff --git a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
index 99f7bdd..374b839 100644
--- a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -15,6 +15,7 @@
*/
package com.android.quickstep;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.content.Intent.EXTRA_COMPONENT_NAME;
import static android.content.Intent.EXTRA_USER;
@@ -26,7 +27,6 @@
import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
import static com.android.launcher3.anim.Interpolators.ACCEL;
import static com.android.quickstep.OverviewComponentObserver.startHomeIntentSafely;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
import android.animation.ObjectAnimator;
import android.annotation.TargetApi;
@@ -48,6 +48,7 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Log;
+import android.view.RemoteAnimationTarget;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
@@ -65,12 +66,11 @@
import com.android.quickstep.fallback.FallbackRecentsView;
import com.android.quickstep.fallback.RecentsState;
import com.android.quickstep.util.RectFSpringAnim;
+import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties;
import com.android.quickstep.util.TransformParams;
import com.android.quickstep.util.TransformParams.BuilderProxy;
import com.android.systemui.shared.recents.model.Task.TaskKey;
import com.android.systemui.shared.system.InputConsumerController;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -125,24 +125,24 @@
}
}
- private void updateHomeActivityTransformDuringSwipeUp(SurfaceParams.Builder builder,
- RemoteAnimationTargetCompat app, TransformParams params) {
+ private void updateHomeActivityTransformDuringSwipeUp(SurfaceProperties builder,
+ RemoteAnimationTarget app, TransformParams params) {
setHomeScaleAndAlpha(builder, app, mCurrentShift.value,
Utilities.boundToRange(1 - mCurrentShift.value, 0, 1));
}
- private void setHomeScaleAndAlpha(SurfaceParams.Builder builder,
- RemoteAnimationTargetCompat app, float verticalShift, float alpha) {
+ private void setHomeScaleAndAlpha(SurfaceProperties builder,
+ RemoteAnimationTarget app, float verticalShift, float alpha) {
float scale = Utilities.mapRange(verticalShift, 1, mMaxLauncherScale);
mTmpMatrix.setScale(scale, scale,
app.localBounds.exactCenterX(), app.localBounds.exactCenterY());
- builder.withMatrix(mTmpMatrix).withAlpha(alpha);
+ builder.setMatrix(mTmpMatrix).setAlpha(alpha);
}
@Override
protected HomeAnimationFactory createHomeAnimationFactory(ArrayList<IBinder> launchCookies,
long duration, boolean isTargetTranslucent, boolean appCanEnterPip,
- RemoteAnimationTargetCompat runningTaskTarget) {
+ RemoteAnimationTarget runningTaskTarget) {
mAppCanEnterPip = appCanEnterPip;
if (appCanEnterPip) {
return new FallbackPipToHomeAnimationFactory();
@@ -154,7 +154,7 @@
private void startHomeIntent(
@Nullable FallbackHomeAnimationFactory gestureContractAnimationFactory,
- @Nullable RemoteAnimationTargetCompat runningTaskTarget) {
+ @Nullable RemoteAnimationTarget runningTaskTarget) {
ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext, 0, 0);
Intent intent = new Intent(mGestureState.getHomeIntent());
if (gestureContractAnimationFactory != null && runningTaskTarget != null) {
@@ -164,7 +164,7 @@
}
@Override
- protected boolean handleTaskAppeared(RemoteAnimationTargetCompat[] appearedTaskTarget) {
+ protected boolean handleTaskAppeared(RemoteAnimationTarget[] appearedTaskTarget) {
if (mActiveAnimationFactory != null
&& mActiveAnimationFactory.handleHomeTaskAppeared(appearedTaskTarget)) {
mActiveAnimationFactory = null;
@@ -279,13 +279,13 @@
return mTargetRect;
}
- private void updateRecentsActivityTransformDuringHomeAnim(SurfaceParams.Builder builder,
- RemoteAnimationTargetCompat app, TransformParams params) {
- builder.withAlpha(mRecentsAlpha.value);
+ private void updateRecentsActivityTransformDuringHomeAnim(SurfaceProperties builder,
+ RemoteAnimationTarget app, TransformParams params) {
+ builder.setAlpha(mRecentsAlpha.value);
}
- private void updateHomeActivityTransformDuringHomeAnim(SurfaceParams.Builder builder,
- RemoteAnimationTargetCompat app, TransformParams params) {
+ private void updateHomeActivityTransformDuringHomeAnim(SurfaceProperties builder,
+ RemoteAnimationTarget app, TransformParams params) {
setHomeScaleAndAlpha(builder, app, mVerticalShiftForScale.value, mHomeAlpha.value);
}
@@ -304,12 +304,12 @@
}
}
- public boolean handleHomeTaskAppeared(RemoteAnimationTargetCompat[] appearedTaskTargets) {
- RemoteAnimationTargetCompat appearedTaskTarget = appearedTaskTargets[0];
- if (appearedTaskTarget.activityType == ACTIVITY_TYPE_HOME) {
+ public boolean handleHomeTaskAppeared(RemoteAnimationTarget[] appearedTaskTargets) {
+ RemoteAnimationTarget appearedTaskTarget = appearedTaskTargets[0];
+ if (appearedTaskTarget.windowConfiguration.getActivityType() == ACTIVITY_TYPE_HOME) {
RemoteAnimationTargets targets = new RemoteAnimationTargets(
- new RemoteAnimationTargetCompat[] {appearedTaskTarget},
- new RemoteAnimationTargetCompat[0], new RemoteAnimationTargetCompat[0],
+ new RemoteAnimationTarget[] {appearedTaskTarget},
+ new RemoteAnimationTarget[0], new RemoteAnimationTarget[0],
appearedTaskTarget.mode);
mHomeAlphaParams.setTargetSet(targets);
updateHomeAlpha();
diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
index acdbbbd..31b78b3 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -19,20 +19,24 @@
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.SET_END_TARGET;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.SET_END_TARGET_HOME;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.SET_END_TARGET_NEW_TASK;
import android.annotation.Nullable;
import android.annotation.TargetApi;
import android.content.Intent;
import android.os.Build;
+import android.view.RemoteAnimationTarget;
import com.android.launcher3.statemanager.BaseState;
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.tracing.GestureStateProto;
import com.android.launcher3.tracing.SwipeHandlerProto;
import com.android.quickstep.TopTaskTracker.CachedTaskInfo;
+import com.android.quickstep.util.ActiveGestureErrorDetector;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.systemui.shared.recents.model.ThumbnailData;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -138,7 +142,7 @@
private CachedTaskInfo mRunningTask;
private GestureEndTarget mEndTarget;
- private RemoteAnimationTargetCompat mLastAppearedTaskTarget;
+ private RemoteAnimationTarget mLastAppearedTaskTarget;
private Set<Integer> mPreviouslyAppearedTaskIds = new HashSet<>();
private int mLastStartedTaskId = -1;
private RecentsAnimationController mRecentsAnimationController;
@@ -153,7 +157,8 @@
mHomeIntent = componentObserver.getHomeIntent();
mOverviewIntent = componentObserver.getOverviewIntent();
mActivityInterface = componentObserver.getActivityInterface();
- mStateCallback = new MultiStateCallback(STATE_NAMES.toArray(new String[0]));
+ mStateCallback = new MultiStateCallback(
+ STATE_NAMES.toArray(new String[0]), GestureState::getTrackedEventForState);
mGestureId = gestureId;
}
@@ -175,10 +180,23 @@
mHomeIntent = new Intent();
mOverviewIntent = new Intent();
mActivityInterface = null;
- mStateCallback = new MultiStateCallback(STATE_NAMES.toArray(new String[0]));
+ mStateCallback = new MultiStateCallback(
+ STATE_NAMES.toArray(new String[0]), GestureState::getTrackedEventForState);
mGestureId = -1;
}
+ @Nullable
+ private static ActiveGestureErrorDetector.GestureEvent getTrackedEventForState(int stateFlag) {
+ if (stateFlag == STATE_END_TARGET_ANIMATION_FINISHED) {
+ return ActiveGestureErrorDetector.GestureEvent.STATE_END_TARGET_ANIMATION_FINISHED;
+ } else if (stateFlag == STATE_RECENTS_SCROLLING_FINISHED) {
+ return ActiveGestureErrorDetector.GestureEvent.STATE_RECENTS_SCROLLING_FINISHED;
+ } else if (stateFlag == STATE_RECENTS_ANIMATION_CANCELED) {
+ return ActiveGestureErrorDetector.GestureEvent.STATE_RECENTS_ANIMATION_CANCELED;
+ }
+ return null;
+ }
+
/**
* @return whether the gesture state has the provided {@param stateMask} flags set.
*/
@@ -253,7 +271,7 @@
/**
* Updates the last task that appeared during this gesture.
*/
- public void updateLastAppearedTaskTarget(RemoteAnimationTargetCompat lastAppearedTaskTarget) {
+ public void updateLastAppearedTaskTarget(RemoteAnimationTarget lastAppearedTaskTarget) {
mLastAppearedTaskTarget = lastAppearedTaskTarget;
if (lastAppearedTaskTarget != null) {
mPreviouslyAppearedTaskIds.add(lastAppearedTaskTarget.taskId);
@@ -311,7 +329,21 @@
public void setEndTarget(GestureEndTarget target, boolean isAtomic) {
mEndTarget = target;
mStateCallback.setState(STATE_END_TARGET_SET);
- ActiveGestureLog.INSTANCE.addLog("setEndTarget " + mEndTarget);
+ ActiveGestureLog.INSTANCE.addLog(
+ /* event= */ "setEndTarget " + mEndTarget,
+ /* gestureEvent= */ SET_END_TARGET);
+ switch (mEndTarget) {
+ case HOME:
+ ActiveGestureLog.INSTANCE.trackEvent(SET_END_TARGET_HOME);
+ break;
+ case NEW_TASK:
+ ActiveGestureLog.INSTANCE.trackEvent(SET_END_TARGET_NEW_TASK);
+ break;
+ case LAST_TASK:
+ case RECENTS:
+ default:
+ // No-Op
+ }
if (isAtomic) {
mStateCallback.setState(STATE_END_TARGET_ANIMATION_FINISHED);
}
diff --git a/quickstep/src/com/android/quickstep/KtR.java b/quickstep/src/com/android/quickstep/KtR.java
deleted file mode 100644
index 758c6e0..0000000
--- a/quickstep/src/com/android/quickstep/KtR.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.quickstep;
-
-import com.android.launcher3.R;
-
-/**
- * Bridge class to allow using resources in Kotlin.
- * <br/>
- * TODO(b/204069723) Can't use resources directly in Kotlin
- */
-public class KtR {
- public static final class id {
- public static int menu_option_layout = R.id.menu_option_layout;
- }
-
- public static final class dimen {
- public static int task_menu_spacing = R.dimen.task_menu_spacing;
- public static int task_menu_horizontal_padding = R.dimen.task_menu_horizontal_padding;
- public static int taskbar_ime_size = R.dimen.taskbar_ime_size;
- }
-
- public static final class layout {
- public static int task_menu_with_arrow = R.layout.task_menu_with_arrow;
- public static int task_view_menu_option = R.layout.task_view_menu_option;
- }
-}
diff --git a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
index c13b95f..9ff9416 100644
--- a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
@@ -21,38 +21,39 @@
import static com.android.launcher3.LauncherState.QUICK_SWITCH;
import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.content.Context;
import android.graphics.Rect;
import android.view.MotionEvent;
+import android.view.RemoteAnimationTarget;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
-import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAnimUtils;
import com.android.launcher3.LauncherInitListener;
import com.android.launcher3.LauncherState;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.statehandlers.DepthController;
-import com.android.launcher3.statehandlers.DepthController.ClampedDepthProperty;
+import com.android.launcher3.statehandlers.DesktopVisibilityController;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.taskbar.LauncherTaskbarUIController;
import com.android.launcher3.touch.PagedOrientationHandler;
+import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.launcher3.util.DisplayController;
-import com.android.launcher3.util.DisplayController.NavigationMode;
+import com.android.launcher3.util.NavigationMode;
import com.android.quickstep.GestureState.GestureEndTarget;
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.plugins.shared.LauncherOverlayManager;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -61,7 +62,7 @@
* {@link BaseActivityInterface} for the in-launcher recents.
*/
public final class LauncherActivityInterface extends
- BaseActivityInterface<LauncherState, BaseQuickstepLauncher> {
+ BaseActivityInterface<LauncherState, QuickstepLauncher> {
public static final LauncherActivityInterface INSTANCE = new LauncherActivityInterface();
@@ -108,34 +109,26 @@
}
@Override
- public void onOneHandedModeStateChanged(boolean activated) {
- Launcher launcher = getCreatedActivity();
- if (launcher == null) {
- return;
- }
- launcher.onOneHandedStateChanged(activated);
- }
-
- @Override
public AnimationFactory prepareRecentsUI(RecentsAnimationDeviceState deviceState,
boolean activityVisible, Consumer<AnimatorControllerWithResistance> callback) {
notifyRecentsOfOrientation(deviceState.getRotationTouchHelper());
DefaultAnimationFactory factory = new DefaultAnimationFactory(callback) {
@Override
- protected void createBackgroundToOverviewAnim(BaseQuickstepLauncher activity,
+ protected void createBackgroundToOverviewAnim(QuickstepLauncher activity,
PendingAnimation pa) {
super.createBackgroundToOverviewAnim(activity, pa);
// Animate the blur and wallpaper zoom
float fromDepthRatio = BACKGROUND_APP.getDepth(activity);
float toDepthRatio = OVERVIEW.getDepth(activity);
- pa.addFloat(getDepthController(),
- new ClampedDepthProperty(fromDepthRatio, toDepthRatio),
+ pa.addFloat(getDepthController().stateDepth,
+ new LauncherAnimUtils.ClampedProperty<>(
+ MULTI_PROPERTY_VALUE, fromDepthRatio, toDepthRatio),
fromDepthRatio, toDepthRatio, LINEAR);
}
};
- BaseQuickstepLauncher launcher = factory.initBackgroundStateUI();
+ QuickstepLauncher launcher = factory.initBackgroundStateUI();
// Since all apps is not visible, we can safely reset the scroll position.
// This ensures then the next swipe up to all-apps starts from scroll 0.
launcher.getAppsView().reset(false /* animate */);
@@ -159,14 +152,14 @@
@Nullable
@Override
- public BaseQuickstepLauncher getCreatedActivity() {
- return BaseQuickstepLauncher.ACTIVITY_TRACKER.getCreatedActivity();
+ public QuickstepLauncher getCreatedActivity() {
+ return QuickstepLauncher.ACTIVITY_TRACKER.getCreatedActivity();
}
@Nullable
@Override
public DepthController getDepthController() {
- BaseQuickstepLauncher launcher = getCreatedActivity();
+ QuickstepLauncher launcher = getCreatedActivity();
if (launcher == null) {
return null;
}
@@ -175,8 +168,18 @@
@Nullable
@Override
+ public DesktopVisibilityController getDesktopVisibilityController() {
+ QuickstepLauncher launcher = getCreatedActivity();
+ if (launcher == null) {
+ return null;
+ }
+ return launcher.getDesktopVisibilityController();
+ }
+
+ @Nullable
+ @Override
public LauncherTaskbarUIController getTaskbarController() {
- BaseQuickstepLauncher launcher = getCreatedActivity();
+ QuickstepLauncher launcher = getCreatedActivity();
if (launcher == null) {
return null;
}
@@ -203,8 +206,7 @@
private Launcher getVisibleLauncher() {
Launcher launcher = getCreatedActivity();
return (launcher != null) && launcher.isStarted()
- && ((ENABLE_QUICKSTEP_LIVE_TILE.get() && isInLiveTileMode())
- || launcher.hasBeenResumed()) ? launcher : null;
+ && (isInLiveTileMode() || launcher.hasBeenResumed()) ? launcher : null;
}
@Override
@@ -213,7 +215,7 @@
if (launcher == null) {
return false;
}
- if (ENABLE_QUICKSTEP_LIVE_TILE.get() && isInLiveTileMode()) {
+ if (isInLiveTileMode()) {
RecentsView recentsView = getVisibleRecentsView();
if (recentsView == null) {
return false;
@@ -253,7 +255,7 @@
}
@Override
- public Rect getOverviewWindowBounds(Rect homeBounds, RemoteAnimationTargetCompat target) {
+ public Rect getOverviewWindowBounds(Rect homeBounds, RemoteAnimationTarget target) {
return homeBounds;
}
@@ -291,10 +293,6 @@
} else {
om.hideOverlay(150);
}
- LauncherTaskbarUIController taskbarController = getTaskbarController();
- if (taskbarController != null) {
- taskbarController.hideEdu();
- }
}
@Override
@@ -318,7 +316,7 @@
}
@Override
- protected int getOverviewScrimColorForState(BaseQuickstepLauncher launcher,
+ protected int getOverviewScrimColorForState(QuickstepLauncher launcher,
LauncherState state) {
return state.getWorkspaceScrimColor(launcher);
}
diff --git a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
index fd9f922..2741751 100644
--- a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
+++ b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
@@ -36,17 +36,16 @@
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.window.BackEvent;
+import android.window.BackProgressAnimator;
import android.window.IOnBackInvokedCallback;
import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.QuickstepTransitionManager;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.quickstep.util.RectFSpringAnim;
import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
/**
* Controls the animation of swiping back and returning to launcher.
@@ -75,7 +74,7 @@
private final RectF mCancelRect = new RectF();
/** The current window position. */
private final RectF mCurrentRect = new RectF();
- private final BaseQuickstepLauncher mLauncher;
+ private final QuickstepLauncher mLauncher;
private final int mWindowScaleMarginX;
/** Max window translation in the Y axis. */
private final int mWindowMaxDeltaY;
@@ -84,16 +83,17 @@
private final Interpolator mCancelInterpolator;
private final PointF mInitialTouchPos = new PointF();
- private RemoteAnimationTargetCompat mBackTarget;
+ private RemoteAnimationTarget mBackTarget;
private SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
private boolean mSpringAnimationInProgress = false;
private boolean mAnimatorSetInProgress = false;
private float mBackProgress = 0;
private boolean mBackInProgress = false;
private IOnBackInvokedCallback mBackCallback;
+ private BackProgressAnimator mProgressAnimator = new BackProgressAnimator();
public LauncherBackAnimationController(
- BaseQuickstepLauncher launcher,
+ QuickstepLauncher launcher,
QuickstepTransitionManager quickstepTransitionManager) {
mLauncher = launcher;
mQuickstepTransitionManager = quickstepTransitionManager;
@@ -119,30 +119,41 @@
mBackCallback = new IOnBackInvokedCallback.Stub() {
@Override
public void onBackCancelled() {
- handler.post(() -> resetPositionAnimated());
+ handler.post(() -> {
+ resetPositionAnimated();
+ mProgressAnimator.reset();
+ });
}
@Override
public void onBackInvoked() {
- handler.post(() -> startTransition());
+ handler.post(() -> {
+ startTransition();
+ mProgressAnimator.reset();
+ });
}
@Override
public void onBackProgressed(BackEvent backEvent) {
- mBackProgress = backEvent.getProgress();
- // TODO: Update once the interpolation curve spec is finalized.
- mBackProgress =
- 1 - (1 - mBackProgress) * (1 - mBackProgress) * (1
- - mBackProgress);
- if (!mBackInProgress) {
- startBack(backEvent);
- } else {
- updateBackProgress(mBackProgress, backEvent);
- }
+ handler.post(() -> {
+ mProgressAnimator.onBackProgressed(backEvent);
+ });
}
@Override
- public void onBackStarted() { }
+ public void onBackStarted(BackEvent backEvent) {
+ handler.post(() -> {
+ startBack(backEvent);
+ mProgressAnimator.onBackStarted(backEvent, event -> {
+ mBackProgress = event.getProgress();
+ // TODO: Update once the interpolation curve spec is finalized.
+ mBackProgress =
+ 1 - (1 - mBackProgress) * (1 - mBackProgress) * (1
+ - mBackProgress);
+ updateBackProgress(mBackProgress, event);
+ });
+ });
+ }
};
SystemUiProxy.INSTANCE.get(mLauncher).setBackToLauncherCallback(mBackCallback);
}
@@ -170,6 +181,7 @@
if (mBackCallback != null) {
SystemUiProxy.INSTANCE.get(mLauncher).clearBackToLauncherCallback(mBackCallback);
}
+ mProgressAnimator.reset();
mBackCallback = null;
}
@@ -183,33 +195,25 @@
mTransaction.show(appTarget.leash).apply();
mTransaction.setAnimationTransaction();
- mBackTarget = new RemoteAnimationTargetCompat(appTarget);
+ mBackTarget = appTarget;
mInitialTouchPos.set(backEvent.getTouchX(), backEvent.getTouchY());
// TODO(b/218916755): Offset start rectangle in multiwindow mode.
mStartRect.set(mBackTarget.windowConfiguration.getMaxBounds());
+ mCurrentRect.set(mStartRect);
}
private void updateBackProgress(float progress, BackEvent event) {
- if (mBackTarget == null) {
+ if (!mBackInProgress || mBackTarget == null) {
return;
}
float screenWidth = mStartRect.width();
float screenHeight = mStartRect.height();
- float dX = Math.abs(event.getTouchX() - mInitialTouchPos.x);
- // The 'follow width' is the width of the window if it completely matches
- // the gesture displacement.
- float followWidth = screenWidth - dX;
- // The 'progress width' is the width of the window if it strictly linearly interpolates
- // to minimum scale base on progress.
- float progressWidth = Utilities.mapRange(progress, 1, MIN_WINDOW_SCALE) * screenWidth;
- // The final width is derived from interpolating between the follow with and progress width
- // using gesture progress.
- float width = Utilities.mapRange(progress, followWidth, progressWidth);
+ float width = Utilities.mapRange(progress, 1, MIN_WINDOW_SCALE) * screenWidth;
float height = screenHeight / screenWidth * width;
float deltaYRatio = (event.getTouchY() - mInitialTouchPos.y) / screenHeight;
// Base the window movement in the Y axis on the touch movement in the Y axis.
- float deltaY = (float) Math.sin(deltaYRatio * Math.PI * 0.5f) * mWindowMaxDeltaY;
+ float deltaY = (float) Math.sin(deltaYRatio * Math.PI * 0.5f) * mWindowMaxDeltaY * progress;
// Move the window along the Y axis.
float top = (screenHeight - height) * 0.5f + deltaY;
// Move the window along the X axis.
@@ -242,20 +246,17 @@
/** Transform the target window to match the target rect. */
private void applyTransform(RectF targetRect, float cornerRadius) {
- SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder builder =
- new SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder(mBackTarget.leash);
final float scale = targetRect.width() / mStartRect.width();
mTransformMatrix.reset();
mTransformMatrix.setScale(scale, scale);
mTransformMatrix.postTranslate(targetRect.left, targetRect.top);
- builder.withMatrix(mTransformMatrix)
- .withWindowCrop(mStartRect)
- .withCornerRadius(cornerRadius);
- SyncRtSurfaceTransactionApplierCompat.SurfaceParams surfaceParams = builder.build();
- if (surfaceParams.surface.isValid()) {
- surfaceParams.applyTo(mTransaction);
+ if (mBackTarget.leash.isValid()) {
+ mTransaction.setMatrix(mBackTarget.leash, mTransformMatrix, new float[9]);
+ mTransaction.setWindowCrop(mBackTarget.leash, mStartRect);
+ mTransaction.setCornerRadius(mBackTarget.leash, cornerRadius);
}
+
mTransaction.apply();
}
@@ -284,8 +285,8 @@
mBackProgress, mWindowScaleStartCornerRadius, mWindowScaleEndCornerRadius);
Pair<RectFSpringAnim, AnimatorSet> pair =
mQuickstepTransitionManager.createWallpaperOpenAnimations(
- new RemoteAnimationTargetCompat[]{mBackTarget},
- new RemoteAnimationTargetCompat[]{},
+ new RemoteAnimationTarget[]{mBackTarget},
+ new RemoteAnimationTarget[0],
false /* fromUnlock */,
mCurrentRect,
cornerRadius);
diff --git a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
index 196a664..bb781c8 100644
--- a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
+++ b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
@@ -30,15 +30,16 @@
import android.os.IBinder;
import android.os.UserHandle;
import android.util.Size;
+import android.view.RemoteAnimationTarget;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.states.StateAnimationConfig;
+import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.launcher3.util.ObjectWrapper;
import com.android.launcher3.views.FloatingIconView;
import com.android.launcher3.views.FloatingView;
@@ -49,7 +50,6 @@
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.system.InputConsumerController;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.util.ArrayList;
@@ -57,7 +57,7 @@
* Temporary class to allow easier refactoring
*/
public class LauncherSwipeHandlerV2 extends
- AbsSwipeUpHandler<BaseQuickstepLauncher, RecentsView, LauncherState> {
+ AbsSwipeUpHandler<QuickstepLauncher, RecentsView, LauncherState> {
public LauncherSwipeHandlerV2(Context context, RecentsAnimationDeviceState deviceState,
TaskAnimationManager taskAnimationManager, GestureState gestureState, long touchTimeMs,
@@ -70,7 +70,7 @@
@Override
protected HomeAnimationFactory createHomeAnimationFactory(ArrayList<IBinder> launchCookies,
long duration, boolean isTargetTranslucent, boolean appCanEnterPip,
- RemoteAnimationTargetCompat runningTaskTarget) {
+ RemoteAnimationTarget runningTaskTarget) {
if (mActivity == null) {
mStateCallback.addChangeListener(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED,
isPresent -> mRecentsView.startHome());
@@ -84,7 +84,8 @@
final View workspaceView = findWorkspaceView(launchCookies,
mRecentsView.getRunningTaskView());
- boolean canUseWorkspaceView = workspaceView != null && workspaceView.isAttachedToWindow();
+ boolean canUseWorkspaceView = workspaceView != null && workspaceView.isAttachedToWindow()
+ && workspaceView.getHeight() > 0;
mActivity.getRootView().setForceHideBackArrow(true);
if (!TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) {
@@ -143,7 +144,7 @@
private HomeAnimationFactory createWidgetHomeAnimationFactory(
LauncherAppWidgetHostView hostView, boolean isTargetTranslucent,
- RemoteAnimationTargetCompat runningTaskTarget) {
+ RemoteAnimationTarget runningTaskTarget) {
final float floatingWidgetAlpha = isTargetTranslucent ? 0 : 1;
RectF backgroundLocation = new RectF();
Rect crop = new Rect();
diff --git a/quickstep/src/com/android/quickstep/MultiStateCallback.java b/quickstep/src/com/android/quickstep/MultiStateCallback.java
index b3875ae..a68bea2 100644
--- a/quickstep/src/com/android/quickstep/MultiStateCallback.java
+++ b/quickstep/src/com/android/quickstep/MultiStateCallback.java
@@ -22,7 +22,12 @@
import android.util.Log;
import android.util.SparseArray;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.config.FeatureFlags;
+import com.android.quickstep.util.ActiveGestureErrorDetector;
+import com.android.quickstep.util.ActiveGestureLog;
import java.util.ArrayList;
import java.util.LinkedList;
@@ -41,17 +46,29 @@
private final SparseArray<ArrayList<Consumer<Boolean>>> mStateChangeListeners =
new SparseArray<>();
+ @NonNull private final TrackedEventsMapper mTrackedEventsMapper;
+
private final String[] mStateNames;
private int mState = 0;
public MultiStateCallback(String[] stateNames) {
+ this(stateNames, stateFlag -> null);
+ }
+
+ public MultiStateCallback(
+ String[] stateNames,
+ @NonNull TrackedEventsMapper trackedEventsMapper) {
mStateNames = DEBUG_STATES ? stateNames : null;
+ mTrackedEventsMapper = trackedEventsMapper;
}
/**
* Adds the provided state flags to the global state on the UI thread and executes any callbacks
* as a result.
+ *
+ * Also tracks the provided gesture events for error detection. Each provided event must be
+ * associated with one provided state flag.
*/
public void setStateOnUiThread(int stateFlag) {
if (Looper.myLooper() == Looper.getMainLooper()) {
@@ -69,7 +86,9 @@
Log.d(TAG, "[" + System.identityHashCode(this) + "] Adding "
+ convertToFlagNames(stateFlag) + " to " + convertToFlagNames(mState));
}
-
+ if (FeatureFlags.ENABLE_GESTURE_ERROR_DETECTION.get()) {
+ trackGestureEvents(stateFlag);
+ }
final int oldState = mState;
mState = mState | stateFlag;
@@ -87,6 +106,26 @@
notifyStateChangeListeners(oldState);
}
+ private void trackGestureEvents(int stateFlags) {
+ for (int index = 0; (stateFlags >> index) != 0; index++) {
+ if ((stateFlags & (1 << index)) == 0) {
+ continue;
+ }
+ ActiveGestureErrorDetector.GestureEvent gestureEvent =
+ mTrackedEventsMapper.getTrackedEventForState(1 << index);
+ if (gestureEvent == null) {
+ continue;
+ }
+ if (gestureEvent.mLogEvent && gestureEvent.mTrackEvent) {
+ ActiveGestureLog.INSTANCE.addLog(gestureEvent.name(), gestureEvent);
+ } else if (gestureEvent.mLogEvent) {
+ ActiveGestureLog.INSTANCE.addLog(gestureEvent.name());
+ } else if (gestureEvent.mTrackEvent) {
+ ActiveGestureLog.INSTANCE.trackEvent(gestureEvent);
+ }
+ }
+ }
+
/**
* Adds the provided state flags to the global state and executes any change handlers
* as a result.
@@ -174,4 +213,7 @@
return joiner.toString();
}
+ public interface TrackedEventsMapper {
+ @Nullable ActiveGestureErrorDetector.GestureEvent getTrackedEventForState(int stateflag);
+ }
}
diff --git a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
index 47c5dd0..1b05fd2 100644
--- a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
+++ b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
@@ -34,7 +34,7 @@
import com.android.launcher3.R;
import com.android.launcher3.testing.shared.ResourceUtils;
import com.android.launcher3.util.DisplayController.Info;
-import com.android.launcher3.util.DisplayController.NavigationMode;
+import com.android.launcher3.util.NavigationMode;
import com.android.launcher3.util.window.CachedDisplayInfo;
import java.io.PrintWriter;
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index 1e7e89e..5a09e02 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -116,7 +116,7 @@
*/
@BinderThread
public void addCommand(int type) {
- if (mPendingCommands.size() > MAX_QUEUE_SIZE) {
+ if (mPendingCommands.size() >= MAX_QUEUE_SIZE) {
return;
}
CommandInfo cmd = new CommandInfo(type);
@@ -144,7 +144,7 @@
RunnableList callbackList = null;
if (taskView != null) {
taskView.setEndQuickswitchCuj(true);
- callbackList = taskView.launchTaskAnimated();
+ callbackList = taskView.launchTasks();
}
if (callbackList != null) {
@@ -193,7 +193,20 @@
}
}
- if (activityInterface.switchToRecentsIfVisible(() -> scheduleNextTask(cmd))) {
+ final Runnable completeCallback = () -> {
+ if (cmd.type == TYPE_SHOW_NEXT_FOCUS) {
+ RecentsView rv = activityInterface.getVisibleRecentsView();
+ // When the overview is launched via alt tab (cmd type is TYPE_SHOW_NEXT_FOCUS),
+ // the touch mode somehow is not change to false by the Android framework.
+ // The subsequent tab to go through tasks in overview can only be dispatched to
+ // focuses views, while focus can only be requested in
+ // {@link View#requestFocusNoSearch(int, Rect)} when touch mode is false. To note,
+ // here we launch overview from home.
+ rv.getViewRootImpl().touchModeChanged(false);
+ }
+ scheduleNextTask(cmd);
+ };
+ if (activityInterface.switchToRecentsIfVisible(completeCallback)) {
// If successfully switched, wait until animation finishes
return false;
}
@@ -227,8 +240,11 @@
interactionHandler.onGestureCancelled();
cmd.removeListener(this);
- RecentsView createdRecents =
- activityInterface.getCreatedActivity().getOverviewPanel();
+ T createdActivity = activityInterface.getCreatedActivity();
+ if (createdActivity == null) {
+ return;
+ }
+ RecentsView createdRecents = createdActivity.getOverviewPanel();
if (createdRecents != null) {
createdRecents.onRecentsAnimationComplete();
}
@@ -268,6 +284,13 @@
RecentsView rv =
mOverviewComponentObserver.getActivityInterface().getVisibleRecentsView();
if (rv != null) {
+ // When the overview is launched via alt tab (cmd type is TYPE_SHOW_NEXT_FOCUS),
+ // the touch mode somehow is not change to false by the Android framework.
+ // The subsequent tab to go through tasks in overview can only be dispatched to
+ // focuses views, while focus can only be requested in
+ // {@link View#requestFocusNoSearch(int, Rect)} when touch mode is false. To note,
+ // here we launch overview with live tile.
+ rv.getViewRootImpl().touchModeChanged(false);
// Ensure that recents view has focus so that it receives the followup key inputs
TaskView taskView = rv.getNextTaskView();
if (taskView == null) {
diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
index 9e3173c..8e07376 100644
--- a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
+++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
@@ -115,11 +115,6 @@
if (mDeviceState.isHomeDisabled() != mIsHomeDisabled) {
updateOverviewTargets();
}
-
- // Notify ALL_APPS touch controller when one handed mode state activated or deactivated
- if (mDeviceState.isOneHandedModeEnabled()) {
- mActivityInterface.onOneHandedModeStateChanged(mDeviceState.isOneHandedModeActive());
- }
}
private void updateOverviewTargets(Intent unused) {
diff --git a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
index 9bd0cb9..65614ba 100644
--- a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
+++ b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
@@ -1,16 +1,30 @@
package com.android.quickstep;
+import static com.android.launcher3.testing.shared.TestProtocol.NPE_TRANSIENT_TASKBAR;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+
import android.app.Activity;
import android.content.Context;
+import android.content.res.Resources;
import android.graphics.Rect;
import android.os.Bundle;
+import android.util.Log;
import androidx.annotation.Nullable;
+import com.android.launcher3.R;
+import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.launcher3.testing.TestInformationHandler;
import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.touch.PagedOrientationHandler;
+import com.android.launcher3.util.DisplayController;
import com.android.quickstep.util.LayoutUtils;
+import com.android.quickstep.util.TISBindHelper;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.function.Consumer;
+import java.util.function.Function;
public class QuickstepTestInformationHandler extends TestInformationHandler {
@@ -72,6 +86,68 @@
TestProtocol.REQUEST_HAS_TIS, true);
return response;
}
+
+ case TestProtocol.REQUEST_ENABLE_MANUAL_TASKBAR_STASHING:
+ runOnTISBinder(tisBinder -> {
+ enableManualTaskbarStashing(tisBinder, true);
+ });
+ return response;
+
+ case TestProtocol.REQUEST_DISABLE_MANUAL_TASKBAR_STASHING:
+ runOnTISBinder(tisBinder -> {
+ enableManualTaskbarStashing(tisBinder, false);
+ });
+ return response;
+
+ case TestProtocol.REQUEST_UNSTASH_TASKBAR_IF_STASHED:
+ runOnTISBinder(tisBinder -> {
+ enableManualTaskbarStashing(tisBinder, true);
+
+ // Allow null-pointer to catch illegal states.
+ tisBinder.getTaskbarManager().getCurrentActivityContext()
+ .unstashTaskbarIfStashed();
+
+ enableManualTaskbarStashing(tisBinder, false);
+ });
+ return response;
+
+ case TestProtocol.REQUEST_STASHED_TASKBAR_HEIGHT: {
+ final Resources resources = mContext.getResources();
+ response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD,
+ resources.getDimensionPixelSize(R.dimen.taskbar_stashed_size));
+ return response;
+ }
+
+ case TestProtocol.REQUEST_TASKBAR_ALL_APPS_TOP_PADDING: {
+ return getTISBinderUIProperty(Bundle::putInt, tisBinder ->
+ tisBinder.getTaskbarManager()
+ .getCurrentActivityContext()
+ .getTaskbarAllAppsTopPadding());
+ }
+
+ case TestProtocol.REQUEST_ENABLE_BLOCK_TIMEOUT:
+ runOnTISBinder(tisBinder -> {
+ enableBlockingTimeout(tisBinder, true);
+ });
+ return response;
+
+ case TestProtocol.REQUEST_DISABLE_BLOCK_TIMEOUT:
+ runOnTISBinder(tisBinder -> {
+ enableBlockingTimeout(tisBinder, false);
+ });
+ return response;
+
+ case TestProtocol.REQUEST_ENABLE_TRANSIENT_TASKBAR:
+ runOnTISBinder(tisBinder -> {
+ enableTransientTaskbar(tisBinder, true);
+ });
+ return response;
+
+ case TestProtocol.REQUEST_DISABLE_TRANSIENT_TASKBAR:
+ runOnTISBinder(tisBinder -> {
+ enableTransientTaskbar(tisBinder, false);
+ });
+ return response;
}
return super.call(method, arg, extras);
@@ -93,4 +169,62 @@
protected boolean isLauncherInitialized() {
return super.isLauncherInitialized() && TouchInteractionService.isInitialized();
}
+
+ private void enableManualTaskbarStashing(
+ TouchInteractionService.TISBinder tisBinder, boolean enable) {
+ // Allow null-pointer to catch illegal states.
+ tisBinder.getTaskbarManager().getCurrentActivityContext().enableManualStashingDuringTests(
+ enable);
+ }
+
+ private void enableBlockingTimeout(
+ TouchInteractionService.TISBinder tisBinder, boolean enable) {
+ // Allow null-pointer to catch illegal states.
+ tisBinder.getTaskbarManager().getCurrentActivityContext().enableBlockingTimeoutDuringTests(
+ enable);
+ }
+
+ private void enableTransientTaskbar(
+ TouchInteractionService.TISBinder tisBinder, boolean enable) {
+ TaskbarActivityContext context = tisBinder.getTaskbarManager().getCurrentActivityContext();
+ if (context == null) {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(NPE_TRANSIENT_TASKBAR, "enableTransientTaskbar: enable=" + enable,
+ new Exception());
+ }
+ } else {
+ DisplayController.INSTANCE.get(context).enableTransientTaskbarForTests(enable);
+ }
+ }
+
+ /**
+ * Runs the given command on the UI thread, after ensuring we are connected to
+ * TouchInteractionService.
+ */
+ protected void runOnTISBinder(Consumer<TouchInteractionService.TISBinder> connectionCallback) {
+ try {
+ CountDownLatch countDownLatch = new CountDownLatch(1);
+ TISBindHelper helper = MAIN_EXECUTOR.submit(() ->
+ new TISBindHelper(mContext, tisBinder -> {
+ connectionCallback.accept(tisBinder);
+ countDownLatch.countDown();
+ })).get();
+ countDownLatch.await();
+ MAIN_EXECUTOR.execute(helper::onDestroy);
+ } catch (ExecutionException | InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private <T> Bundle getTISBinderUIProperty(
+ BundleSetter<T> bundleSetter, Function<TouchInteractionService.TISBinder, T> provider) {
+ Bundle response = new Bundle();
+
+ runOnTISBinder(tisBinder -> bundleSetter.set(
+ response,
+ TestProtocol.TEST_INFO_RESPONSE_FIELD,
+ provider.apply(tisBinder)));
+
+ return response;
+ }
}
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index 813e687..d46565b 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -17,9 +17,14 @@
package com.android.quickstep;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+import static com.android.quickstep.views.DesktopTaskView.DESKTOP_MODE_SUPPORTED;
+import static com.android.wm.shell.util.GroupedRecentTaskInfo.TYPE_FREEFORM;
import android.annotation.TargetApi;
import android.app.ActivityManager;
+import android.app.KeyguardManager;
+import android.app.TaskInfo;
+import android.content.ComponentName;
import android.os.Build;
import android.os.Process;
import android.os.RemoteException;
@@ -27,11 +32,11 @@
import androidx.annotation.VisibleForTesting;
-import com.android.quickstep.util.GroupTask;
import com.android.launcher3.util.LooperExecutor;
import com.android.launcher3.util.SplitConfigurationOptions;
+import com.android.quickstep.util.DesktopTask;
+import com.android.quickstep.util.GroupTask;
import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.system.KeyguardManagerCompat;
import com.android.wm.shell.recents.IRecentTasksListener;
import com.android.wm.shell.util.GroupedRecentTaskInfo;
import com.android.wm.shell.util.SplitBounds;
@@ -49,7 +54,7 @@
private static final TaskLoadResult INVALID_RESULT = new TaskLoadResult(-1, false, 0);
- private final KeyguardManagerCompat mKeyguardManager;
+ private final KeyguardManager mKeyguardManager;
private final LooperExecutor mMainThreadExecutor;
private final SystemUiProxy mSysUiProxy;
@@ -66,8 +71,8 @@
// Tasks are stored in order of least recently launched to most recently launched.
private ArrayList<ActivityManager.RunningTaskInfo> mRunningTasks;
- public RecentTasksList(LooperExecutor mainThreadExecutor,
- KeyguardManagerCompat keyguardManager, SystemUiProxy sysUiProxy) {
+ public RecentTasksList(LooperExecutor mainThreadExecutor, KeyguardManager keyguardManager,
+ SystemUiProxy sysUiProxy) {
mMainThreadExecutor = mainThreadExecutor;
mKeyguardManager = keyguardManager;
mChangeId = 1;
@@ -253,9 +258,15 @@
};
TaskLoadResult allTasks = new TaskLoadResult(requestId, loadKeysOnly, rawTasks.size());
+
for (GroupedRecentTaskInfo rawTask : rawTasks) {
- ActivityManager.RecentTaskInfo taskInfo1 = rawTask.mTaskInfo1;
- ActivityManager.RecentTaskInfo taskInfo2 = rawTask.mTaskInfo2;
+ if (DESKTOP_MODE_SUPPORTED && rawTask.getType() == TYPE_FREEFORM) {
+ GroupTask desktopTask = createDesktopTask(rawTask);
+ allTasks.add(desktopTask);
+ continue;
+ }
+ ActivityManager.RecentTaskInfo taskInfo1 = rawTask.getTaskInfo1();
+ ActivityManager.RecentTaskInfo taskInfo2 = rawTask.getTaskInfo2();
Task.TaskKey task1Key = new Task.TaskKey(taskInfo1);
Task task1 = loadKeysOnly
? new Task(task1Key)
@@ -272,13 +283,27 @@
task2.setLastSnapshotData(taskInfo2);
}
final SplitConfigurationOptions.SplitBounds launcherSplitBounds =
- convertSplitBounds(rawTask.mSplitBounds);
+ convertSplitBounds(rawTask.getSplitBounds());
allTasks.add(new GroupTask(task1, task2, launcherSplitBounds));
}
return allTasks;
}
+ private DesktopTask createDesktopTask(GroupedRecentTaskInfo recentTaskInfo) {
+ ArrayList<Task> tasks = new ArrayList<>(recentTaskInfo.getTaskInfoList().size());
+ for (ActivityManager.RecentTaskInfo taskInfo : recentTaskInfo.getTaskInfoList()) {
+ Task.TaskKey key = new Task.TaskKey(taskInfo);
+ Task task = Task.from(key, taskInfo, false);
+ 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);
+ }
+
private SplitConfigurationOptions.SplitBounds convertSplitBounds(
SplitBounds shellSplitBounds) {
return shellSplitBounds == null ?
@@ -291,7 +316,7 @@
private ArrayList<GroupTask> copyOf(ArrayList<GroupTask> tasks) {
ArrayList<GroupTask> newTasks = new ArrayList<>();
for (int i = 0; i < tasks.size(); i++) {
- newTasks.add(new GroupTask(tasks.get(i)));
+ newTasks.add(tasks.get(i).copy());
}
return newTasks;
}
@@ -301,8 +326,14 @@
writer.println(prefix + " mChangeId=" + mChangeId);
writer.println(prefix + " mResultsUi=[id=" + mResultsUi.mRequestId + ", tasks=");
for (GroupTask task : mResultsUi) {
- writer.println(prefix + " t1=" + task.task1.key.id
- + " t2=" + (task.hasMultipleTasks() ? task.task2.key.id : "-1"));
+ Task task1 = task.task1;
+ Task task2 = task.task2;
+ ComponentName cn1 = task1.getTopComponent();
+ ComponentName cn2 = task2 != null ? task2.getTopComponent() : null;
+ writer.println(prefix + " t1: (id=" + task1.key.id
+ + "; package=" + (cn1 != null ? cn1.getPackageName() + ")" : "no package)")
+ + " t2: (id=" + (task2 != null ? task2.key.id : "-1")
+ + "; package=" + (cn2 != null ? cn2.getPackageName() + ")" : "no package)"));
}
writer.println(prefix + " ]");
int currentUserId = Process.myUserHandle().getIdentifier();
@@ -310,8 +341,14 @@
mSysUiProxy.getRecentTasks(Integer.MAX_VALUE, currentUserId);
writer.println(prefix + " rawTasks=[");
for (GroupedRecentTaskInfo task : rawTasks) {
- writer.println(prefix + " t1=" + task.mTaskInfo1.taskId
- + " t2=" + (task.mTaskInfo2 != null ? task.mTaskInfo2.taskId : "-1"));
+ TaskInfo taskInfo1 = task.getTaskInfo1();
+ TaskInfo taskInfo2 = task.getTaskInfo2();
+ ComponentName cn1 = taskInfo1.topActivity;
+ ComponentName cn2 = taskInfo2 != null ? taskInfo2.topActivity : null;
+ writer.println(prefix + " t1: (id=" + taskInfo1.taskId
+ + "; package=" + (cn1 != null ? cn1.getPackageName() + ")" : "no package)")
+ + " t2: (id=" + (taskInfo2 != null ? taskInfo2.taskId : "-1")
+ + "; package=" + (cn2 != null ? cn2.getPackageName() + ")" : "no package)"));
}
writer.println(prefix + " ]");
}
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 3e3a431..dc405ff 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -15,29 +15,33 @@
*/
package com.android.quickstep;
+import static android.view.RemoteAnimationTarget.MODE_CLOSING;
+import static android.view.RemoteAnimationTarget.MODE_OPENING;
+
import static com.android.launcher3.QuickstepTransitionManager.RECENTS_LAUNCH_DURATION;
import static com.android.launcher3.QuickstepTransitionManager.STATUS_BAR_TRANSITION_DURATION;
import static com.android.launcher3.QuickstepTransitionManager.STATUS_BAR_TRANSITION_PRE_DELAY;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
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;
import static com.android.quickstep.TaskViewUtils.createRecentsWindowAnimator;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
+import android.app.ActivityOptions;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.Display;
+import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl.Transaction;
import android.view.View;
+import android.window.RemoteTransition;
import android.window.SplashScreen;
import androidx.annotation.Nullable;
@@ -77,9 +81,6 @@
import com.android.quickstep.views.OverviewActionsView;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
-import com.android.systemui.shared.system.ActivityOptionsCompat;
-import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -131,7 +132,7 @@
SplitSelectStateController controller =
new SplitSelectStateController(this, mHandler, getStateManager(),
- null /* depthController */);
+ /* depthController */ null, getStatsLogManager());
mDragLayer.recreateControllers();
mFallbackRecentsView.init(mActionsView, controller);
@@ -239,9 +240,9 @@
mActivityLaunchAnimationRunner = new RemoteAnimationFactory() {
@Override
- public void onCreateAnimation(int transit, RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets,
- RemoteAnimationTargetCompat[] nonAppTargets, AnimationResult result) {
+ public void onCreateAnimation(int transit, RemoteAnimationTarget[] appTargets,
+ RemoteAnimationTarget[] wallpaperTargets,
+ RemoteAnimationTarget[] nonAppTargets, AnimationResult result) {
mHandler.removeCallbacks(mAnimationStartTimeoutRunnable);
AnimatorSet anim = composeRecentsLaunchAnimator(taskView, appTargets,
wallpaperTargets, nonAppTargets);
@@ -259,12 +260,12 @@
final LauncherAnimationRunner wrapper = new LauncherAnimationRunner(
mUiHandler, mActivityLaunchAnimationRunner, true /* startAtFrontOfQueue */);
- RemoteAnimationAdapterCompat adapterCompat = new RemoteAnimationAdapterCompat(
- wrapper, RECENTS_LAUNCH_DURATION,
- RECENTS_LAUNCH_DURATION - STATUS_BAR_TRANSITION_DURATION
- - STATUS_BAR_TRANSITION_PRE_DELAY, getIApplicationThread());
- final ActivityOptionsWrapper activityOptions = new ActivityOptionsWrapper(
- ActivityOptionsCompat.makeRemoteAnimation(adapterCompat),
+ final ActivityOptions options = ActivityOptions.makeRemoteAnimation(
+ new RemoteAnimationAdapter(wrapper, RECENTS_LAUNCH_DURATION,
+ RECENTS_LAUNCH_DURATION - STATUS_BAR_TRANSITION_DURATION
+ - STATUS_BAR_TRANSITION_PRE_DELAY),
+ new RemoteTransition(wrapper.toRemoteTransition(), getIApplicationThread()));
+ final ActivityOptionsWrapper activityOptions = new ActivityOptionsWrapper(options,
onEndCallback);
activityOptions.options.setSplashScreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_ICON);
activityOptions.options.setLaunchDisplayId(
@@ -278,9 +279,9 @@
* Composes the animations for a launch from the recents list if possible.
*/
private AnimatorSet composeRecentsLaunchAnimator(TaskView taskView,
- RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets,
- RemoteAnimationTargetCompat[] nonAppTargets) {
+ RemoteAnimationTarget[] appTargets,
+ RemoteAnimationTarget[] wallpaperTargets,
+ RemoteAnimationTarget[] nonAppTargets) {
AnimatorSet target = new AnimatorSet();
boolean activityClosing = taskIsATargetWithMode(appTargets, getTaskId(), MODE_CLOSING);
PendingAnimation pa = new PendingAnimation(RECENTS_LAUNCH_DURATION);
@@ -391,38 +392,33 @@
}
public void startHome() {
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- RecentsView recentsView = getOverviewPanel();
- recentsView.switchToScreenshot(() -> recentsView.finishRecentsAnimation(true,
- this::startHomeInternal));
- } else {
- startHomeInternal();
- }
+ RecentsView recentsView = getOverviewPanel();
+ recentsView.switchToScreenshot(() -> recentsView.finishRecentsAnimation(true,
+ this::startHomeInternal));
}
private void startHomeInternal() {
LauncherAnimationRunner runner = new LauncherAnimationRunner(
getMainThreadHandler(), mAnimationToHomeFactory, true);
- RemoteAnimationAdapterCompat adapterCompat =
- new RemoteAnimationAdapterCompat(runner, HOME_APPEAR_DURATION, 0,
- getIApplicationThread());
- startHomeIntentSafely(this,
- ActivityOptionsCompat.makeRemoteAnimation(adapterCompat).toBundle());
+ ActivityOptions options = ActivityOptions.makeRemoteAnimation(
+ new RemoteAnimationAdapter(runner, HOME_APPEAR_DURATION, 0),
+ new RemoteTransition(runner.toRemoteTransition(), getIApplicationThread()));
+ startHomeIntentSafely(this, options.toBundle());
}
private final RemoteAnimationFactory mAnimationToHomeFactory =
new RemoteAnimationFactory() {
@Override
- public void onCreateAnimation(int transit, RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets,
- RemoteAnimationTargetCompat[] nonAppTargets, AnimationResult result) {
+ public void onCreateAnimation(int transit, RemoteAnimationTarget[] appTargets,
+ RemoteAnimationTarget[] wallpaperTargets,
+ RemoteAnimationTarget[] nonAppTargets, AnimationResult result) {
AnimatorPlaybackController controller = getStateManager()
.createAnimationToNewWorkspace(RecentsState.BG_LAUNCHER, HOME_APPEAR_DURATION);
controller.dispatchOnStart();
RemoteAnimationTargets targets = new RemoteAnimationTargets(
appTargets, wallpaperTargets, nonAppTargets, MODE_OPENING);
- for (RemoteAnimationTargetCompat app : targets.apps) {
+ for (RemoteAnimationTarget app : targets.apps) {
new Transaction().setAlpha(app.leash, 1).apply();
}
AnimatorSet anim = new AnimatorSet();
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
index c602324..b82ff03 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
@@ -16,6 +16,8 @@
package com.android.quickstep;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.CANCEL_RECENTS_ANIMATION;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.START_RECENTS_ANIMATION;
import android.graphics.Rect;
import android.util.ArraySet;
@@ -27,11 +29,11 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.util.Preconditions;
+import com.android.quickstep.util.ActiveGestureErrorDetector;
+import com.android.quickstep.util.ActiveGestureLog;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import java.util.Arrays;
import java.util.HashMap;
import java.util.Set;
@@ -84,17 +86,17 @@
@BinderThread
@Deprecated
public final void onAnimationStart(RecentsAnimationControllerCompat controller,
- RemoteAnimationTargetCompat[] appTargets, Rect homeContentInsets,
+ RemoteAnimationTarget[] appTargets, Rect homeContentInsets,
Rect minimizedHomeBounds) {
- onAnimationStart(controller, appTargets, new RemoteAnimationTargetCompat[0],
+ onAnimationStart(controller, appTargets, new RemoteAnimationTarget[0],
homeContentInsets, minimizedHomeBounds);
}
// Called only in R+ platform
@BinderThread
public final void onAnimationStart(RecentsAnimationControllerCompat animationController,
- RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets,
+ RemoteAnimationTarget[] appTargets,
+ RemoteAnimationTarget[] wallpaperTargets,
Rect homeContentInsets, Rect minimizedHomeBounds) {
mController = new RecentsAnimationController(animationController,
mAllowMinimizeSplitScreen, this::onAnimationFinished);
@@ -103,14 +105,19 @@
Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(),
mController::finishAnimationToApp);
} else {
- final RemoteAnimationTarget[] nonAppTargets = mSystemUiProxy.onGoingToRecentsLegacy(
- Arrays.stream(appTargets).map(RemoteAnimationTargetCompat::unwrap)
- .toArray(RemoteAnimationTarget[]::new));
+ RemoteAnimationTarget[] nonAppTargets =
+ mSystemUiProxy.onGoingToRecentsLegacy(appTargets);
+ if (nonAppTargets == null) {
+ nonAppTargets = new RemoteAnimationTarget[0];
+ }
final RecentsAnimationTargets targets = new RecentsAnimationTargets(appTargets,
- wallpaperTargets, RemoteAnimationTargetCompat.wrap(nonAppTargets),
- homeContentInsets, minimizedHomeBounds);
+ wallpaperTargets, nonAppTargets, homeContentInsets, minimizedHomeBounds);
Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> {
+ ActiveGestureLog.INSTANCE.addLog(
+ /* event= */ "RecentsAnimationCallbacks.onAnimationStart",
+ /* extras= */ targets.apps.length,
+ /* gestureEvent= */ START_RECENTS_ANIMATION);
for (RecentsAnimationListener listener : getListeners()) {
listener.onRecentsAnimationStart(mController, targets);
}
@@ -122,6 +129,9 @@
@Override
public final void onAnimationCanceled(HashMap<Integer, ThumbnailData> thumbnailDatas) {
Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> {
+ ActiveGestureLog.INSTANCE.addLog(
+ /* event= */ "RecentsAnimationCallbacks.onAnimationCanceled",
+ /* gestureEvent= */ CANCEL_RECENTS_ANIMATION);
for (RecentsAnimationListener listener : getListeners()) {
listener.onRecentsAnimationCanceled(thumbnailDatas);
}
@@ -130,8 +140,10 @@
@BinderThread
@Override
- public void onTasksAppeared(RemoteAnimationTargetCompat[] apps) {
+ public void onTasksAppeared(RemoteAnimationTarget[] apps) {
Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> {
+ ActiveGestureLog.INSTANCE.addLog("RecentsAnimationCallbacks.onTasksAppeared",
+ ActiveGestureErrorDetector.GestureEvent.TASK_APPEARED);
for (RecentsAnimationListener listener : getListeners()) {
listener.onTasksAppeared(apps);
}
@@ -152,6 +164,8 @@
private final void onAnimationFinished(RecentsAnimationController controller) {
Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> {
+ ActiveGestureLog.INSTANCE.addLog(
+ /* event= */ "RecentsAnimationCallbacks.onAnimationFinished");
for (RecentsAnimationListener listener : getListeners()) {
listener.onRecentsAnimationFinished(controller);
}
@@ -184,7 +198,7 @@
/**
* Callback made when a task started from the recents is ready for an app transition.
*/
- default void onTasksAppeared(@NonNull RemoteAnimationTargetCompat[] appearedTaskTarget) {}
+ default void onTasksAppeared(@NonNull RemoteAnimationTarget[] appearedTaskTarget) {}
/**
* @return whether this will call onFinished or not (onFinished should only be called once).
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
index fefef2f..4adfae5 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
@@ -18,11 +18,13 @@
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.FINISH_RECENTS_ANIMATION;
import android.content.Context;
import android.os.RemoteException;
import android.util.Log;
import android.view.IRecentsAnimationController;
+import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.view.WindowManagerGlobal;
import android.window.PictureInPictureSurfaceTransaction;
@@ -32,10 +34,11 @@
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.RunnableList;
+import com.android.quickstep.util.ActiveGestureErrorDetector;
+import com.android.quickstep.util.ActiveGestureLog;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.util.function.Consumer;
@@ -112,7 +115,7 @@
* {@link RecentsAnimationCallbacks#onTasksAppeared}}.
*/
@UiThread
- public void removeTaskTarget(@NonNull RemoteAnimationTargetCompat target) {
+ public void removeTaskTarget(@NonNull RemoteAnimationTarget target) {
UI_HELPER_EXECUTOR.execute(() -> mController.removeTask(target.taskId));
}
@@ -153,6 +156,10 @@
mPendingFinishCallbacks.add(callback);
return;
}
+ ActiveGestureLog.INSTANCE.addLog(
+ /* event= */ "finishRecentsAnimation",
+ /* extras= */ toRecents,
+ /* gestureEvent= */ FINISH_RECENTS_ANIMATION);
// Finish not yet requested
mFinishRequested = true;
@@ -163,6 +170,8 @@
mController.finish(toRecents, sendUserLeaveHint);
InteractionJankMonitorWrapper.end(InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH);
InteractionJankMonitorWrapper.end(InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_HOME);
+ InteractionJankMonitorWrapper.end(
+ InteractionJankMonitorWrapper.CUJ_APP_SWIPE_TO_RECENTS);
MAIN_EXECUTOR.execute(mPendingFinishCallbacks::executeAllAndDestroy);
});
}
@@ -172,7 +181,12 @@
*/
@UiThread
public void cleanupScreenshot() {
- UI_HELPER_EXECUTOR.execute(() -> mController.cleanupScreenshot());
+ UI_HELPER_EXECUTOR.execute(() -> {
+ ActiveGestureLog.INSTANCE.addLog(
+ "cleanupScreenshot",
+ ActiveGestureErrorDetector.GestureEvent.CLEANUP_SCREENSHOT);
+ mController.cleanupScreenshot();
+ });
}
/**
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index 48f0557..9e25555 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -23,9 +23,9 @@
import static com.android.launcher3.util.DisplayController.CHANGE_ALL;
import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
import static com.android.launcher3.util.DisplayController.CHANGE_ROTATION;
-import static com.android.launcher3.util.DisplayController.NavigationMode.NO_BUTTON;
-import static com.android.launcher3.util.DisplayController.NavigationMode.THREE_BUTTONS;
-import static com.android.launcher3.util.DisplayController.NavigationMode.TWO_BUTTONS;
+import static com.android.launcher3.util.NavigationMode.NO_BUTTON;
+import static com.android.launcher3.util.NavigationMode.THREE_BUTTONS;
+import static com.android.launcher3.util.NavigationMode.TWO_BUTTONS;
import static com.android.launcher3.util.SettingsCache.ONE_HANDED_ENABLED;
import static com.android.launcher3.util.SettingsCache.ONE_HANDED_SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
@@ -46,10 +46,7 @@
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
import android.app.ActivityTaskManager;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.graphics.Region;
import android.inputmethodservice.InputMethodService;
import android.net.Uri;
@@ -63,12 +60,12 @@
import androidx.annotation.BinderThread;
import androidx.annotation.NonNull;
-import com.android.launcher3.Utilities;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener;
import com.android.launcher3.util.DisplayController.Info;
-import com.android.launcher3.util.DisplayController.NavigationMode;
+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;
@@ -114,15 +111,12 @@
private boolean mIsUserUnlocked;
private final ArrayList<Runnable> mUserUnlockedActions = new ArrayList<>();
- private final BroadcastReceiver mUserUnlockedReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (ACTION_USER_UNLOCKED.equals(intent.getAction())) {
- mIsUserUnlocked = true;
- notifyUserUnlocked();
- }
+ 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();
@@ -153,10 +147,9 @@
mIsUserUnlocked = context.getSystemService(UserManager.class)
.isUserUnlocked(Process.myUserHandle());
if (!mIsUserUnlocked) {
- mContext.registerReceiver(mUserUnlockedReceiver,
- new IntentFilter(ACTION_USER_UNLOCKED));
+ mUserUnlockedReceiver.register(mContext, ACTION_USER_UNLOCKED);
}
- runOnDestroy(() -> Utilities.unregisterReceiverSafely(mContext, mUserUnlockedReceiver));
+ runOnDestroy(() -> mUserUnlockedReceiver.unregisterReceiverSafely(mContext));
// Register for exclusion updates
mExclusionListener = new SystemGestureExclusionListenerCompat(mDisplayId) {
@@ -347,7 +340,7 @@
action.run();
}
mUserUnlockedActions.clear();
- Utilities.unregisterReceiverSafely(mContext, mUserUnlockedReceiver);
+ mUserUnlockedReceiver.unregisterReceiverSafely(mContext);
}
/**
@@ -580,12 +573,15 @@
&& ((mSystemUiStateFlags & SYSUI_STATE_IME_SHOWING) != 0);
}
+ public String getSystemUiStateString() {
+ return QuickStepContract.getSystemUiStateString(mSystemUiStateFlags);
+ }
+
public void dump(PrintWriter pw) {
pw.println("DeviceState:");
pw.println(" canStartSystemGesture=" + canStartSystemGesture());
pw.println(" systemUiFlags=" + mSystemUiStateFlags);
- pw.println(" systemUiFlagsDesc="
- + QuickStepContract.getSystemUiStateString(mSystemUiStateFlags));
+ pw.println(" systemUiFlagsDesc=" + getSystemUiStateString());
pw.println(" assistantAvailable=" + mAssistantAvailable);
pw.println(" assistantDisabled="
+ QuickStepContract.isAssistantGestureDisabled(mSystemUiStateFlags));
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
index b6d9016..388e125 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
@@ -15,11 +15,10 @@
*/
package com.android.quickstep;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
+import static android.view.RemoteAnimationTarget.MODE_CLOSING;
import android.graphics.Rect;
-
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import android.view.RemoteAnimationTarget;
/**
* Extension of {@link RemoteAnimationTargets} with additional information about swipe
@@ -30,8 +29,8 @@
public final Rect homeContentInsets;
public final Rect minimizedHomeBounds;
- public RecentsAnimationTargets(RemoteAnimationTargetCompat[] apps,
- RemoteAnimationTargetCompat[] wallpapers, RemoteAnimationTargetCompat[] nonApps,
+ public RecentsAnimationTargets(RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
Rect homeContentInsets, Rect minimizedHomeBounds) {
super(apps, wallpapers, nonApps, MODE_CLOSING);
this.homeContentInsets = homeContentInsets;
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index 3074dbb..3053474 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -22,6 +22,7 @@
import android.annotation.TargetApi;
import android.app.ActivityManager;
+import android.app.KeyguardManager;
import android.content.ComponentCallbacks2;
import android.content.Context;
import android.content.Intent;
@@ -40,7 +41,6 @@
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.KeyguardManagerCompat;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
@@ -75,7 +75,8 @@
private RecentsModel(Context context) {
mContext = context;
mTaskList = new RecentTasksList(MAIN_EXECUTOR,
- new KeyguardManagerCompat(context), SystemUiProxy.INSTANCE.get(context));
+ context.getSystemService(KeyguardManager.class),
+ SystemUiProxy.INSTANCE.get(context));
IconProvider iconProvider = new IconProvider(context);
mIconCache = new TaskIconCache(context, RECENTS_MODEL_EXECUTOR, iconProvider);
diff --git a/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java b/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java
index 1bd808d..80aaad0 100644
--- a/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java
+++ b/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java
@@ -15,9 +15,11 @@
*/
package com.android.quickstep;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import android.view.RemoteAnimationTarget;
import java.util.ArrayList;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -29,41 +31,40 @@
private final CopyOnWriteArrayList<ReleaseCheck> mReleaseChecks = new CopyOnWriteArrayList<>();
- public final RemoteAnimationTargetCompat[] unfilteredApps;
- public final RemoteAnimationTargetCompat[] apps;
- public final RemoteAnimationTargetCompat[] wallpapers;
- public final RemoteAnimationTargetCompat[] nonApps;
+ public final RemoteAnimationTarget[] unfilteredApps;
+ public final RemoteAnimationTarget[] apps;
+ public final RemoteAnimationTarget[] wallpapers;
+ public final RemoteAnimationTarget[] nonApps;
public final int targetMode;
public final boolean hasRecents;
private boolean mReleased = false;
- public RemoteAnimationTargets(RemoteAnimationTargetCompat[] apps,
- RemoteAnimationTargetCompat[] wallpapers, RemoteAnimationTargetCompat[] nonApps,
+ public RemoteAnimationTargets(RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
int targetMode) {
- ArrayList<RemoteAnimationTargetCompat> filteredApps = new ArrayList<>();
+ ArrayList<RemoteAnimationTarget> filteredApps = new ArrayList<>();
boolean hasRecents = false;
if (apps != null) {
- for (RemoteAnimationTargetCompat target : apps) {
+ for (RemoteAnimationTarget target : apps) {
if (target.mode == targetMode) {
filteredApps.add(target);
}
- hasRecents |= target.activityType ==
- RemoteAnimationTargetCompat.ACTIVITY_TYPE_RECENTS;
+ hasRecents |= target.windowConfiguration.getActivityType() == ACTIVITY_TYPE_RECENTS;
}
}
this.unfilteredApps = apps;
- this.apps = filteredApps.toArray(new RemoteAnimationTargetCompat[filteredApps.size()]);
+ this.apps = filteredApps.toArray(new RemoteAnimationTarget[filteredApps.size()]);
this.wallpapers = wallpapers;
this.targetMode = targetMode;
this.hasRecents = hasRecents;
this.nonApps = nonApps;
}
- public RemoteAnimationTargetCompat findTask(int taskId) {
- for (RemoteAnimationTargetCompat target : apps) {
+ public RemoteAnimationTarget findTask(int taskId) {
+ for (RemoteAnimationTarget target : apps) {
if (target.taskId == taskId) {
return target;
}
@@ -74,12 +75,12 @@
/**
* Gets the navigation bar remote animation target if exists.
*/
- public RemoteAnimationTargetCompat getNavBarRemoteAnimationTarget() {
+ public RemoteAnimationTarget getNavBarRemoteAnimationTarget() {
return getNonAppTargetOfType(TYPE_NAVIGATION_BAR);
}
- public RemoteAnimationTargetCompat getNonAppTargetOfType(int type) {
- for (RemoteAnimationTargetCompat target : nonApps) {
+ public RemoteAnimationTarget getNonAppTargetOfType(int type) {
+ for (RemoteAnimationTarget target : nonApps) {
if (target.windowType == type) {
return target;
}
@@ -88,19 +89,19 @@
}
/** Returns the first opening app target. */
- public RemoteAnimationTargetCompat getFirstAppTarget() {
+ public RemoteAnimationTarget getFirstAppTarget() {
return apps.length > 0 ? apps[0] : null;
}
/** Returns the task id of the first opening app target, or -1 if none is found. */
public int getFirstAppTargetTaskId() {
- RemoteAnimationTargetCompat target = getFirstAppTarget();
+ RemoteAnimationTarget target = getFirstAppTarget();
return target == null ? -1 : target.taskId;
}
public boolean isAnimatingHome() {
- for (RemoteAnimationTargetCompat target : unfilteredApps) {
- if (target.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
+ for (RemoteAnimationTarget target : unfilteredApps) {
+ if (target.windowConfiguration.getActivityType() == ACTIVITY_TYPE_HOME) {
return true;
}
}
@@ -123,15 +124,19 @@
}
mReleaseChecks.clear();
mReleased = true;
+ release(unfilteredApps);
+ release(wallpapers);
+ release(nonApps);
+ }
- for (RemoteAnimationTargetCompat target : unfilteredApps) {
- target.release();
- }
- for (RemoteAnimationTargetCompat target : wallpapers) {
- target.release();
- }
- for (RemoteAnimationTargetCompat target : nonApps) {
- target.release();
+ private static void release(RemoteAnimationTarget[] targets) {
+ for (RemoteAnimationTarget target : targets) {
+ if (target.leash != null) {
+ target.leash.release();
+ }
+ if (target.startLeash != null) {
+ target.startLeash.release();
+ }
}
}
diff --git a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
index 7183c49..4c41bef 100644
--- a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
+++ b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
@@ -17,6 +17,8 @@
package com.android.quickstep;
import android.content.Context;
+import android.graphics.Rect;
+import android.view.RemoteAnimationTarget;
import androidx.annotation.Nullable;
@@ -24,7 +26,6 @@
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.TaskViewSimulator;
import com.android.quickstep.util.TransformParams;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.util.ArrayList;
@@ -75,7 +76,7 @@
*/
public RemoteTargetHandle[] assignTargets(RemoteAnimationTargets targets) {
for (int i = 0; i < mRemoteTargetHandles.length; i++) {
- RemoteAnimationTargetCompat primaryTaskTarget = targets.apps[i];
+ RemoteAnimationTarget primaryTaskTarget = targets.apps[i];
mRemoteTargetHandles[i].mTransformParams.setTargetSet(
createRemoteAnimationTargetsForTarget(targets, null));
mRemoteTargetHandles[i].mTaskViewSimulator.setPreview(primaryTaskTarget, null);
@@ -100,8 +101,8 @@
*/
public RemoteTargetHandle[] assignTargetsForSplitScreen(RemoteAnimationTargets targets,
int[] splitIds) {
- RemoteAnimationTargetCompat topLeftTarget; // only one set if single/fullscreen task
- RemoteAnimationTargetCompat bottomRightTarget;
+ RemoteAnimationTarget topLeftTarget; // only one set if single/fullscreen task
+ RemoteAnimationTarget bottomRightTarget;
if (mRemoteTargetHandles.length == 1) {
// If we're not in split screen, the splitIds count doesn't really matter since we
// should always hit this case.
@@ -119,8 +120,8 @@
// remoteTargetHandle[0] denotes topLeft task, so we pass in the bottomRight to exclude,
// vice versa
mSplitBounds = new SplitBounds(
- topLeftTarget.startScreenSpaceBounds,
- bottomRightTarget.startScreenSpaceBounds, splitIds[0], splitIds[1]);
+ getStartBounds(topLeftTarget),
+ getStartBounds(bottomRightTarget), splitIds[0], splitIds[1]);
mRemoteTargetHandles[0].mTransformParams.setTargetSet(
createRemoteAnimationTargetsForTarget(targets, bottomRightTarget));
mRemoteTargetHandles[0].mTaskViewSimulator.setPreview(topLeftTarget,
@@ -134,6 +135,10 @@
return mRemoteTargetHandles;
}
+ private Rect getStartBounds(RemoteAnimationTarget target) {
+ return target.startBounds == null ? target.screenSpaceBounds : target.startBounds;
+ }
+
/**
* Ensures that we aren't excluding ancillary targets such as home/recents
*
@@ -144,11 +149,10 @@
*/
private RemoteAnimationTargets createRemoteAnimationTargetsForTarget(
RemoteAnimationTargets targets,
- RemoteAnimationTargetCompat targetToExclude) {
- ArrayList<RemoteAnimationTargetCompat> targetsWithoutExcluded =
- new ArrayList<RemoteAnimationTargetCompat>();
+ RemoteAnimationTarget targetToExclude) {
+ ArrayList<RemoteAnimationTarget> targetsWithoutExcluded = new ArrayList<>();
- for (RemoteAnimationTargetCompat targetCompat : targets.unfilteredApps) {
+ for (RemoteAnimationTarget targetCompat : targets.unfilteredApps) {
if (targetCompat == targetToExclude) {
continue;
}
@@ -162,9 +166,8 @@
targetsWithoutExcluded.add(targetCompat);
}
- final RemoteAnimationTargetCompat[] filteredApps =
- targetsWithoutExcluded.toArray(
- new RemoteAnimationTargetCompat[targetsWithoutExcluded.size()]);
+ final RemoteAnimationTarget[] filteredApps = targetsWithoutExcluded.toArray(
+ new RemoteAnimationTarget[targetsWithoutExcluded.size()]);
return new RemoteAnimationTargets(
filteredApps, targets.wallpapers, targets.nonApps, targets.targetMode);
}
diff --git a/quickstep/src/com/android/quickstep/RotationTouchHelper.java b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
index 2186a3b..f8b6966 100644
--- a/quickstep/src/com/android/quickstep/RotationTouchHelper.java
+++ b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
@@ -23,8 +23,8 @@
import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
import static com.android.launcher3.util.DisplayController.CHANGE_ROTATION;
import static com.android.launcher3.util.DisplayController.CHANGE_SUPPORTED_BOUNDS;
-import static com.android.launcher3.util.DisplayController.NavigationMode.THREE_BUTTONS;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+import static com.android.launcher3.util.NavigationMode.THREE_BUTTONS;
import android.content.Context;
import android.content.res.Resources;
@@ -35,8 +35,8 @@
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener;
import com.android.launcher3.util.DisplayController.Info;
-import com.android.launcher3.util.DisplayController.NavigationMode;
import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.launcher3.util.NavigationMode;
import com.android.quickstep.util.RecentsOrientedState;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.TaskStackChangeListener;
diff --git a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
index baeb514..ddb06ce 100644
--- a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
+++ b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
@@ -24,6 +24,7 @@
import android.graphics.Matrix.ScaleToFit;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.view.RemoteAnimationTarget;
import androidx.annotation.NonNull;
import androidx.annotation.UiThread;
@@ -37,11 +38,10 @@
import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.RectFSpringAnim;
+import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties;
import com.android.quickstep.util.TaskViewSimulator;
import com.android.quickstep.util.TransformParams;
import com.android.quickstep.util.TransformParams.BuilderProxy;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder;
import java.util.Arrays;
import java.util.function.Consumer;
@@ -65,6 +65,7 @@
// 1 => preview snapShot is completely aligned with the recents view and hotseat is completely
// visible.
protected final AnimatedFloat mCurrentShift = new AnimatedFloat(this::updateFinalShift);
+ protected float mCurrentDisplacement;
// The distance needed to drag to reach the task size in recents.
protected int mTransitionDragLength;
@@ -116,6 +117,8 @@
public void updateDisplacement(float displacement) {
// We are moving in the negative x/y direction
displacement = -displacement;
+ mCurrentDisplacement = displacement;
+
float shift;
if (displacement > mTransitionDragLength * mDragLengthFactor && mTransitionDragLength > 0) {
shift = mDragLengthFactor;
@@ -335,11 +338,11 @@
}
@Override
- public void onBuildTargetParams(
- Builder builder, RemoteAnimationTargetCompat app, TransformParams params) {
- builder.withMatrix(mMatrix)
- .withWindowCrop(mCropRect)
- .withCornerRadius(params.getCornerRadius());
+ public void onBuildTargetParams(SurfaceProperties builder, RemoteAnimationTarget app,
+ TransformParams params) {
+ builder.setMatrix(mMatrix)
+ .setWindowCrop(mCropRect)
+ .setCornerRadius(params.getCornerRadius());
}
@Override
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index 0ec7e62..bb97334 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -17,8 +17,8 @@
import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
-import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import android.app.ActivityManager;
import android.app.PendingIntent;
@@ -28,12 +28,14 @@
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
-import android.graphics.Bitmap;
+import android.content.pm.ShortcutInfo;
import android.graphics.Insets;
import android.graphics.Rect;
import android.os.Bundle;
+import android.os.Handler;
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
+import android.os.Message;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Log;
@@ -42,18 +44,22 @@
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.window.IOnBackInvokedCallback;
+import android.window.RemoteTransition;
+import android.window.TransitionFilter;
-import com.android.launcher3.util.DisplayController;
-import com.android.launcher3.util.DisplayController.Info;
+import androidx.annotation.Nullable;
+import androidx.annotation.WorkerThread;
+
+import com.android.internal.logging.InstanceId;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.system.RemoteTransitionCompat;
import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController;
import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController;
import com.android.systemui.shared.system.smartspace.SmartspaceState;
import com.android.wm.shell.back.IBackAnimation;
+import com.android.wm.shell.desktopmode.IDesktopMode;
import com.android.wm.shell.onehanded.IOneHanded;
import com.android.wm.shell.pip.IPip;
import com.android.wm.shell.pip.IPipAnimationListener;
@@ -68,16 +74,19 @@
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.LinkedHashMap;
/**
* Holds the reference to SystemUI.
*/
-public class SystemUiProxy implements ISystemUiProxy, DisplayController.DisplayInfoChangeListener {
+public class SystemUiProxy implements ISystemUiProxy {
private static final String TAG = SystemUiProxy.class.getSimpleName();
public static final MainThreadInitializedObject<SystemUiProxy> INSTANCE =
new MainThreadInitializedObject<>(SystemUiProxy::new);
+ private static final int MSG_SET_SHELF_HEIGHT = 1;
+
private ISystemUiProxy mSystemUiProxy;
private IPip mPip;
private ISysuiUnlockAnimationController mSysuiUnlockAnimationController;
@@ -87,6 +96,7 @@
private IStartingWindow mStartingWindow;
private IRecentTasks mRecentTasks;
private IBackAnimation mBackAnimation;
+ private IDesktopMode mDesktopMode;
private final DeathRecipient mSystemUiProxyDeathRecipient = () -> {
MAIN_EXECUTOR.execute(() -> clearProxy());
};
@@ -100,32 +110,23 @@
private IStartingWindowListener mStartingWindowListener;
private ILauncherUnlockAnimationController mLauncherUnlockAnimationController;
private IRecentTasksListener mRecentTasksListener;
- private final ArrayList<RemoteTransitionCompat> mRemoteTransitions = new ArrayList<>();
+ private final LinkedHashMap<RemoteTransition, TransitionFilter> mRemoteTransitions =
+ new LinkedHashMap<>();
private IOnBackInvokedCallback mBackToLauncherCallback;
// Used to dedupe calls to SystemUI
private int mLastShelfHeight;
private boolean mLastShelfVisible;
- private float mLastNavButtonAlpha;
- private boolean mLastNavButtonAnimate;
- private boolean mHasNavButtonAlphaBeenSet = false;
- private Runnable mPendingSetNavButtonAlpha = null;
- private Context mContext;
+
+ private final Context mContext;
+ private final Handler mAsyncHandler;
// TODO(141886704): Find a way to remove this
private int mLastSystemUiStateFlags;
public SystemUiProxy(Context context) {
- DisplayController.INSTANCE.get(context).addChangeListener(this);
mContext = context;
- }
-
- @Override
- public void onDisplayInfoChanged(Context context, Info info, int flags) {
- if ((flags & CHANGE_NAVIGATION_MODE) != 0) {
- // Whenever the nav mode changes, force reset the nav button alpha
- setNavBarButtonAlpha(1f, false);
- }
+ mAsyncHandler = new Handler(UI_HELPER_EXECUTOR.getLooper(), this::handleMessageAsync);
}
@Override
@@ -171,7 +172,7 @@
IOneHanded oneHanded, IShellTransitions shellTransitions,
IStartingWindow startingWindow, IRecentTasks recentTasks,
ISysuiUnlockAnimationController sysuiUnlockAnimationController,
- IBackAnimation backAnimation) {
+ IBackAnimation backAnimation, IDesktopMode desktopMode) {
unlinkToDeath();
mSystemUiProxy = proxy;
mPip = pip;
@@ -182,10 +183,11 @@
mSysuiUnlockAnimationController = sysuiUnlockAnimationController;
mRecentTasks = recentTasks;
mBackAnimation = backAnimation;
+ mDesktopMode = desktopMode;
linkToDeath();
// re-attach the listeners once missing due to setProxy has not been initialized yet.
if (mPipAnimationListener != null && mPip != null) {
- setPinnedStackAnimationListener(mPipAnimationListener);
+ setPipAnimationListener(mPipAnimationListener);
}
if (mSplitScreenListener != null && mSplitScreen != null) {
registerSplitScreenListener(mSplitScreenListener);
@@ -196,24 +198,17 @@
if (mSysuiUnlockAnimationController != null && mLauncherUnlockAnimationController != null) {
setLauncherUnlockAnimationController(mLauncherUnlockAnimationController);
}
- for (int i = mRemoteTransitions.size() - 1; i >= 0; --i) {
- registerRemoteTransition(mRemoteTransitions.get(i));
- }
+ new LinkedHashMap<>(mRemoteTransitions).forEach(this::registerRemoteTransition);
if (mRecentTasksListener != null && mRecentTasks != null) {
registerRecentTasksListener(mRecentTasksListener);
}
if (mBackAnimation != null && mBackToLauncherCallback != null) {
setBackToLauncherCallback(mBackToLauncherCallback);
}
-
- if (mPendingSetNavButtonAlpha != null) {
- mPendingSetNavButtonAlpha.run();
- mPendingSetNavButtonAlpha = null;
- }
}
public void clearProxy() {
- setProxy(null, null, null, null, null, null, null, null, null);
+ setProxy(null, null, null, null, null, null, null, null, null, null);
}
// TODO(141886704): Find a way to remove this
@@ -272,31 +267,6 @@
}
}
- public float getLastNavButtonAlpha() {
- return mLastNavButtonAlpha;
- }
-
- @Override
- public void setNavBarButtonAlpha(float alpha, boolean animate) {
- boolean changed = Float.compare(alpha, mLastNavButtonAlpha) != 0
- || animate != mLastNavButtonAnimate
- || !mHasNavButtonAlphaBeenSet;
- if (changed) {
- if (mSystemUiProxy == null) {
- mPendingSetNavButtonAlpha = () -> setNavBarButtonAlpha(alpha, animate);
- } else {
- mLastNavButtonAlpha = alpha;
- mLastNavButtonAnimate = animate;
- mHasNavButtonAlphaBeenSet = true;
- try {
- mSystemUiProxy.setNavBarButtonAlpha(alpha, animate);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call setNavBarButtonAlpha", e);
- }
- }
- }
- }
-
@Override
public void onStatusBarMotionEvent(MotionEvent event) {
if (mSystemUiProxy != null) {
@@ -375,31 +345,6 @@
}
@Override
- public void notifySwipeUpGestureStarted() {
- if (mSystemUiProxy != null) {
- try {
- mSystemUiProxy.notifySwipeUpGestureStarted();
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call notifySwipeUpGestureStarted", e);
- }
- }
- }
-
- /**
- * Notifies that swipe-to-home action is finished.
- */
- @Override
- public void notifySwipeToHomeFinished() {
- if (mSystemUiProxy != null) {
- try {
- mSystemUiProxy.notifySwipeToHomeFinished();
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call notifySwipeToHomeFinished", e);
- }
- }
- }
-
- @Override
public void notifyPrioritizedRotation(int rotation) {
if (mSystemUiProxy != null) {
try {
@@ -481,12 +426,20 @@
* Sets the shelf height.
*/
public void setShelfHeight(boolean visible, int shelfHeight) {
+ Message.obtain(mAsyncHandler, MSG_SET_SHELF_HEIGHT,
+ visible ? 1 : 0 , shelfHeight).sendToTarget();
+ }
+
+ @WorkerThread
+ private void setShelfHeightAsync(int visibleInt, int shelfHeight) {
+ boolean visible = visibleInt != 0;
boolean changed = visible != mLastShelfVisible || shelfHeight != mLastShelfHeight;
- if (mPip != null && changed) {
+ IPip pip = mPip;
+ if (pip != null && changed) {
mLastShelfVisible = visible;
mLastShelfHeight = shelfHeight;
try {
- mPip.setShelfHeight(visible, shelfHeight);
+ pip.setShelfHeight(visible, shelfHeight);
} catch (RemoteException e) {
Log.w(TAG, "Failed call setShelfHeight visible: " + visible
+ " height: " + shelfHeight, e);
@@ -495,12 +448,12 @@
}
/**
- * Sets listener to get pinned stack animation callbacks.
+ * Sets listener to get pip animation callbacks.
*/
- public void setPinnedStackAnimationListener(IPipAnimationListener listener) {
+ public void setPipAnimationListener(IPipAnimationListener listener) {
if (mPip != null) {
try {
- mPip.setPinnedStackAnimationListener(listener);
+ mPip.setPipAnimationListener(listener);
} catch (RemoteException e) {
Log.w(TAG, "Failed call setPinnedStackAnimationListener", e);
}
@@ -508,12 +461,17 @@
mPipAnimationListener = listener;
}
+ /**
+ * @return Destination bounds of auto-pip animation, {@code null} if the animation is not ready.
+ */
+ @Nullable
public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
- PictureInPictureParams pictureInPictureParams, int launcherRotation, int shelfHeight) {
+ PictureInPictureParams pictureInPictureParams, int launcherRotation,
+ Rect hotseatKeepClearArea) {
if (mPip != null) {
try {
return mPip.startSwipePipToHome(componentName, activityInfo,
- pictureInPictureParams, launcherRotation, shelfHeight);
+ pictureInPictureParams, launcherRotation, hotseatKeepClearArea);
} catch (RemoteException e) {
Log.w(TAG, "Failed call startSwipePipToHome", e);
}
@@ -537,6 +495,19 @@
}
}
+ /**
+ * Sets the next pip animation type to be the alpha animation.
+ */
+ public void setPipAnimationTypeToAlpha() {
+ if (mPip != null) {
+ try {
+ mPip.setPipAnimationTypeToAlpha();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call setPipAnimationTypeToAlpha", e);
+ }
+ }
+ }
+
//
// Splitscreen
//
@@ -564,15 +535,55 @@
}
/** Start multiple tasks in split-screen simultaneously. */
- public void startTasks(int mainTaskId, Bundle mainOptions, int sideTaskId, Bundle sideOptions,
- @SplitConfigurationOptions.StagePosition int sidePosition, float splitRatio,
- RemoteTransitionCompat remoteTransition) {
+ public void startTasks(int taskId1, Bundle options1, int taskId2, Bundle options2,
+ @SplitConfigurationOptions.StagePosition int splitPosition, float splitRatio,
+ RemoteTransition remoteTransition, InstanceId instanceId) {
if (mSystemUiProxy != null) {
try {
- mSplitScreen.startTasks(mainTaskId, mainOptions, sideTaskId, sideOptions,
- sidePosition, splitRatio, remoteTransition.getTransition());
+ mSplitScreen.startTasks(taskId1, options1, taskId2, options2, splitPosition,
+ splitRatio, remoteTransition, instanceId);
} catch (RemoteException e) {
- Log.w(TAG, "Failed call startTask");
+ Log.w(TAG, "Failed call startTasks");
+ }
+ }
+ }
+
+ public void startIntentAndTask(PendingIntent pendingIntent, Bundle options1, int taskId,
+ Bundle options2, @SplitConfigurationOptions.StagePosition int splitPosition,
+ float splitRatio, RemoteTransition remoteTransition, InstanceId instanceId) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSplitScreen.startIntentAndTask(pendingIntent, options1, taskId, options2,
+ splitPosition, splitRatio, remoteTransition, instanceId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call startIntentAndTask");
+ }
+ }
+ }
+
+ public void startIntents(PendingIntent pendingIntent1, Bundle options1,
+ PendingIntent pendingIntent2, Bundle options2,
+ @SplitConfigurationOptions.StagePosition int splitPosition,
+ float splitRatio, RemoteTransition remoteTransition, InstanceId instanceId) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSplitScreen.startIntents(pendingIntent1, options1, pendingIntent2, options2,
+ splitPosition, splitRatio, remoteTransition, instanceId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call startIntents");
+ }
+ }
+ }
+
+ public void startShortcutAndTask(ShortcutInfo shortcutInfo, Bundle options1, int taskId,
+ Bundle options2, @SplitConfigurationOptions.StagePosition int splitPosition,
+ float splitRatio, RemoteTransition remoteTransition, InstanceId instanceId) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSplitScreen.startShortcutAndTask(shortcutInfo, options1, taskId, options2,
+ splitPosition, splitRatio, remoteTransition, instanceId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call startShortcutAndTask");
}
}
}
@@ -580,13 +591,13 @@
/**
* Start multiple tasks in split-screen simultaneously.
*/
- public void startTasksWithLegacyTransition(int mainTaskId, Bundle mainOptions, int sideTaskId,
- Bundle sideOptions, @SplitConfigurationOptions.StagePosition int sidePosition,
- float splitRatio, RemoteAnimationAdapter adapter) {
+ public void startTasksWithLegacyTransition(int taskId1, Bundle options1, int taskId2,
+ Bundle options2, @SplitConfigurationOptions.StagePosition int splitPosition,
+ float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId) {
if (mSystemUiProxy != null) {
try {
- mSplitScreen.startTasksWithLegacyTransition(mainTaskId, mainOptions, sideTaskId,
- sideOptions, sidePosition, splitRatio, adapter);
+ mSplitScreen.startTasksWithLegacyTransition(taskId1, options1, taskId2, options2,
+ splitPosition, splitRatio, adapter, instanceId);
} catch (RemoteException e) {
Log.w(TAG, "Failed call startTasksWithLegacyTransition");
}
@@ -594,25 +605,52 @@
}
public void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent,
- Intent fillInIntent, int taskId, Bundle mainOptions, Bundle sideOptions,
- @SplitConfigurationOptions.StagePosition int sidePosition, float splitRatio,
- RemoteAnimationAdapter adapter) {
+ Bundle options1, int taskId, Bundle options2,
+ @SplitConfigurationOptions.StagePosition int splitPosition, float splitRatio,
+ RemoteAnimationAdapter adapter, InstanceId instanceId) {
if (mSystemUiProxy != null) {
try {
- mSplitScreen.startIntentAndTaskWithLegacyTransition(pendingIntent, fillInIntent,
- taskId, mainOptions, sideOptions, sidePosition, splitRatio, adapter);
+ mSplitScreen.startIntentAndTaskWithLegacyTransition(pendingIntent, options1, taskId,
+ options2, splitPosition, splitRatio, adapter, instanceId);
} catch (RemoteException e) {
- Log.w(TAG, "Failed call startTasksWithLegacyTransition");
+ Log.w(TAG, "Failed call startIntentAndTaskWithLegacyTransition");
+ }
+ }
+ }
+
+ public void startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo, Bundle options1,
+ int taskId, Bundle options2, @SplitConfigurationOptions.StagePosition int splitPosition,
+ float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSplitScreen.startShortcutAndTaskWithLegacyTransition(shortcutInfo, options1,
+ taskId, options2, splitPosition, splitRatio, adapter, instanceId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call startShortcutAndTaskWithLegacyTransition");
+ }
+ }
+ }
+
+ public void startIntentsWithLegacyTransition(PendingIntent pendingIntent1, Bundle options1,
+ PendingIntent pendingIntent2, Bundle options2,
+ @SplitConfigurationOptions.StagePosition int sidePosition, float splitRatio,
+ RemoteAnimationAdapter adapter, InstanceId instanceId) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSplitScreen.startIntentsWithLegacyTransition(pendingIntent1, options1,
+ pendingIntent2, options2, sidePosition, splitRatio, adapter, instanceId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call startIntentsWithLegacyTransition");
}
}
}
public void startShortcut(String packageName, String shortcutId, int position,
- Bundle options, UserHandle user) {
+ Bundle options, UserHandle user, InstanceId instanceId) {
if (mSplitScreen != null) {
try {
mSplitScreen.startShortcut(packageName, shortcutId, position, options,
- user);
+ user, instanceId);
} catch (RemoteException e) {
Log.w(TAG, "Failed call startShortcut");
}
@@ -620,10 +658,10 @@
}
public void startIntent(PendingIntent intent, Intent fillInIntent, int position,
- Bundle options) {
+ Bundle options, InstanceId instanceId) {
if (mSplitScreen != null) {
try {
- mSplitScreen.startIntent(intent, fillInIntent, position, options);
+ mSplitScreen.startIntent(intent, fillInIntent, position, options, instanceId);
} catch (RemoteException e) {
Log.w(TAG, "Failed call startIntent");
}
@@ -646,6 +684,7 @@
*
* @return RemoteAnimationTargets of windows that need to animate but only exist in shell.
*/
+ @Nullable
public RemoteAnimationTarget[] onGoingToRecentsLegacy(RemoteAnimationTarget[] apps) {
if (mSplitScreen != null) {
try {
@@ -657,6 +696,7 @@
return null;
}
+ @Nullable
public RemoteAnimationTarget[] onStartingSplitLegacy(RemoteAnimationTarget[] apps) {
if (mSplitScreen != null) {
try {
@@ -696,24 +736,24 @@
// Remote transitions
//
- public void registerRemoteTransition(RemoteTransitionCompat remoteTransition) {
+ public void registerRemoteTransition(
+ RemoteTransition remoteTransition, TransitionFilter filter) {
if (mShellTransitions != null) {
try {
- mShellTransitions.registerRemote(remoteTransition.getFilter(),
- remoteTransition.getTransition());
+ mShellTransitions.registerRemote(filter, remoteTransition);
} catch (RemoteException e) {
Log.w(TAG, "Failed call registerRemoteTransition");
}
}
- if (!mRemoteTransitions.contains(remoteTransition)) {
- mRemoteTransitions.add(remoteTransition);
+ if (!mRemoteTransitions.containsKey(remoteTransition)) {
+ mRemoteTransitions.put(remoteTransition, filter);
}
}
- public void unregisterRemoteTransition(RemoteTransitionCompat remoteTransition) {
+ public void unregisterRemoteTransition(RemoteTransition remoteTransition) {
if (mShellTransitions != null) {
try {
- mShellTransitions.unregisterRemote(remoteTransition.getTransition());
+ mShellTransitions.unregisterRemote(remoteTransition);
} catch (RemoteException e) {
Log.w(TAG, "Failed call registerRemoteTransition");
}
@@ -880,12 +920,36 @@
if (mRecentTasks != null
&& mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_PC)) {
try {
- return new ArrayList<ActivityManager.RunningTaskInfo>(
- Arrays.asList(mRecentTasks.getRunningTasks(numTasks)));
+ return new ArrayList<>(Arrays.asList(mRecentTasks.getRunningTasks(numTasks)));
} catch (RemoteException e) {
Log.w(TAG, "Failed call getRunningTasks", e);
}
}
return new ArrayList<>();
}
+
+ private boolean handleMessageAsync(Message msg) {
+ switch (msg.what) {
+ case MSG_SET_SHELF_HEIGHT:
+ setShelfHeightAsync(msg.arg1, msg.arg2);
+ return true;
+ }
+
+ return false;
+ }
+
+ //
+ // Desktop Mode
+ //
+
+ /** Call shell to show all apps active on the desktop */
+ public void showDesktopApps() {
+ if (mDesktopMode != null) {
+ try {
+ mDesktopMode.showDesktopApps();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call showDesktopApps", e);
+ }
+ }
+ }
}
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index 0314761..c45b2f0 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -15,12 +15,14 @@
*/
package com.android.quickstep;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_INITIALIZED;
import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_STARTED;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.START_RECENTS_ANIMATION;
+import static com.android.systemui.shared.system.RemoteTransitionCompat.newRemoteTransition;
import android.app.ActivityManager;
import android.app.ActivityOptions;
@@ -29,6 +31,7 @@
import android.os.SystemProperties;
import android.util.Log;
import android.view.RemoteAnimationTarget;
+import android.window.RemoteTransition;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
@@ -36,16 +39,13 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
import com.android.quickstep.TopTaskTracker.CachedTaskInfo;
+import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.ActivityOptionsCompat;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.RemoteTransitionCompat;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
-import java.util.Arrays;
import java.util.HashMap;
public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAnimationListener {
@@ -59,7 +59,7 @@
private RecentsAnimationTargets mTargets;
// Temporary until we can hook into gesture state events
private GestureState mLastGestureState;
- private RemoteAnimationTargetCompat mLastAppearedTaskTarget;
+ private RemoteAnimationTarget mLastAppearedTaskTarget;
private Runnable mLiveTileCleanUpHandler;
private Context mCtx;
@@ -73,7 +73,7 @@
return;
}
BaseActivityInterface activityInterface = mLastGestureState.getActivityInterface();
- if (ENABLE_QUICKSTEP_LIVE_TILE.get() && activityInterface.isInLiveTileMode()
+ if (activityInterface.isInLiveTileMode()
&& activityInterface.getCreatedActivity() != null) {
RecentsView recentsView = activityInterface.getCreatedActivity().getOverviewPanel();
if (recentsView != null) {
@@ -103,6 +103,9 @@
@UiThread
public RecentsAnimationCallbacks startRecentsAnimation(GestureState gestureState,
Intent intent, RecentsAnimationCallbacks.RecentsAnimationListener listener) {
+ ActiveGestureLog.INSTANCE.addLog(
+ /* event= */ "startRecentsAnimation",
+ /* gestureEvent= */ START_RECENTS_ANIMATION);
// Notify if recents animation is still running
if (mController != null) {
String msg = "New recents animation started before old animation completed";
@@ -153,12 +156,12 @@
}
@Override
- public void onTasksAppeared(RemoteAnimationTargetCompat[] appearedTaskTargets) {
- RemoteAnimationTargetCompat appearedTaskTarget = appearedTaskTargets[0];
+ public void onTasksAppeared(RemoteAnimationTarget[] appearedTaskTargets) {
+ RemoteAnimationTarget appearedTaskTarget = appearedTaskTargets[0];
BaseActivityInterface activityInterface = mLastGestureState.getActivityInterface();
- for (RemoteAnimationTargetCompat compat : appearedTaskTargets) {
- if (compat.activityType == ACTIVITY_TYPE_HOME
+ for (RemoteAnimationTarget compat : appearedTaskTargets) {
+ if (compat.windowConfiguration.getActivityType() == ACTIVITY_TYPE_HOME
&& activityInterface.getCreatedActivity() instanceof RecentsActivity) {
// When receive opening home activity while recents is running, enter home
// and dismiss recents.
@@ -167,25 +170,25 @@
}
}
- RemoteAnimationTarget[] nonAppTargets = SystemUiProxy.INSTANCE.getNoCreate()
- .onStartingSplitLegacy(Arrays.stream(appearedTaskTargets)
- .map(RemoteAnimationTargetCompat::unwrap)
- .toArray(RemoteAnimationTarget[]::new));
-
- if (ENABLE_QUICKSTEP_LIVE_TILE.get() && activityInterface.isInLiveTileMode()
+ RemoteAnimationTarget[] nonAppTargets = SystemUiProxy.INSTANCE.get(mCtx)
+ .onStartingSplitLegacy(appearedTaskTargets);
+ if (nonAppTargets == null) {
+ nonAppTargets = new RemoteAnimationTarget[0];
+ }
+ if (activityInterface.isInLiveTileMode()
&& activityInterface.getCreatedActivity() != null) {
RecentsView recentsView =
activityInterface.getCreatedActivity().getOverviewPanel();
if (recentsView != null) {
recentsView.launchSideTaskInLiveTileMode(appearedTaskTarget.taskId,
appearedTaskTargets,
- new RemoteAnimationTargetCompat[0] /* wallpaper */,
- RemoteAnimationTargetCompat.wrap(nonAppTargets) /* nonApps */);
+ new RemoteAnimationTarget[0] /* wallpaper */,
+ nonAppTargets /* nonApps */);
return;
}
- } else if (nonAppTargets != null && nonAppTargets.length > 0) {
+ } else if (nonAppTargets.length > 0) {
TaskViewUtils.createSplitAuxiliarySurfacesAnimator(
- RemoteAnimationTargetCompat.wrap(nonAppTargets) /* nonApps */,
+ nonAppTargets /* nonApps */,
true /*shown*/, dividerAnimator -> {
dividerAnimator.start();
dividerAnimator.end();
@@ -205,7 +208,7 @@
@Override
public boolean onSwitchToScreenshot(Runnable onFinished) {
- if (!ENABLE_QUICKSTEP_LIVE_TILE.get() || !activityInterface.isInLiveTileMode()
+ if (!activityInterface.isInLiveTileMode()
|| activityInterface.getCreatedActivity() == null) {
// No need to switch since tile is already a screenshot.
onFinished.run();
@@ -226,10 +229,10 @@
mCallbacks.addListener(listener);
if (ENABLE_SHELL_TRANSITIONS) {
- RemoteTransitionCompat transition = new RemoteTransitionCompat(mCallbacks,
+ RemoteTransition transition = newRemoteTransition(mCallbacks,
mController != null ? mController.getController() : null,
mCtx.getIApplicationThread());
- final ActivityOptions options = ActivityOptionsCompat.makeRemoteTransition(transition);
+ final ActivityOptions options = ActivityOptions.makeRemoteTransition(transition);
// Allowing to pause Home if Home is top activity and Recents is not Home. So when user
// start home when recents animation is playing, the home activity can be resumed again
// to let the transition controller collect Home activity.
@@ -252,6 +255,7 @@
* Continues the existing running recents animation for a new gesture.
*/
public RecentsAnimationCallbacks continueRecentsAnimation(GestureState gestureState) {
+ ActiveGestureLog.INSTANCE.addLog(/* event= */ "continueRecentsAnimation");
mCallbacks.removeListener(mLastGestureState);
mLastGestureState = gestureState;
mCallbacks.addListener(gestureState);
@@ -266,7 +270,7 @@
return;
}
BaseActivityInterface activityInterface = mLastGestureState.getActivityInterface();
- if (ENABLE_QUICKSTEP_LIVE_TILE.get() && activityInterface.isInLiveTileMode()
+ if (activityInterface.isInLiveTileMode()
&& activityInterface.getCreatedActivity() != null) {
RecentsView recentsView = activityInterface.getCreatedActivity().getOverviewPanel();
if (recentsView != null) {
@@ -290,6 +294,8 @@
*/
public void finishRunningRecentsAnimation(boolean toHome) {
if (mController != null) {
+ ActiveGestureLog.INSTANCE.addLog(
+ /* event= */ "finishRunningRecentsAnimation", toHome);
mCallbacks.notifyAnimationCanceled();
Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), toHome
? mController::finishAnimationToHome
@@ -322,6 +328,7 @@
* Cleans up the recents animation entirely.
*/
private void cleanUpRecentsAnimation() {
+ ActiveGestureLog.INSTANCE.addLog(/* event= */ "cleanUpRecentsAnimation");
if (mLiveTileCleanUpHandler != null) {
mLiveTileCleanUpHandler.run();
mLiveTileCleanUpHandler = null;
diff --git a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
index e10cab6..d40f2ae 100644
--- a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
@@ -18,7 +18,6 @@
import static android.view.Surface.ROTATION_0;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.quickstep.views.OverviewActionsView.DISABLED_NO_THUMBNAIL;
import static com.android.quickstep.views.OverviewActionsView.DISABLED_ROTATED;
@@ -174,14 +173,10 @@
* @param callback callback to run, after switching to screenshot
*/
public void endLiveTileMode(@NonNull Runnable callback) {
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- RecentsView recentsView = mThumbnailView.getTaskView().getRecentsView();
- recentsView.switchToScreenshot(
- () -> recentsView.finishRecentsAnimation(true /* toRecents */,
- false /* shouldPip */, callback));
- } else {
- callback.run();
- }
+ RecentsView recentsView = mThumbnailView.getTaskView().getRecentsView();
+ recentsView.switchToScreenshot(
+ () -> recentsView.finishRecentsAnimation(true /* toRecents */,
+ false /* shouldPip */, callback));
}
/**
diff --git a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
index 69a295b..eae79df 100644
--- a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
@@ -16,6 +16,8 @@
package com.android.quickstep;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+
import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_SELECTIONS;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_FREE_FORM_TAP;
@@ -26,8 +28,12 @@
import android.graphics.Rect;
import android.os.Handler;
import android.os.Looper;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.util.Log;
import android.view.View;
import android.view.WindowInsets;
+import android.view.WindowManagerGlobal;
import android.window.SplashScreen;
import androidx.annotation.Nullable;
@@ -51,8 +57,6 @@
import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture;
import com.android.systemui.shared.recents.view.RecentsTransition;
import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.ActivityOptionsCompat;
-import com.android.systemui.shared.system.WindowManagerWrapper;
import java.util.Collections;
import java.util.List;
@@ -120,6 +124,7 @@
}
class FreeformSystemShortcut extends SystemShortcut<BaseDraggingActivity> {
+ private static final String TAG = "FreeformSystemShortcut";
private Handler mHandler;
@@ -192,7 +197,7 @@
taskId, thumbnail, taskBounds));
}
};
- WindowManagerWrapper.getInstance().overridePendingAppTransitionMultiThumbFuture(
+ overridePendingAppTransitionMultiThumbFuture(
future, animStartedListener, mHandler, true /* scaleUp */,
taskKey.displayId);
mTarget.getStatsLogManager().logger().withItemInfo(mTaskView.getItemInfo())
@@ -200,8 +205,27 @@
}
}
+ /**
+ * Overrides a pending app transition.
+ */
+ private void overridePendingAppTransitionMultiThumbFuture(
+ AppTransitionAnimationSpecsFuture animationSpecFuture, Runnable animStartedCallback,
+ Handler animStartedCallbackHandler, boolean scaleUp, int displayId) {
+ try {
+ WindowManagerGlobal.getWindowManagerService()
+ .overridePendingAppTransitionMultiThumbFuture(
+ animationSpecFuture.getFuture(),
+ RecentsTransition.wrapStartedListener(animStartedCallbackHandler,
+ animStartedCallback), scaleUp, displayId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to override pending app transition (multi-thumbnail future): ",
+ e);
+ }
+ }
+
private ActivityOptions makeLaunchOptions(Activity activity) {
- ActivityOptions activityOptions = ActivityOptionsCompat.makeFreeformOptions();
+ ActivityOptions activityOptions = ActivityOptions.makeBasic();
+ activityOptions.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM);
// Arbitrary bounds only because freeform is in dev mode right now
final View decorView = activity.getWindow().getDecorView();
final WindowInsets insets = decorView.getRootWindowInsets();
@@ -278,7 +302,8 @@
}
private boolean isAvailable(BaseDraggingActivity activity, int displayId) {
- return ActivityManagerWrapper.getInstance().supportsFreeformMultiWindow(activity);
+ return ActivityManagerWrapper.getInstance().supportsFreeformMultiWindow(activity)
+ && !SystemProperties.getBoolean("persist.wm.debug.desktop_mode", false);
}
};
diff --git a/quickstep/src/com/android/quickstep/TaskUtils.java b/quickstep/src/com/android/quickstep/TaskUtils.java
index c9db153..67360c4 100644
--- a/quickstep/src/com/android/quickstep/TaskUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskUtils.java
@@ -18,19 +18,23 @@
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.UserHandle;
import android.util.Log;
+import android.view.RemoteAnimationTarget;
+
+import androidx.annotation.Nullable;
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.util.List;
@@ -47,16 +51,32 @@
* TODO: remove this once we switch to getting the icon and label from IconCache.
*/
public static CharSequence getTitle(Context context, Task task) {
- UserHandle user = UserHandle.of(task.key.userId);
+ return getTitle(context, task.key.userId, task.getTopComponent().getPackageName());
+ }
+
+ public static CharSequence getTitle(
+ @NonNull Context context,
+ @UserIdInt @Nullable Integer userId,
+ @Nullable String packageName) {
+ if (userId == null || packageName == null) {
+ if (userId == null) {
+ Log.e(TAG, "Failed to get title; missing userId");
+ }
+ if (packageName == null) {
+ Log.e(TAG, "Failed to get title; missing packageName");
+ }
+ return "";
+ }
+ UserHandle user = UserHandle.of(userId);
ApplicationInfo applicationInfo = new PackageManagerHelper(context)
- .getApplicationInfo(task.getTopComponent().getPackageName(), user, 0);
+ .getApplicationInfo(packageName, user, 0);
if (applicationInfo == null) {
- Log.e(TAG, "Failed to get title for task " + task);
+ Log.e(TAG, "Failed to get title for userId=" + userId + ", packageName=" + packageName);
return "";
}
PackageManager packageManager = context.getPackageManager();
return packageManager.getUserBadgedLabel(
- applicationInfo.loadLabel(packageManager), user);
+ applicationInfo.loadLabel(packageManager), user);
}
public static ComponentKey getLaunchComponentKeyForTask(Task.TaskKey taskKey) {
@@ -67,9 +87,9 @@
}
- public static boolean taskIsATargetWithMode(RemoteAnimationTargetCompat[] targets,
+ public static boolean taskIsATargetWithMode(RemoteAnimationTarget[] targets,
int taskId, int mode) {
- for (RemoteAnimationTargetCompat target : targets) {
+ for (RemoteAnimationTarget target : targets) {
if (target.mode == mode && target.taskId == taskId) {
return true;
}
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index 3bd72fe..e8722e2 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -15,7 +15,8 @@
*/
package com.android.quickstep;
-import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.view.RemoteAnimationTarget.MODE_CLOSING;
+import static android.view.RemoteAnimationTarget.MODE_OPENING;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
@@ -35,18 +36,13 @@
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.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
-import static com.android.launcher3.statehandlers.DepthController.DEPTH;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
+import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
-import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.graphics.Matrix;
@@ -54,6 +50,7 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Build;
+import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.view.View;
import android.window.TransitionInfo;
@@ -73,6 +70,8 @@
import com.android.launcher3.util.DisplayController;
import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
import com.android.quickstep.util.MultiValueUpdateListener;
+import com.android.quickstep.util.SurfaceTransaction;
+import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties;
import com.android.quickstep.util.SurfaceTransactionApplier;
import com.android.quickstep.util.TaskViewSimulator;
import com.android.quickstep.util.TransformParams;
@@ -83,7 +82,6 @@
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
import java.util.ArrayList;
import java.util.List;
@@ -105,7 +103,7 @@
* opening remote target (which we don't get until onAnimationStart) will resolve to a TaskView.
*/
public static TaskView findTaskViewToLaunch(
- RecentsView recentsView, View v, RemoteAnimationTargetCompat[] targets) {
+ RecentsView recentsView, View v, RemoteAnimationTarget[] targets) {
if (v instanceof TaskView) {
TaskView taskView = (TaskView) v;
return recentsView.isTaskViewVisible(taskView) ? taskView : null;
@@ -135,7 +133,7 @@
}
// Resolve the opening task id
int openingTaskId = -1;
- for (RemoteAnimationTargetCompat target : targets) {
+ for (RemoteAnimationTarget target : targets) {
if (target.mode == MODE_OPENING) {
openingTaskId = target.taskId;
break;
@@ -158,9 +156,9 @@
public static void createRecentsWindowAnimator(
@NonNull TaskView v, boolean skipViewChanges,
- @NonNull RemoteAnimationTargetCompat[] appTargets,
- @NonNull RemoteAnimationTargetCompat[] wallpaperTargets,
- @NonNull RemoteAnimationTargetCompat[] nonAppTargets,
+ @NonNull RemoteAnimationTarget[] appTargets,
+ @NonNull RemoteAnimationTarget[] wallpaperTargets,
+ @NonNull RemoteAnimationTarget[] nonAppTargets,
@Nullable DepthController depthController,
PendingAnimation out) {
RecentsView recentsView = v.getRecentsView();
@@ -170,7 +168,7 @@
final RemoteAnimationTargets targets =
new RemoteAnimationTargets(appTargets, wallpaperTargets, nonAppTargets,
MODE_OPENING);
- final RemoteAnimationTargetCompat navBarTarget = targets.getNavBarRemoteAnimationTarget();
+ final RemoteAnimationTarget navBarTarget = targets.getNavBarRemoteAnimationTarget();
SurfaceTransactionApplier applier = new SurfaceTransactionApplier(v);
targets.addReleaseCheck(applier);
@@ -195,10 +193,10 @@
int taskIndex = recentsView.indexOfChild(v);
Context context = v.getContext();
- DeviceProfile dp = BaseActivity.fromContext(context).getDeviceProfile();
+ BaseActivity baseActivity = BaseActivity.fromContext(context);
+ DeviceProfile dp = baseActivity.getDeviceProfile();
boolean showAsGrid = dp.isTablet;
- boolean parallaxCenterAndAdjacentTask =
- taskIndex != recentsView.getCurrentPage() && !showAsGrid;
+ boolean parallaxCenterAndAdjacentTask = taskIndex != recentsView.getCurrentPage();
int taskRectTranslationPrimary = recentsView.getScrollOffset(taskIndex);
int taskRectTranslationSecondary = showAsGrid ? (int) v.getGridTranslationY() : 0;
@@ -253,21 +251,24 @@
@Override
public void onUpdate(float percent, boolean initOnly) {
- final SurfaceParams.Builder navBuilder =
- new SurfaceParams.Builder(navBarTarget.leash);
+
// TODO Do we need to operate over multiple TVSs for the navbar leash?
for (RemoteTargetHandle handle : remoteTargetHandles) {
+ SurfaceTransaction transaction = new SurfaceTransaction();
+ SurfaceProperties navBuilder =
+ transaction.forSurface(navBarTarget.leash);
+
if (mNavFadeIn.value > mNavFadeIn.getStartValue()) {
TaskViewSimulator taskViewSimulator = handle.getTaskViewSimulator();
taskViewSimulator.getCurrentCropRect().round(cropRect);
- navBuilder.withMatrix(taskViewSimulator.getCurrentMatrix())
- .withWindowCrop(cropRect)
- .withAlpha(mNavFadeIn.value);
+ navBuilder.setMatrix(taskViewSimulator.getCurrentMatrix())
+ .setWindowCrop(cropRect)
+ .setAlpha(mNavFadeIn.value);
} else {
- navBuilder.withAlpha(mNavFadeOut.value);
+ navBuilder.setAlpha(mNavFadeOut.value);
}
- handle.getTransformParams().applySurfaceParams(navBuilder.build());
+ handle.getTransformParams().applySurfaceParams(transaction);
}
}
});
@@ -305,9 +306,15 @@
// to follow the TaskViewSimulator. So the final matrix applied on the thumbnailView is:
// Mt K(0)` K(t) Mt`
TaskThumbnailView[] thumbnails = v.getThumbnails();
- Matrix[] mt = new Matrix[simulatorCopies.length];
- Matrix[] mti = new Matrix[simulatorCopies.length];
- for (int i = 0; i < thumbnails.length; i++) {
+
+ // In case simulator copies and thumbnail size do no match, ensure we get the lesser.
+ // This ensures we do not create arrays with empty elements or attempt to references
+ // indexes out of array bounds.
+ final int matrixSize = Math.min(simulatorCopies.length, thumbnails.length);
+
+ Matrix[] mt = new Matrix[matrixSize];
+ Matrix[] mti = new Matrix[matrixSize];
+ for (int i = 0; i < matrixSize; i++) {
TaskThumbnailView ttv = thumbnails[i];
RectF localBounds = new RectF(0, 0, ttv.getWidth(), ttv.getHeight());
float[] tvBoundsMapped = new float[]{0, 0, ttv.getWidth(), ttv.getHeight()};
@@ -324,14 +331,14 @@
mti[i] = localMti;
}
- Matrix[] k0i = new Matrix[simulatorCopies.length];
- for (int i = 0; i < simulatorCopies.length; i++) {
+ Matrix[] k0i = new Matrix[matrixSize];
+ for (int i = 0; i < matrixSize; i++) {
k0i[i] = new Matrix();
simulatorCopies[i].getTaskViewSimulator().getCurrentMatrix().invert(k0i[i]);
}
Matrix animationMatrix = new Matrix();
out.addOnFrameCallback(() -> {
- for (int i = 0; i < simulatorCopies.length; i++) {
+ for (int i = 0; i < matrixSize; i++) {
animationMatrix.set(mt[i]);
animationMatrix.postConcat(k0i[i]);
animationMatrix.postConcat(simulatorCopies[i]
@@ -368,8 +375,8 @@
});
if (depthController != null) {
- out.setFloat(depthController, DEPTH, BACKGROUND_APP.getDepth(context),
- TOUCH_RESPONSE_INTERPOLATOR);
+ out.setFloat(depthController.stateDepth, MULTI_PROPERTY_VALUE,
+ BACKGROUND_APP.getDepth(baseActivity), TOUCH_RESPONSE_INTERPOLATOR);
}
}
@@ -391,9 +398,8 @@
*/
public static void composeRecentsSplitLaunchAnimator(GroupedTaskView launchingTaskView,
@NonNull StateManager stateManager, @Nullable DepthController depthController,
- int initialTaskId, @Nullable PendingIntent initialTaskPendingIntent, int secondTaskId,
- @NonNull TransitionInfo transitionInfo, SurfaceControl.Transaction t,
- @NonNull Runnable finishCallback) {
+ int initialTaskId, int secondTaskId, @NonNull TransitionInfo transitionInfo,
+ SurfaceControl.Transaction t, @NonNull Runnable finishCallback) {
if (launchingTaskView != null) {
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.addListener(new AnimatorListenerAdapter() {
@@ -403,12 +409,12 @@
}
});
- final RemoteAnimationTargetCompat[] appTargets =
+ final RemoteAnimationTarget[] appTargets =
RemoteAnimationTargetCompat.wrapApps(transitionInfo, t, null /* leashMap */);
- final RemoteAnimationTargetCompat[] wallpaperTargets =
+ final RemoteAnimationTarget[] wallpaperTargets =
RemoteAnimationTargetCompat.wrapNonApps(
transitionInfo, true /* wallpapers */, t, null /* leashMap */);
- final RemoteAnimationTargetCompat[] nonAppTargets =
+ final RemoteAnimationTarget[] nonAppTargets =
RemoteAnimationTargetCompat.wrapNonApps(
transitionInfo, false /* wallpapers */, t, null /* leashMap */);
final RecentsView recentsView = launchingTaskView.getRecentsView();
@@ -427,8 +433,10 @@
TransitionInfo.Change splitRoot2 = null;
for (int i = 0; i < transitionInfo.getChanges().size(); ++i) {
final TransitionInfo.Change change = transitionInfo.getChanges().get(i);
- final int taskId = change.getTaskInfo() != null ? change.getTaskInfo().taskId : -1;
+ if (change.getTaskInfo() == null) continue;
+ final int taskId = change.getTaskInfo().taskId;
final int mode = change.getMode();
+
// Find the target tasks' root tasks since those are the split stages that need to
// be animated (the tasks themselves are children and thus inherit animation).
if (taskId == initialTaskId || taskId == secondTaskId) {
@@ -441,7 +449,7 @@
+ "root of " + taskId + " is already visible or has broken hierarchy.");
}
}
- if (taskId == initialTaskId && initialTaskId != INVALID_TASK_ID) {
+ if (taskId == initialTaskId) {
splitRoot1 = transitionInfo.getChange(change.getParent());
}
if (taskId == secondTaskId) {
@@ -475,17 +483,16 @@
* If {@param launchingTaskView} is not null, then this will play the tasks launch animation
* from the position of the GroupedTaskView (when user taps on the TaskView to start it).
* Technically this case should be taken care of by
- * {@link #composeRecentsSplitLaunchAnimatorLegacy()} below, but the way we launch tasks whether
+ * {@link #composeRecentsSplitLaunchAnimatorLegacy} below, but the way we launch tasks whether
* it's a single task or multiple tasks results in different entry-points.
*
* If it is null, then it will simply fade in the starting apps and fade out launcher (for the
* case where launcher handles animating starting split tasks from app icon) */
public static void composeRecentsSplitLaunchAnimatorLegacy(
- @Nullable GroupedTaskView launchingTaskView, int initialTaskId,
- @Nullable PendingIntent initialTaskPendingIntent, int secondTaskId,
- @NonNull RemoteAnimationTargetCompat[] appTargets,
- @NonNull RemoteAnimationTargetCompat[] wallpaperTargets,
- @NonNull RemoteAnimationTargetCompat[] nonAppTargets,
+ @Nullable GroupedTaskView launchingTaskView, int initialTaskId, int secondTaskId,
+ @NonNull RemoteAnimationTarget[] appTargets,
+ @NonNull RemoteAnimationTarget[] wallpaperTargets,
+ @NonNull RemoteAnimationTarget[] nonAppTargets,
@NonNull StateManager stateManager,
@Nullable DepthController depthController,
@NonNull Runnable finishCallback) {
@@ -508,7 +515,7 @@
final ArrayList<SurfaceControl> openingTargets = new ArrayList<>();
final ArrayList<SurfaceControl> closingTargets = new ArrayList<>();
- for (RemoteAnimationTargetCompat appTarget : appTargets) {
+ for (RemoteAnimationTarget appTarget : appTargets) {
final int taskId = appTarget.taskInfo != null ? appTarget.taskInfo.taskId : -1;
final int mode = appTarget.mode;
final SurfaceControl leash = appTarget.leash;
@@ -563,9 +570,9 @@
}
public static void composeRecentsLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
- @NonNull RemoteAnimationTargetCompat[] appTargets,
- @NonNull RemoteAnimationTargetCompat[] wallpaperTargets,
- @NonNull RemoteAnimationTargetCompat[] nonAppTargets, boolean launcherClosing,
+ @NonNull RemoteAnimationTarget[] appTargets,
+ @NonNull RemoteAnimationTarget[] wallpaperTargets,
+ @NonNull RemoteAnimationTarget[] nonAppTargets, boolean launcherClosing,
@NonNull StateManager stateManager, @NonNull RecentsView recentsView,
@Nullable DepthController depthController) {
boolean skipLauncherChanges = !launcherClosing;
@@ -593,11 +600,16 @@
Animator launcherAnim;
final AnimatorListenerAdapter windowAnimEndListener;
if (launcherClosing) {
- Context context = v.getContext();
- DeviceProfile dp = BaseActivity.fromContext(context).getDeviceProfile();
- launcherAnim = dp.isTablet
- ? ObjectAnimator.ofFloat(recentsView, RecentsView.CONTENT_ALPHA, 0)
- : recentsView.createAdjacentPageAnimForTaskLaunch(taskView);
+ // Since Overview is in launcher, just opening overview sets willFinishToHome to true.
+ // Now that we are closing the launcher, we need to (re)set willFinishToHome back to
+ // false. Otherwise, RecentsAnimationController can't differentiate between closing
+ // overview to 3p home vs closing overview to app.
+ final RecentsAnimationController raController =
+ recentsView.getRecentsAnimationController();
+ if (raController != null) {
+ raController.setWillFinishToHome(false);
+ }
+ launcherAnim = recentsView.createAdjacentPageAnimForTaskLaunch(taskView);
launcherAnim.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR);
launcherAnim.setDuration(RECENTS_LAUNCH_DURATION);
@@ -633,7 +645,7 @@
};
}
pa.add(launcherAnim);
- if (ENABLE_QUICKSTEP_LIVE_TILE.get() && recentsView.getRunningTaskIndex() != -1) {
+ if (recentsView.getRunningTaskIndex() != -1) {
pa.addOnFrameCallback(recentsView::redrawLiveTile);
}
anim.play(pa.buildAnim());
@@ -652,7 +664,7 @@
* @return the animator animating the surfaces
*/
public static ValueAnimator createSplitAuxiliarySurfacesAnimator(
- RemoteAnimationTargetCompat[] nonApps, boolean shown,
+ RemoteAnimationTarget[] nonApps, boolean shown,
Consumer<ValueAnimator> animatorHandler) {
if (nonApps == null || nonApps.length == 0) {
return null;
@@ -662,9 +674,9 @@
List<SurfaceControl> auxiliarySurfaces = new ArrayList<>(nonApps.length);
boolean hasSurfaceToAnimate = false;
for (int i = 0; i < nonApps.length; ++i) {
- final RemoteAnimationTargetCompat targ = nonApps[i];
+ final RemoteAnimationTarget targ = nonApps[i];
final SurfaceControl leash = targ.leash;
- if (targ.windowType == TYPE_DOCK_DIVIDER && leash != null) {
+ if (targ.windowType == TYPE_DOCK_DIVIDER && leash != null && leash.isValid()) {
auxiliarySurfaces.add(leash);
hasSurfaceToAnimate = true;
}
@@ -677,7 +689,9 @@
dockFadeAnimator.addUpdateListener(valueAnimator -> {
float progress = valueAnimator.getAnimatedFraction();
for (SurfaceControl leash : auxiliarySurfaces) {
- t.setAlpha(leash, shown ? progress : 1 - progress);
+ if (leash != null && leash.isValid()) {
+ t.setAlpha(leash, shown ? progress : 1 - progress);
+ }
}
t.apply();
});
@@ -698,7 +712,9 @@
public void onAnimationEnd(Animator animation) {
if (!shown) {
for (SurfaceControl leash : auxiliarySurfaces) {
- t.hide(leash);
+ if (leash != null && leash.isValid()) {
+ t.hide(leash);
+ }
}
t.apply();
}
diff --git a/quickstep/src/com/android/quickstep/TopTaskTracker.java b/quickstep/src/com/android/quickstep/TopTaskTracker.java
index cfcba4c..d4bf5c7 100644
--- a/quickstep/src/com/android/quickstep/TopTaskTracker.java
+++ b/quickstep/src/com/android/quickstep/TopTaskTracker.java
@@ -24,6 +24,7 @@
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
+import android.annotation.UserIdInt;
import android.app.ActivityManager.RunningTaskInfo;
import android.content.Context;
@@ -282,5 +283,16 @@
}
return result;
}
+
+ @UserIdInt
+ @Nullable
+ public Integer getUserId() {
+ return mTopTask == null ? null : mTopTask.userId;
+ }
+
+ @Nullable
+ public String getPackageName() {
+ return mTopTask == null ? null : mTopTask.baseActivity.getPackageName();
+ }
}
}
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index c46926e..450774b 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,26 +15,31 @@
*/
package com.android.quickstep;
+import static android.accessibilityservice.AccessibilityService.GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS;
import static android.view.MotionEvent.ACTION_CANCEL;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_UP;
import static com.android.launcher3.config.FeatureFlags.ASSISTANT_GIVES_LAUNCHER_FOCUS;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.quickstep.GestureState.DEFAULT_STATE;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.MOTION_DOWN;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.MOTION_UP;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
-import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_RECENT_TASKS;
-import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_BACK_ANIMATION;
-import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_ONE_HANDED;
-import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_PIP;
-import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SHELL_TRANSITIONS;
-import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SPLIT_SCREEN;
-import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_STARTING_WINDOW;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
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_TRACING_ENABLED;
+import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_BACK_ANIMATION;
+import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_DESKTOP_MODE;
+import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_ONE_HANDED;
+import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_PIP;
+import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_RECENT_TASKS;
+import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_SHELL_TRANSITIONS;
+import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_SPLIT_SCREEN;
+import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_STARTING_WINDOW;
import android.annotation.TargetApi;
import android.app.PendingIntent;
@@ -43,7 +48,6 @@
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Configuration;
-import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.drawable.Icon;
import android.os.Build;
@@ -51,11 +55,11 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.SystemClock;
-import android.os.SystemProperties;
import android.util.Log;
import android.view.Choreographer;
import android.view.InputEvent;
import android.view.MotionEvent;
+import android.view.SurfaceControl;
import android.view.accessibility.AccessibilityManager;
import androidx.annotation.BinderThread;
@@ -63,11 +67,13 @@
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
+import com.android.app.viewcapture.ViewCapture;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
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;
@@ -80,7 +86,6 @@
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.OnboardingPrefs;
import com.android.launcher3.util.TraceHelper;
-import com.android.launcher3.util.WindowBounds;
import com.android.quickstep.inputconsumers.AccessibilityInputConsumer;
import com.android.quickstep.inputconsumers.AssistantInputConsumer;
import com.android.quickstep.inputconsumers.DeviceLockedInputConsumer;
@@ -94,9 +99,9 @@
import com.android.quickstep.inputconsumers.SysUiOverlayInputConsumer;
import com.android.quickstep.inputconsumers.TaskbarStashInputConsumer;
import com.android.quickstep.util.ActiveGestureLog;
+import com.android.quickstep.util.ActiveGestureLog.CompoundString;
import com.android.quickstep.util.ProtoTracer;
import com.android.quickstep.util.ProxyScreenStatusProvider;
-import com.android.quickstep.util.SplitScreenBounds;
import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -106,6 +111,7 @@
import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController;
import com.android.systemui.shared.tracing.ProtoTraceable;
import com.android.wm.shell.back.IBackAnimation;
+import com.android.wm.shell.desktopmode.IDesktopMode;
import com.android.wm.shell.onehanded.IOneHanded;
import com.android.wm.shell.pip.IPip;
import com.android.wm.shell.recents.IRecentTasks;
@@ -126,23 +132,12 @@
public class TouchInteractionService extends Service
implements ProtoTraceable<LauncherTraceProto.Builder> {
+ private static final String SUBSTRING_PREFIX = "; ";
+ private static final String NEWLINE_PREFIX = "\n\t\t\t-> ";
+
private static final String TAG = "TouchInteractionService";
- private static final boolean BUBBLES_HOME_GESTURE_ENABLED =
- SystemProperties.getBoolean("persist.wm.debug.bubbles_home_gesture", true);
-
- private static final String KEY_BACK_NOTIFICATION_COUNT = "backNotificationCount";
- private static final String NOTIFY_ACTION_BACK = "com.android.quickstep.action.BACK_GESTURE";
private static final String HAS_ENABLED_QUICKSTEP_ONCE = "launcher.has_enabled_quickstep_once";
- private static final int MAX_BACK_NOTIFICATION_COUNT = 3;
-
- /**
- * System Action ID to show all apps.
- * TODO: Use AccessibilityService's corresponding global action constant in S
- */
- private static final int SYSTEM_ACTION_ID_ALL_APPS = 14;
-
- private int mBackGestureNotificationCounter = -1;
private final TISBinder mTISBinder = new TISBinder();
@@ -170,13 +165,15 @@
ISysuiUnlockAnimationController.Stub.asInterface(
bundle.getBinder(KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER));
IRecentTasks recentTasks = IRecentTasks.Stub.asInterface(
- bundle.getBinder(KEY_EXTRA_RECENT_TASKS));
+ bundle.getBinder(KEY_EXTRA_SHELL_RECENT_TASKS));
IBackAnimation backAnimation = IBackAnimation.Stub.asInterface(
bundle.getBinder(KEY_EXTRA_SHELL_BACK_ANIMATION));
+ IDesktopMode desktopMode = IDesktopMode.Stub.asInterface(
+ bundle.getBinder(KEY_EXTRA_SHELL_DESKTOP_MODE));
MAIN_EXECUTOR.execute(() -> {
SystemUiProxy.INSTANCE.get(TouchInteractionService.this).setProxy(proxy, pip,
- splitscreen, onehanded, shellTransitions, startingWindow, recentTasks,
- launcherUnlockAnimationController, backAnimation);
+ splitscreen, onehanded, shellTransitions, startingWindow,
+ recentTasks, launcherUnlockAnimationController, backAnimation, desktopMode);
TouchInteractionService.this.initInputMonitor("TISBinder#onInitialize()");
preloadOverview(true /* fromInit */);
});
@@ -216,12 +213,6 @@
@BinderThread
@Override
- public void onTip(int actionType, int viewType) {
- // Please delete this method from the interface
- }
-
- @BinderThread
- @Override
public void onAssistantAvailable(boolean available) {
MAIN_EXECUTOR.execute(() -> {
mDeviceState.setAssistantAvailable(available);
@@ -238,10 +229,9 @@
});
}
- @BinderThread
- public void onBackAction(boolean completed, int downX, int downY, boolean isButton,
- boolean gestureSwipeLeft) {
- // Remove this method from the interface
+ @Override
+ public void onNavigationBarSurface(SurfaceControl surface) {
+ // TODO: implement
}
@BinderThread
@@ -258,12 +248,6 @@
MAIN_EXECUTOR.execute(() -> mDeviceState.setDeferredGestureRegion(region));
}
- @Override
- public void onSplitScreenSecondaryBoundsChanged(Rect bounds, Rect insets) {
- WindowBounds wb = new WindowBounds(bounds, insets);
- MAIN_EXECUTOR.execute(() -> SplitScreenBounds.INSTANCE.setSecondaryWindowBounds(wb));
- }
-
@BinderThread
@Override
public void onScreenTurnedOn() {
@@ -282,6 +266,16 @@
MAIN_EXECUTOR.execute(ProxyScreenStatusProvider.INSTANCE::onScreenTurningOff);
}
+ @BinderThread
+ @Override
+ public void enterStageSplitFromRunningApp(boolean leftOrTop) {
+ StatefulActivity activity =
+ mOverviewComponentObserver.getActivityInterface().getCreatedActivity();
+ if (activity != null) {
+ activity.enterStageSplitFromRunningApp(leftOrTop);
+ }
+ }
+
/**
* Preloads the Overview activity.
*
@@ -455,7 +449,8 @@
mOverviewComponentObserver = new OverviewComponentObserver(this, mDeviceState);
mOverviewCommandHelper = new OverviewCommandHelper(this,
mOverviewComponentObserver, mTaskAnimationManager);
- mResetGestureInputConsumer = new ResetGestureInputConsumer(mTaskAnimationManager);
+ mResetGestureInputConsumer = new ResetGestureInputConsumer(
+ mTaskAnimationManager, mTaskbarManager::getCurrentActivityContext);
mInputConsumer = InputConsumerController.getRecentsAnimationInputConsumer();
mInputConsumer.registerInputConsumer();
onSystemUiFlagsChanged(mDeviceState.getSystemUiStateFlags());
@@ -466,8 +461,6 @@
// Temporarily disable model preload
// new ModelPreload().start(this);
- mBackGestureNotificationCounter = Math.max(0, Utilities.getDevicePrefs(this)
- .getInt(KEY_BACK_NOTIFICATION_COUNT, MAX_BACK_NOTIFICATION_COUNT));
resetHomeBounceSeenOnQuickstepEnabledFirstTime();
mOverviewComponentObserver.setOverviewChangeListener(this::onOverviewTargetChange);
@@ -505,11 +498,11 @@
Icon.createWithResource(this, R.drawable.ic_apps),
getString(R.string.all_apps_label),
getString(R.string.all_apps_label),
- PendingIntent.getActivity(this, SYSTEM_ACTION_ID_ALL_APPS, intent,
+ PendingIntent.getActivity(this, GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS, intent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE));
- am.registerSystemAction(allAppsAction, SYSTEM_ACTION_ID_ALL_APPS);
+ am.registerSystemAction(allAppsAction, GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS);
} else {
- am.unregisterSystemAction(SYSTEM_ACTION_ID_ALL_APPS);
+ am.unregisterSystemAction(GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS);
}
StatefulActivity newOverviewActivity = mOverviewComponentObserver.getActivityInterface()
@@ -528,6 +521,18 @@
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 = mOverviewComponentObserver
+ .getActivityInterface().getDesktopVisibilityController();
+ if (controller != null) {
+ controller.setFreeformTasksVisible(isFreeformActive);
+ }
+ }
+
boolean wasExpanded = (lastSysUIFlags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) != 0;
boolean isExpanded =
(systemUiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) != 0;
@@ -575,7 +580,7 @@
ProtoTracer.INSTANCE.get(this).remove(this);
getSystemService(AccessibilityManager.class)
- .unregisterSystemAction(SYSTEM_ACTION_ID_ALL_APPS);
+ .unregisterSystemAction(GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS);
mTaskbarManager.destroy();
sConnected = false;
@@ -619,8 +624,6 @@
mConsumer.onConsumerAboutToBeSwitched();
mGestureState = newGestureState;
mConsumer = newConsumer(prevGestureState, mGestureState, event);
-
- ActiveGestureLog.INSTANCE.addLog("setInputConsumer: " + mConsumer.getName());
mUncheckedConsumer = mConsumer;
} else if (mDeviceState.isUserUnlocked() && mDeviceState.isFullyGesturalNavMode()
&& mDeviceState.canTriggerAssistantAction(event)) {
@@ -628,8 +631,7 @@
// Do not change mConsumer as if there is an ongoing QuickSwitch gesture, we
// should not interrupt it. QuickSwitch assumes that interruption can only
// happen if the next gesture is also quick switch.
- mUncheckedConsumer = tryCreateAssistantInputConsumer(
- InputConsumer.NO_OP, mGestureState, event);
+ mUncheckedConsumer = tryCreateAssistantInputConsumer(mGestureState, event);
} else if (mDeviceState.canTriggerOneHandedAction(event)) {
// Consume gesture event for triggering one handed feature.
mUncheckedConsumer = new OneHandedModeInputConsumer(this, mDeviceState,
@@ -649,12 +651,17 @@
switch (event.getActionMasked()) {
case ACTION_DOWN:
case ACTION_UP:
- ActiveGestureLog.INSTANCE.addLog("onMotionEvent("
- + (int) event.getRawX() + ", " + (int) event.getRawY() + ")",
- event.getActionMasked());
+ ActiveGestureLog.INSTANCE.addLog(
+ /* event= */ "onMotionEvent(" + (int) event.getRawX() + ", "
+ + (int) event.getRawY() + "): "
+ + MotionEvent.actionToString(event.getActionMasked()),
+ /* gestureEvent= */ event.getActionMasked() == ACTION_DOWN
+ ? MOTION_DOWN
+ : MOTION_UP);
break;
default:
- ActiveGestureLog.INSTANCE.addLog("onMotionEvent", event.getActionMasked());
+ ActiveGestureLog.INSTANCE.addLog("onMotionEvent: "
+ + MotionEvent.actionToString(event.getActionMasked()));
break;
}
}
@@ -676,87 +683,137 @@
ProtoTracer.INSTANCE.get(this).scheduleFrameUpdate();
}
- private InputConsumer tryCreateAssistantInputConsumer(InputConsumer base,
+ private InputConsumer tryCreateAssistantInputConsumer(
GestureState gestureState, MotionEvent motionEvent) {
- return mDeviceState.isGestureBlockedTask(gestureState.getRunningTask())
- ? base
- : new AssistantInputConsumer(this, gestureState, base, mInputMonitorCompat,
- mDeviceState, motionEvent);
+ return tryCreateAssistantInputConsumer(
+ InputConsumer.NO_OP, gestureState, motionEvent, CompoundString.NO_OP);
+ }
+
+ private InputConsumer tryCreateAssistantInputConsumer(
+ InputConsumer base,
+ GestureState gestureState,
+ MotionEvent motionEvent,
+ CompoundString reasonString) {
+ if (mDeviceState.isGestureBlockedTask(gestureState.getRunningTask())) {
+ reasonString.append(SUBSTRING_PREFIX)
+ .append("is gesture-blocked task, using base input consumer");
+ return base;
+ } else {
+ reasonString.append(SUBSTRING_PREFIX).append("using AssistantInputConsumer");
+ return new AssistantInputConsumer(
+ this, gestureState, base, mInputMonitorCompat, mDeviceState, motionEvent);
+ }
}
public GestureState createGestureState(GestureState previousGestureState) {
- GestureState gestureState = new GestureState(mOverviewComponentObserver,
- ActiveGestureLog.INSTANCE.generateAndSetLogId());
+ final GestureState gestureState;
+ TopTaskTracker.CachedTaskInfo taskInfo;
if (mTaskAnimationManager.isRecentsAnimationRunning()) {
- gestureState.updateRunningTask(previousGestureState.getRunningTask());
+ gestureState = new GestureState(mOverviewComponentObserver,
+ ActiveGestureLog.INSTANCE.getLogId());
+ taskInfo = previousGestureState.getRunningTask();
+ gestureState.updateRunningTask(taskInfo);
gestureState.updateLastStartedTaskId(previousGestureState.getLastStartedTaskId());
gestureState.updatePreviouslyAppearedTaskIds(
previousGestureState.getPreviouslyAppearedTaskIds());
} else {
- gestureState.updateRunningTask(
- TopTaskTracker.INSTANCE.get(this).getCachedTopTask(false));
+ gestureState = new GestureState(mOverviewComponentObserver,
+ ActiveGestureLog.INSTANCE.incrementLogId());
+ taskInfo = TopTaskTracker.INSTANCE.get(this).getCachedTopTask(false);
+ gestureState.updateRunningTask(taskInfo);
}
+ // Log initial state for the gesture.
+ ActiveGestureLog.INSTANCE.addLog(new CompoundString("Current running task package name=")
+ .append(taskInfo == null ? "no running task" : taskInfo.getPackageName()));
+ ActiveGestureLog.INSTANCE.addLog(new CompoundString("Current SystemUi state flags=")
+ .append(mDeviceState.getSystemUiStateString()));
return gestureState;
}
- private InputConsumer newConsumer(GestureState previousGestureState,
- GestureState newGestureState, MotionEvent event) {
+ private InputConsumer newConsumer(
+ GestureState previousGestureState, GestureState newGestureState, MotionEvent event) {
AnimatedFloat progressProxy = mSwipeUpProxyProvider.apply(mGestureState);
if (progressProxy != null) {
- return new ProgressDelegateInputConsumer(this, mTaskAnimationManager,
- mGestureState, mInputMonitorCompat, progressProxy);
+ InputConsumer consumer = new ProgressDelegateInputConsumer(
+ this, mTaskAnimationManager, mGestureState, mInputMonitorCompat, progressProxy);
+
+ logInputConsumerSelectionReason(consumer, newCompoundString(
+ "mSwipeUpProxyProvider has been set, using ProgressDelegateInputConsumer"));
+
+ return consumer;
}
boolean canStartSystemGesture = mDeviceState.canStartSystemGesture();
if (!mDeviceState.isUserUnlocked()) {
+ CompoundString reasonString = newCompoundString("device locked");
+ InputConsumer consumer;
if (canStartSystemGesture) {
// This handles apps launched in direct boot mode (e.g. dialer) as well as apps
// launched while device is locked even after exiting direct boot mode (e.g. camera).
- return createDeviceLockedInputConsumer(newGestureState);
+ consumer = createDeviceLockedInputConsumer(
+ newGestureState, reasonString.append(SUBSTRING_PREFIX)
+ .append("can start system gesture"));
} else {
- return getDefaultInputConsumer();
+ consumer = getDefaultInputConsumer(
+ reasonString.append(SUBSTRING_PREFIX)
+ .append("cannot start system gesture"));
}
+ logInputConsumerSelectionReason(consumer, reasonString);
+ return consumer;
}
+ CompoundString reasonString;
+ InputConsumer base;
// When there is an existing recents animation running, bypass systemState check as this is
// a followup gesture and the first gesture started in a valid system state.
- InputConsumer base = canStartSystemGesture
- || previousGestureState.isRecentsAnimationRunning()
- ? newBaseConsumer(previousGestureState, newGestureState, event)
- : getDefaultInputConsumer();
+ if (canStartSystemGesture || previousGestureState.isRecentsAnimationRunning()) {
+ reasonString = newCompoundString(canStartSystemGesture
+ ? "can start system gesture" : "recents animation was running")
+ .append(", trying to use base consumer");
+ base = newBaseConsumer(previousGestureState, newGestureState, event, reasonString);
+ } else {
+ reasonString = newCompoundString(
+ "cannot start system gesture and recents animation was not running")
+ .append(", trying to use default input consumer");
+ base = getDefaultInputConsumer(reasonString);
+ }
if (mDeviceState.isGesturalNavMode()) {
handleOrientationSetup(base);
}
if (mDeviceState.isFullyGesturalNavMode()) {
+ String reasonPrefix = "device is in gesture navigation mode";
if (mDeviceState.canTriggerAssistantAction(event)) {
- base = tryCreateAssistantInputConsumer(base, newGestureState, event);
+ reasonString.append(NEWLINE_PREFIX)
+ .append(reasonPrefix)
+ .append(SUBSTRING_PREFIX)
+ .append("gesture can trigger the assistant")
+ .append(", trying to use assistant input consumer");
+ base = tryCreateAssistantInputConsumer(base, newGestureState, event, reasonString);
}
// If Taskbar is present, we listen for long press to unstash it.
TaskbarActivityContext tac = mTaskbarManager.getCurrentActivityContext();
if (tac != null) {
+ reasonString.append(NEWLINE_PREFIX)
+ .append(reasonPrefix)
+ .append(SUBSTRING_PREFIX)
+ .append("TaskbarActivityContext != null, using TaskbarStashInputConsumer");
base = new TaskbarStashInputConsumer(this, base, mInputMonitorCompat, tac);
}
if (mDeviceState.isBubblesExpanded()) {
- if (BUBBLES_HOME_GESTURE_ENABLED) {
- // Bubbles can handle home gesture itself.
- base = getDefaultInputConsumer();
- } else {
- // If Bubbles is expanded, use the overlay input consumer, which will close
- // Bubbles instead of going all the way home when a swipe up is detected.
- // Notification panel can be expanded on top of expanded bubbles. Bubbles remain
- // expanded in the back. Make sure swipe up is not passed to bubbles in this
- // case.
- if (!mDeviceState.isNotificationPanelExpanded()) {
- base = new SysUiOverlayInputConsumer(
- getBaseContext(), mDeviceState, mInputMonitorCompat);
- }
- }
+ reasonString = newCompoundString(reasonPrefix)
+ .append(SUBSTRING_PREFIX)
+ .append("bubbles expanded, trying to use default input consumer");
+ // Bubbles can handle home gesture itself.
+ base = getDefaultInputConsumer(reasonString);
}
if (mDeviceState.isSystemUiDialogShowing()) {
+ reasonString = newCompoundString(reasonPrefix)
+ .append(SUBSTRING_PREFIX)
+ .append("system dialog is showing, using SysUiOverlayInputConsumer");
base = new SysUiOverlayInputConsumer(
getBaseContext(), mDeviceState, mInputMonitorCompat);
}
@@ -764,44 +821,94 @@
if (mDeviceState.isScreenPinningActive()) {
+ reasonString = newCompoundString(reasonPrefix)
+ .append(SUBSTRING_PREFIX)
+ .append("screen pinning is active, using ScreenPinnedInputConsumer");
// Note: we only allow accessibility to wrap this, and it replaces the previous
// base input consumer (which should be NO_OP anyway since topTaskLocked == true).
base = new ScreenPinnedInputConsumer(this, newGestureState);
}
if (mDeviceState.canTriggerOneHandedAction(event)) {
- base = new OneHandedModeInputConsumer(this, mDeviceState, base,
- mInputMonitorCompat);
+ reasonString.append(NEWLINE_PREFIX)
+ .append(reasonPrefix)
+ .append(SUBSTRING_PREFIX)
+ .append("gesture can trigger one handed mode")
+ .append(", using OneHandedModeInputConsumer");
+ base = new OneHandedModeInputConsumer(
+ this, mDeviceState, base, mInputMonitorCompat);
}
if (mDeviceState.isAccessibilityMenuAvailable()) {
- base = new AccessibilityInputConsumer(this, mDeviceState, base,
- mInputMonitorCompat);
+ reasonString.append(NEWLINE_PREFIX)
+ .append(reasonPrefix)
+ .append(SUBSTRING_PREFIX)
+ .append("accessibility menu is available")
+ .append(", using AccessibilityInputConsumer");
+ base = new AccessibilityInputConsumer(
+ this, mDeviceState, base, mInputMonitorCompat);
}
} else {
+ String reasonPrefix = "device is not in gesture navigation mode";
if (mDeviceState.isScreenPinningActive()) {
- base = getDefaultInputConsumer();
+ reasonString = newCompoundString(reasonPrefix)
+ .append(SUBSTRING_PREFIX)
+ .append("screen pinning is active, trying to use default input consumer");
+ base = getDefaultInputConsumer(reasonString);
}
if (mDeviceState.canTriggerOneHandedAction(event)) {
- base = new OneHandedModeInputConsumer(this, mDeviceState, base,
- mInputMonitorCompat);
+ reasonString.append(NEWLINE_PREFIX)
+ .append(reasonPrefix)
+ .append(SUBSTRING_PREFIX)
+ .append("gesture can trigger one handed mode")
+ .append(", using OneHandedModeInputConsumer");
+ base = new OneHandedModeInputConsumer(
+ this, mDeviceState, base, mInputMonitorCompat);
}
}
+ logInputConsumerSelectionReason(base, reasonString);
return base;
}
+ private CompoundString newCompoundString(String substring) {
+ return new CompoundString(NEWLINE_PREFIX).append(substring);
+ }
+
+ private void logInputConsumerSelectionReason(
+ InputConsumer consumer, CompoundString reasonString) {
+ if (!FeatureFlags.ENABLE_INPUT_CONSUMER_REASON_LOGGING.get()) {
+ ActiveGestureLog.INSTANCE.addLog("setInputConsumer: " + consumer.getName());
+ return;
+ }
+ ActiveGestureLog.INSTANCE.addLog(new CompoundString("setInputConsumer: ")
+ .append(consumer.getName())
+ .append(". reason(s):")
+ .append(reasonString));
+ if ((consumer.getType() & InputConsumer.TYPE_OTHER_ACTIVITY) != 0) {
+ ActiveGestureLog.INSTANCE.trackEvent(FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER);
+ }
+ }
+
private void handleOrientationSetup(InputConsumer baseInputConsumer) {
baseInputConsumer.notifyOrientationSetup();
}
- private InputConsumer newBaseConsumer(GestureState previousGestureState,
- GestureState gestureState, MotionEvent event) {
+ private InputConsumer newBaseConsumer(
+ GestureState previousGestureState,
+ GestureState gestureState,
+ MotionEvent event,
+ CompoundString reasonString) {
if (mDeviceState.isKeyguardShowingOccluded()) {
// This handles apps showing over the lockscreen (e.g. camera)
- return createDeviceLockedInputConsumer(gestureState);
+ return createDeviceLockedInputConsumer(
+ gestureState,
+ reasonString.append(SUBSTRING_PREFIX)
+ .append("keyguard is showing occluded")
+ .append(", trying to use device locked input consumer"));
}
+ reasonString.append(SUBSTRING_PREFIX).append("keyguard is not showing occluded");
// Use overview input consumer for sharesheets on top of home.
boolean forceOverviewInputConsumer = gestureState.getActivityInterface().isStarted()
&& gestureState.getRunningTask() != null
@@ -815,23 +922,45 @@
forceOverviewInputConsumer = gestureState.getRunningTask().isHomeTask();
}
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()
- && gestureState.getActivityInterface().isInLiveTileMode()) {
+ boolean previousGestureAnimatedToLauncher =
+ previousGestureState.isRunningAnimationToLauncher();
+ // with shell-transitions, home is resumed during recents animation, so
+ // explicitly check against recents animation too.
+ boolean launcherResumedThroughShellTransition =
+ gestureState.getActivityInterface().isResumed()
+ && !previousGestureState.isRecentsAnimationRunning();
+ if (gestureState.getActivityInterface().isInLiveTileMode()) {
return createOverviewInputConsumer(
- previousGestureState, gestureState, event, forceOverviewInputConsumer);
+ previousGestureState,
+ gestureState,
+ event,
+ forceOverviewInputConsumer,
+ reasonString.append(SUBSTRING_PREFIX)
+ .append("is in live tile mode, trying to use overview input consumer"));
} else if (gestureState.getRunningTask() == null) {
- return getDefaultInputConsumer();
- } else if (previousGestureState.isRunningAnimationToLauncher()
- || (gestureState.getActivityInterface().isResumed()
- // with shell-transitions, home is resumed during recents animation, so
- // explicitly check against recents animation too.
- && !previousGestureState.isRecentsAnimationRunning())
+ return getDefaultInputConsumer(reasonString.append(SUBSTRING_PREFIX)
+ .append("running task == null"));
+ } else if (previousGestureAnimatedToLauncher
+ || launcherResumedThroughShellTransition
|| forceOverviewInputConsumer) {
return createOverviewInputConsumer(
- previousGestureState, gestureState, event, forceOverviewInputConsumer);
+ previousGestureState,
+ gestureState,
+ event,
+ forceOverviewInputConsumer,
+ reasonString.append(SUBSTRING_PREFIX)
+ .append(previousGestureAnimatedToLauncher
+ ? "previous gesture animated to launcher"
+ : (launcherResumedThroughShellTransition
+ ? "launcher resumed through a shell transition"
+ : "forceOverviewInputConsumer == true"))
+ .append(", trying to use overview input consumer"));
} else if (mDeviceState.isGestureBlockedTask(gestureState.getRunningTask())) {
- return getDefaultInputConsumer();
+ return getDefaultInputConsumer(reasonString.append(SUBSTRING_PREFIX)
+ .append("is gesture-blocked task, trying to use default input consumer"));
} else {
+ reasonString.append(SUBSTRING_PREFIX)
+ .append("using OtherActivityInputConsumer");
return createOtherActivityInputConsumer(gestureState, event);
}
}
@@ -853,32 +982,63 @@
mInputMonitorCompat, mInputEventReceiver, disableHorizontalSwipe, factory);
}
- private InputConsumer createDeviceLockedInputConsumer(GestureState gestureState) {
+ private InputConsumer createDeviceLockedInputConsumer(
+ GestureState gestureState, CompoundString reasonString) {
if (mDeviceState.isFullyGesturalNavMode() && gestureState.getRunningTask() != null) {
- return new DeviceLockedInputConsumer(this, mDeviceState, mTaskAnimationManager,
- gestureState, mInputMonitorCompat);
+ reasonString.append(SUBSTRING_PREFIX)
+ .append("device is in gesture nav mode and running task != null")
+ .append(", using DeviceLockedInputConsumer");
+ return new DeviceLockedInputConsumer(
+ this, mDeviceState, mTaskAnimationManager, gestureState, mInputMonitorCompat);
} else {
- return getDefaultInputConsumer();
+ return getDefaultInputConsumer(reasonString
+ .append(SUBSTRING_PREFIX)
+ .append(mDeviceState.isFullyGesturalNavMode()
+ ? "running task == null" : "device is not in gesture nav mode")
+ .append(", trying to use default input consumer"));
}
}
- public InputConsumer createOverviewInputConsumer(GestureState previousGestureState,
- GestureState gestureState, MotionEvent event,
- boolean forceOverviewInputConsumer) {
+ public InputConsumer createOverviewInputConsumer(
+ GestureState previousGestureState,
+ GestureState gestureState,
+ MotionEvent event,
+ boolean forceOverviewInputConsumer,
+ CompoundString reasonString) {
StatefulActivity activity = gestureState.getActivityInterface().getCreatedActivity();
if (activity == null) {
- return getDefaultInputConsumer();
+ return getDefaultInputConsumer(
+ reasonString.append(SUBSTRING_PREFIX)
+ .append("activity == null, trying to use default input consumer"));
}
- if (activity.getRootView().hasWindowFocus()
- || previousGestureState.isRunningAnimationToLauncher()
- || (ASSISTANT_GIVES_LAUNCHER_FOCUS.get()
- && forceOverviewInputConsumer)
- || (ENABLE_QUICKSTEP_LIVE_TILE.get()
- && gestureState.getActivityInterface().isInLiveTileMode())) {
+ boolean hasWindowFocus = activity.getRootView().hasWindowFocus();
+ boolean isPreviousGestureAnimatingToLauncher =
+ previousGestureState.isRunningAnimationToLauncher();
+ boolean forcingOverviewInputConsumer =
+ ASSISTANT_GIVES_LAUNCHER_FOCUS.get() && forceOverviewInputConsumer;
+ boolean isInLiveTileMode = gestureState.getActivityInterface().isInLiveTileMode();
+ reasonString.append(SUBSTRING_PREFIX)
+ .append(hasWindowFocus
+ ? "activity has window focus"
+ : (isPreviousGestureAnimatingToLauncher
+ ? "previous gesture is still animating to launcher"
+ : (forcingOverviewInputConsumer
+ ? "assistant gives launcher focus and forcing focus"
+ : (isInLiveTileMode
+ ? "device is in live mode"
+ : "all overview focus conditions failed"))));
+ if (hasWindowFocus
+ || isPreviousGestureAnimatingToLauncher
+ || forcingOverviewInputConsumer
+ || isInLiveTileMode) {
+ reasonString.append(SUBSTRING_PREFIX)
+ .append("overview should have focus, using OverviewInputConsumer");
return new OverviewInputConsumer(gestureState, activity, mInputMonitorCompat,
false /* startingInActivityBounds */);
} else {
+ reasonString.append(SUBSTRING_PREFIX).append(
+ "overview shouldn't have focus, using OverviewWithoutFocusInputConsumer");
final boolean disableHorizontalSwipe = mDeviceState.isInExclusionRegion(event);
return new OverviewWithoutFocusInputConsumer(activity, mDeviceState, gestureState,
mInputMonitorCompat, disableHorizontalSwipe);
@@ -906,13 +1066,21 @@
}
}
+ private @NonNull InputConsumer getDefaultInputConsumer() {
+ return getDefaultInputConsumer(CompoundString.NO_OP);
+ }
+
/**
* Returns the {@link ResetGestureInputConsumer} if user is unlocked, else NO_OP.
*/
- private @NonNull InputConsumer getDefaultInputConsumer() {
+ private @NonNull InputConsumer getDefaultInputConsumer(@NonNull CompoundString reasonString) {
if (mResetGestureInputConsumer != null) {
+ reasonString.append(SUBSTRING_PREFIX).append(
+ "mResetGestureInputConsumer initialized, using ResetGestureInputConsumer");
return mResetGestureInputConsumer;
} else {
+ reasonString.append(SUBSTRING_PREFIX).append(
+ "mResetGestureInputConsumer not initialized, using no-op input consumer");
// mResetGestureInputConsumer isn't initialized until onUserUnlocked(), so reset to
// NO_OP until then (we never want these to be null).
return InputConsumer.NO_OP;
@@ -1030,19 +1198,36 @@
createdOverviewActivity.getDeviceProfile().dump(this, "", pw);
}
mTaskbarManager.dumpLogs("", pw);
+
+ if (FeatureFlags.CONTINUOUS_VIEW_TREE_CAPTURE.get()) {
+ ViewCapture.getInstance().dump(pw, fd, this);
+ }
}
}
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) {
- switch (args.pollFirst()) {
+ String cmd = args.pollFirst();
+ if (cmd == null) {
+ pw.println("Command missing");
+ printAvailableCommands(pw);
+ return;
+ }
+ switch (cmd) {
case "clear-touch-log":
ActiveGestureLog.INSTANCE.clear();
break;
+ case "print-gesture-log":
+ ActiveGestureLog.INSTANCE.dump("", pw);
+ break;
+ default:
+ pw.println("Command does not exist: " + cmd);
+ printAvailableCommands(pw);
}
}
diff --git a/quickstep/src/com/android/quickstep/ViewUtils.java b/quickstep/src/com/android/quickstep/ViewUtils.java
index 1bb95b9..b132067 100644
--- a/quickstep/src/com/android/quickstep/ViewUtils.java
+++ b/quickstep/src/com/android/quickstep/ViewUtils.java
@@ -53,13 +53,13 @@
final Runnable mFinishCallback;
final BooleanSupplier mCancelled;
final Handler mHandler;
+ boolean mSurfaceCallbackRegistered = false;
boolean mFinished;
int mDeferFrameCount = 1;
FrameHandler(View view, Runnable finishCallback, BooleanSupplier cancelled) {
mViewRoot = view.getViewRootImpl();
- mViewRoot.addSurfaceChangedCallback(this);
mFinishCallback = finishCallback;
mCancelled = cancelled;
mHandler = new Handler();
@@ -103,6 +103,10 @@
private boolean schedule() {
if (mViewRoot != null && mViewRoot.getView() != null) {
+ if (!mSurfaceCallbackRegistered) {
+ mSurfaceCallbackRegistered = true;
+ mViewRoot.addSurfaceChangedCallback(this);
+ }
mViewRoot.registerRtFrameCallback(this);
mViewRoot.getView().invalidate();
return true;
@@ -119,7 +123,10 @@
if (mFinishCallback != null) {
mFinishCallback.run();
}
- mViewRoot.removeSurfaceChangedCallback(this);
+ if (mViewRoot != null) {
+ mViewRoot.removeSurfaceChangedCallback(this);
+ mSurfaceCallbackRegistered = false;
+ }
}
}
}
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackNavBarTouchController.java b/quickstep/src/com/android/quickstep/fallback/FallbackNavBarTouchController.java
index 3e01ed0..8a87f63 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackNavBarTouchController.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackNavBarTouchController.java
@@ -22,7 +22,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.util.DisplayController;
-import com.android.launcher3.util.DisplayController.NavigationMode;
+import com.android.launcher3.util.NavigationMode;
import com.android.launcher3.util.TouchController;
import com.android.quickstep.RecentsActivity;
import com.android.quickstep.util.NavBarPosition;
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
index c7c3441..062e50e 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
@@ -27,13 +27,13 @@
import static com.android.quickstep.fallback.RecentsState.OVERVIEW_SPLIT_SELECT;
import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
-import static com.android.quickstep.views.RecentsView.OVERVIEW_PROGRESS;
import static com.android.quickstep.views.RecentsView.RECENTS_GRID_PROGRESS;
import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
import static com.android.quickstep.views.RecentsView.TASK_MODALNESS;
import static com.android.quickstep.views.RecentsView.TASK_PRIMARY_SPLIT_TRANSLATION;
import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_SPLIT_TRANSLATION;
import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION;
+import static com.android.quickstep.views.RecentsView.TASK_THUMBNAIL_SPLASH_ALPHA;
import static com.android.quickstep.views.TaskView.FLAG_UPDATE_ALL;
import android.util.FloatProperty;
@@ -45,7 +45,7 @@
import com.android.launcher3.anim.PropertySetter;
import com.android.launcher3.statemanager.StateManager.StateHandler;
import com.android.launcher3.states.StateAnimationConfig;
-import com.android.launcher3.util.MultiValueAlpha;
+import com.android.launcher3.util.MultiPropertyFactory;
import com.android.quickstep.RecentsActivity;
import com.android.quickstep.views.ClearAllButton;
@@ -78,6 +78,11 @@
}
// While animating into recents, update the visible task data as needed
setter.addOnFrameCallback(() -> mRecentsView.loadVisibleTaskData(FLAG_UPDATE_ALL));
+ setter.addEndListener(success -> {
+ if (!success) {
+ mRecentsView.reset();
+ }
+ });
mRecentsView.updateEmptyMessage();
setProperties(toState, config, setter);
@@ -90,7 +95,7 @@
clearAllButtonAlpha, LINEAR);
float overviewButtonAlpha = state.hasOverviewActions() ? 1 : 0;
setter.setFloat(mActivity.getActionsView().getVisibilityAlpha(),
- MultiValueAlpha.VALUE, overviewButtonAlpha, LINEAR);
+ MultiPropertyFactory.MULTI_PROPERTY_VALUE, overviewButtonAlpha, LINEAR);
float[] scaleAndOffset = state.getOverviewScaleAndOffset(mActivity);
setter.setFloat(mRecentsView, RECENTS_SCALE_PROPERTY, scaleAndOffset[0],
@@ -106,16 +111,19 @@
boolean showAsGrid = state.displayOverviewTasksAsGrid(mActivity.getDeviceProfile());
setter.setFloat(mRecentsView, RECENTS_GRID_PROGRESS, showAsGrid ? 1f : 0f,
showAsGrid ? INSTANT : FINAL_FRAME);
- setter.setFloat(mRecentsView, OVERVIEW_PROGRESS, state == RecentsState.DEFAULT ? 1f : 0f,
- INSTANT);
+ setter.setFloat(mRecentsView, TASK_THUMBNAIL_SPLASH_ALPHA,
+ state.showTaskThumbnailSplash() ? 1f : 0f, INSTANT);
setter.setViewBackgroundColor(mActivity.getScrimView(), state.getScrimColor(mActivity),
config.getInterpolator(ANIM_SCRIM_FADE, LINEAR));
RecentsState currentState = mActivity.getStateManager().getState();
if (isSplitSelectionState(state) && !isSplitSelectionState(currentState)) {
- setter.add(mRecentsView.createSplitSelectInitAnimation(
- state.getTransitionDuration(mActivity, true /* isToState */)).buildAnim());
+ int duration = state.getTransitionDuration(mActivity, true /* isToState */);
+ // TODO (b/246851887): Pass in setter as a NO_ANIM PendingAnimation instead
+ PendingAnimation pa = new PendingAnimation(duration);
+ mRecentsView.createSplitSelectInitAnimation(pa, duration);
+ setter.add(pa.buildAnim());
}
Pair<FloatProperty, FloatProperty> taskViewsFloat =
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index eb739a6..bcaae99 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -33,6 +33,7 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.popup.QuickstepSystemShortcut;
import com.android.launcher3.statemanager.StateManager.StateListener;
import com.android.launcher3.util.SplitConfigurationOptions;
@@ -54,6 +55,8 @@
public class FallbackRecentsView extends RecentsView<RecentsActivity, RecentsState>
implements StateListener<RecentsState> {
+ private static final int TASK_DISMISS_DURATION = 150;
+
@Nullable
private Task mHomeTask;
@@ -105,8 +108,9 @@
if (mHomeTask != null && endTarget == RECENTS && animatorSet != null) {
TaskView tv = getTaskViewByTaskId(mHomeTask.key.id);
if (tv != null) {
- PendingAnimation pa = createTaskDismissAnimation(tv, true, false, 150,
- false /* dismissingForSplitSelection*/);
+ PendingAnimation pa = new PendingAnimation(TASK_DISMISS_DURATION);
+ createTaskDismissAnimation(pa, tv, true, false,
+ TASK_DISMISS_DURATION, false /* dismissingForSplitSelection*/);
pa.addEndListener(e -> setCurrentTask(-1));
AnimatorPlaybackController controller = pa.createPlaybackController();
controller.dispatchOnStart();
@@ -196,13 +200,12 @@
}
@Override
- public void setModalStateEnabled(boolean isModalState) {
- super.setModalStateEnabled(isModalState);
+ public void setModalStateEnabled(boolean isModalState, boolean animate) {
if (isModalState) {
- mActivity.getStateManager().goToState(RecentsState.MODAL_TASK);
+ mActivity.getStateManager().goToState(RecentsState.MODAL_TASK, animate);
} else {
if (mActivity.isInState(RecentsState.MODAL_TASK)) {
- mActivity.getStateManager().goToState(DEFAULT);
+ mActivity.getStateManager().goToState(DEFAULT, animate);
resetModalVisuals();
}
}
@@ -210,8 +213,9 @@
@Override
public void initiateSplitSelect(TaskView taskView,
- @SplitConfigurationOptions.StagePosition int stagePosition) {
- super.initiateSplitSelect(taskView, stagePosition);
+ @SplitConfigurationOptions.StagePosition int stagePosition,
+ StatsLogManager.EventEnum splitEvent) {
+ super.initiateSplitSelect(taskView, stagePosition, splitEvent);
mActivity.getStateManager().goToState(OVERVIEW_SPLIT_SELECT);
}
@@ -238,6 +242,9 @@
if (finalState != MODAL_TASK) {
setOverviewSelectEnabled(false);
}
+ if (finalState != OVERVIEW_SPLIT_SELECT) {
+ resetFromSplitSelectionState();
+ }
if (isOverlayEnabled) {
runActionOnRemoteHandles(remoteTargetHandle ->
diff --git a/quickstep/src/com/android/quickstep/fallback/RecentsState.java b/quickstep/src/com/android/quickstep/fallback/RecentsState.java
index af9d0cb..8b5f091 100644
--- a/quickstep/src/com/android/quickstep/fallback/RecentsState.java
+++ b/quickstep/src/com/android/quickstep/fallback/RecentsState.java
@@ -41,6 +41,7 @@
private static final int FLAG_SCRIM = BaseState.getFlag(5);
private static final int FLAG_LIVE_TILE = BaseState.getFlag(6);
private static final int FLAG_OVERVIEW_UI = BaseState.getFlag(7);
+ private static final int FLAG_TASK_THUMBNAIL_SPLASH = BaseState.getFlag(8);
public static final RecentsState DEFAULT = new RecentsState(0,
FLAG_DISABLE_RESTORE | FLAG_CLEAR_ALL_BUTTON | FLAG_OVERVIEW_ACTIONS | FLAG_SHOW_AS_GRID
@@ -49,11 +50,13 @@
FLAG_DISABLE_RESTORE | FLAG_CLEAR_ALL_BUTTON | FLAG_OVERVIEW_ACTIONS | FLAG_MODAL
| FLAG_SHOW_AS_GRID | FLAG_SCRIM | FLAG_LIVE_TILE | FLAG_OVERVIEW_UI);
public static final RecentsState BACKGROUND_APP = new BackgroundAppState(2,
- FLAG_DISABLE_RESTORE | FLAG_NON_INTERACTIVE | FLAG_FULL_SCREEN | FLAG_OVERVIEW_UI);
+ FLAG_DISABLE_RESTORE | FLAG_NON_INTERACTIVE | FLAG_FULL_SCREEN | FLAG_OVERVIEW_UI
+ | FLAG_TASK_THUMBNAIL_SPLASH);
public static final RecentsState HOME = new RecentsState(3, 0);
public static final RecentsState BG_LAUNCHER = new LauncherState(4, 0);
public static final RecentsState OVERVIEW_SPLIT_SELECT = new RecentsState(5,
- FLAG_SHOW_AS_GRID | FLAG_SCRIM | FLAG_OVERVIEW_UI | FLAG_CLOSE_POPUPS);
+ FLAG_SHOW_AS_GRID | FLAG_SCRIM | FLAG_OVERVIEW_UI | FLAG_CLOSE_POPUPS
+ | FLAG_DISABLE_RESTORE);
public final int ordinal;
private final int mFlags;
@@ -139,6 +142,11 @@
return hasFlag(FLAG_SHOW_AS_GRID) && deviceProfile.isTablet;
}
+ @Override
+ public boolean showTaskThumbnailSplash() {
+ return hasFlag(FLAG_TASK_THUMBNAIL_SPLASH);
+ }
+
/**
* True if the state has overview panel visible.
*/
diff --git a/quickstep/src/com/android/quickstep/fallback/RecentsTaskController.java b/quickstep/src/com/android/quickstep/fallback/RecentsTaskController.java
index eca61bb..db4927a 100644
--- a/quickstep/src/com/android/quickstep/fallback/RecentsTaskController.java
+++ b/quickstep/src/com/android/quickstep/fallback/RecentsTaskController.java
@@ -15,8 +15,6 @@
*/
package com.android.quickstep.fallback;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
-
import com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController;
import com.android.quickstep.RecentsActivity;
@@ -28,8 +26,7 @@
@Override
protected boolean isRecentsInteractive() {
- return mActivity.hasWindowFocus() || (ENABLE_QUICKSTEP_LIVE_TILE.get()
- && mActivity.getStateManager().getState().hasLiveTile());
+ return mActivity.hasWindowFocus() || mActivity.getStateManager().getState().hasLiveTile();
}
@Override
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
index 6bc24f2..8410149 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
@@ -37,6 +37,7 @@
import android.graphics.Point;
import android.graphics.PointF;
import android.view.MotionEvent;
+import android.view.RemoteAnimationTarget;
import android.view.VelocityTracker;
import com.android.launcher3.R;
@@ -53,13 +54,12 @@
import com.android.quickstep.RecentsAnimationDeviceState;
import com.android.quickstep.RecentsAnimationTargets;
import com.android.quickstep.TaskAnimationManager;
+import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties;
import com.android.quickstep.util.TransformParams;
import com.android.quickstep.util.TransformParams.BuilderProxy;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.InputMonitorCompat;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder;
import java.util.HashMap;
@@ -290,9 +290,9 @@
@Override
public void onBuildTargetParams(
- Builder builder, RemoteAnimationTargetCompat app, TransformParams params) {
+ SurfaceProperties builder, RemoteAnimationTarget app, TransformParams params) {
mMatrix.setTranslate(0, mProgress.value * mMaxTranslationY);
- builder.withMatrix(mMatrix);
+ builder.setMatrix(mMatrix);
}
@Override
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index 92d3d23..b3d3c3d 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -36,10 +36,9 @@
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
+import android.content.res.Resources;
import android.graphics.PointF;
import android.os.Build;
-import android.os.Handler;
-import android.os.Looper;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
@@ -49,9 +48,11 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.taskbar.TaskbarUIController;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.tracing.InputConsumerProto;
+import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.TraceHelper;
import com.android.quickstep.AbsSwipeUpHandler;
@@ -65,11 +66,9 @@
import com.android.quickstep.RecentsAnimationTargets;
import com.android.quickstep.RotationTouchHelper;
import com.android.quickstep.TaskAnimationManager;
-import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.CachedEventDispatcher;
import com.android.quickstep.util.MotionPauseDetector;
import com.android.quickstep.util.NavBarPosition;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.InputChannelCompat.InputEventReceiver;
import com.android.systemui.shared.system.InputMonitorCompat;
@@ -135,11 +134,9 @@
// Might be displacement in X or Y, depending on the direction we are swiping from the nav bar.
private float mStartDisplacement;
- private Handler mMainThreadHandler;
- private Runnable mCancelRecentsAnimationRunnable = () -> {
- ActivityManagerWrapper.getInstance().cancelRecentsAnimation(
- true /* restoreHomeStackPosition */);
- };
+ private final boolean mIsTransientTaskbar;
+ private final boolean mTaskbarAlreadyOpen;
+ private final int mTaskbarHomeOverviewThreshold;
public OtherActivityInputConsumer(Context base, RecentsAnimationDeviceState deviceState,
TaskAnimationManager taskAnimationManager, GestureState gestureState,
@@ -151,10 +148,13 @@
mNavBarPosition = mDeviceState.getNavBarPosition();
mTaskAnimationManager = taskAnimationManager;
mGestureState = gestureState;
- mMainThreadHandler = new Handler(Looper.getMainLooper());
mHandlerFactory = handlerFactory;
mActivityInterface = mGestureState.getActivityInterface();
+ Resources res = base.getResources();
+ mTaskbarHomeOverviewThreshold = res
+ .getDimensionPixelSize(R.dimen.taskbar_home_overview_threshold);
+
mMotionPauseDetector = new MotionPauseDetector(base, false,
mNavBarPosition.isLeftEdge() || mNavBarPosition.isRightEdge()
? MotionEvent.AXIS_X : MotionEvent.AXIS_Y);
@@ -165,6 +165,10 @@
mInputMonitorCompat = inputMonitorCompat;
mInputEventReceiver = inputEventReceiver;
+ TaskbarUIController controller = mActivityInterface.getTaskbarController();
+ mTaskbarAlreadyOpen = controller != null && !controller.isTaskbarStashed();
+ mIsTransientTaskbar = DisplayController.isTransientTaskbar(base);
+
boolean continuingPreviousGesture = mTaskAnimationManager.isRecentsAnimationRunning();
mIsDeferredDownTarget = !continuingPreviousGesture && isDeferredDownTarget;
@@ -291,6 +295,7 @@
float upDist = -displacement;
boolean passedSlop = squaredHypot(displacementX, displacementY)
>= mSquaredTouchSlop;
+
if (!mPassedSlopOnThisGesture && passedSlop) {
mPassedSlopOnThisGesture = true;
}
@@ -335,7 +340,13 @@
}
if (mDeviceState.isFullyGesturalNavMode()) {
- mMotionPauseDetector.setDisallowPause(upDist < mMotionPauseMinDisplacement
+ float minDisplacement = mMotionPauseMinDisplacement;
+
+ if (mIsTransientTaskbar && !mTaskbarAlreadyOpen) {
+ minDisplacement += mTaskbarHomeOverviewThreshold;
+ }
+
+ mMotionPauseDetector.setDisallowPause(upDist < minDisplacement
|| isLikelyToStartNewTask);
mMotionPauseDetector.addPosition(ev);
mInteractionHandler.setIsLikelyToStartNewTask(isLikelyToStartNewTask);
@@ -359,7 +370,6 @@
}
private void notifyGestureStarted(boolean isLikelyToStartNewTask) {
- ActiveGestureLog.INSTANCE.addLog("startQuickstep");
if (mInteractionHandler == null) {
return;
}
@@ -370,11 +380,11 @@
// Notify the handler that the gesture has actually started
mInteractionHandler.onGestureStarted(isLikelyToStartNewTask);
+
+ mInteractionHandler.setTaskbarAlreadyOpen(mTaskbarAlreadyOpen);
}
private void startTouchTrackingForWindowAnimation(long touchTimeMs) {
- ActiveGestureLog.INSTANCE.addLog("startRecentsAnimation");
-
mInteractionHandler = mHandlerFactory.newHandler(mGestureState, touchTimeMs);
mInteractionHandler.setGestureEndCallback(this::onInteractionGestureFinished);
mMotionPauseDetector.setOnMotionPauseListener(mInteractionHandler.getMotionPauseListener());
@@ -435,13 +445,6 @@
}
onConsumerAboutToBeSwitched();
onInteractionGestureFinished();
-
- // Cancel the recents animation if SysUI happens to handle UP before we have a chance
- // to start the recents animation. In addition, workaround for b/126336729 by delaying
- // the cancel of the animation for a period, in case SysUI is slow to handle UP and we
- // handle DOWN & UP and move the home stack before SysUI can start the activity
- mMainThreadHandler.removeCallbacks(mCancelRecentsAnimationRunnable);
- mMainThreadHandler.postDelayed(mCancelRecentsAnimationRunnable, 100);
}
cleanupAfterGesture();
TraceHelper.INSTANCE.endSection(traceToken);
@@ -463,7 +466,6 @@
@Override
public void onConsumerAboutToBeSwitched() {
Preconditions.assertUIThread();
- mMainThreadHandler.removeCallbacks(mCancelRecentsAnimationRunnable);
if (mInteractionHandler != null) {
// The consumer is being switched while we are active. Set up the shared state to be
// used by the next animation
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
index 7899c55..64165b6 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
@@ -15,7 +15,6 @@
*/
package com.android.quickstep.inputconsumers;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
import android.media.AudioManager;
@@ -35,7 +34,6 @@
import com.android.quickstep.GestureState;
import com.android.quickstep.InputConsumer;
import com.android.quickstep.TaskUtils;
-import com.android.quickstep.util.ActiveGestureLog;
import com.android.systemui.shared.system.InputMonitorCompat;
/**
@@ -91,7 +89,6 @@
if (!mStartingInActivityBounds) {
mActivityInterface.closeOverlay();
TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
- ActiveGestureLog.INSTANCE.addLog("startQuickstep");
}
if (mInputMonitor != null) {
TestLogging.recordEvent(TestProtocol.SEQUENCE_PILFER, "pilferPointers");
@@ -102,27 +99,23 @@
@Override
public void onHoverEvent(MotionEvent ev) {
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- mActivity.dispatchGenericMotionEvent(ev);
- }
+ mActivity.dispatchGenericMotionEvent(ev);
}
@Override
public void onKeyEvent(KeyEvent ev) {
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- switch (ev.getKeyCode()) {
- case KeyEvent.KEYCODE_VOLUME_DOWN:
- case KeyEvent.KEYCODE_VOLUME_UP:
- case KeyEvent.KEYCODE_VOLUME_MUTE:
- MediaSessionManager mgr = mActivity.getSystemService(MediaSessionManager.class);
- mgr.dispatchVolumeKeyEventAsSystemService(ev,
- AudioManager.USE_DEFAULT_STREAM_TYPE);
- break;
- default:
- break;
- }
- mActivity.dispatchKeyEvent(ev);
+ switch (ev.getKeyCode()) {
+ case KeyEvent.KEYCODE_VOLUME_DOWN:
+ case KeyEvent.KEYCODE_VOLUME_UP:
+ case KeyEvent.KEYCODE_VOLUME_MUTE:
+ MediaSessionManager mgr = mActivity.getSystemService(MediaSessionManager.class);
+ mgr.dispatchVolumeKeyEventAsSystemService(ev,
+ AudioManager.USE_DEFAULT_STREAM_TYPE);
+ break;
+ default:
+ break;
}
+ mActivity.dispatchKeyEvent(ev);
}
}
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
index bde4240..b70fe8e 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
@@ -32,7 +32,6 @@
import com.android.quickstep.GestureState;
import com.android.quickstep.InputConsumer;
import com.android.quickstep.RecentsAnimationDeviceState;
-import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.TriggerSwipeUpTouchTracker;
import com.android.systemui.shared.system.InputMonitorCompat;
@@ -79,7 +78,6 @@
@Override
public void onSwipeUp(boolean wasFling, PointF finalVelocity) {
startHomeIntentSafely(mContext, mGestureState.getHomeIntent(), null);
- ActiveGestureLog.INSTANCE.addLog("startQuickstep");
BaseActivity activity = BaseDraggingActivity.fromContext(mContext);
int state = (mGestureState != null && mGestureState.getEndTarget() != null)
? mGestureState.getEndTarget().containerType
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/ResetGestureInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/ResetGestureInputConsumer.java
index d34b40b..349f4d2 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/ResetGestureInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/ResetGestureInputConsumer.java
@@ -17,18 +17,25 @@
import android.view.MotionEvent;
+import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.quickstep.InputConsumer;
import com.android.quickstep.TaskAnimationManager;
+import java.util.function.Supplier;
+
/**
* A NO_OP input consumer which also resets any pending gesture
*/
public class ResetGestureInputConsumer implements InputConsumer {
private final TaskAnimationManager mTaskAnimationManager;
+ private final Supplier<TaskbarActivityContext> mActivityContextSupplier;
- public ResetGestureInputConsumer(TaskAnimationManager taskAnimationManager) {
+ public ResetGestureInputConsumer(
+ TaskAnimationManager taskAnimationManager,
+ Supplier<TaskbarActivityContext> activityContextSupplier) {
mTaskAnimationManager = taskAnimationManager;
+ mActivityContextSupplier = activityContextSupplier;
}
@Override
@@ -40,7 +47,9 @@
public void onMotionEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN
&& mTaskAnimationManager.isRecentsAnimationRunning()) {
- mTaskAnimationManager.finishRunningRecentsAnimation(false /* toHome */);
+ TaskbarActivityContext tac = mActivityContextSupplier.get();
+ mTaskAnimationManager.finishRunningRecentsAnimation(
+ /* toHome= */ tac != null && !tac.isInApp());
}
}
}
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java
index 3785de4..b7f2022 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java
@@ -15,16 +15,26 @@
*/
package com.android.quickstep.inputconsumers;
+import static android.view.MotionEvent.INVALID_POINTER_ID;
+
import static com.android.launcher3.Utilities.squaredHypot;
+import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_TOUCHING;
import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.PointF;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.taskbar.TaskbarActivityContext;
+import com.android.launcher3.taskbar.TaskbarTranslationController.TransitionCallback;
+import com.android.launcher3.touch.OverScroll;
+import com.android.launcher3.util.DisplayController;
import com.android.quickstep.InputConsumer;
import com.android.systemui.shared.system.InputMonitorCompat;
@@ -37,20 +47,36 @@
private final GestureDetector mLongPressDetector;
private final float mSquaredTouchSlop;
-
- private float mDownX, mDownY;
+ private float mLongPressDownX, mLongPressDownY;
private boolean mCanceledUnstashHint;
private final float mUnstashArea;
private final float mScreenWidth;
+ private final int mTaskbarThresholdY;
+ private boolean mHasPassedTaskbarThreshold;
+
+ private final PointF mDownPos = new PointF();
+ private final PointF mLastPos = new PointF();
+ private int mActivePointerId = INVALID_POINTER_ID;
+
+ private final boolean mIsTransientTaskbar;
+
+ private final @Nullable TransitionCallback mTransitionCallback;
+
public TaskbarStashInputConsumer(Context context, InputConsumer delegate,
InputMonitorCompat inputMonitor, TaskbarActivityContext taskbarActivityContext) {
super(delegate, inputMonitor);
mTaskbarActivityContext = taskbarActivityContext;
mSquaredTouchSlop = Utilities.squaredTouchSlop(context);
mScreenWidth = taskbarActivityContext.getDeviceProfile().widthPx;
- mUnstashArea = context.getResources()
- .getDimensionPixelSize(R.dimen.taskbar_unstash_input_area);
+
+ Resources res = context.getResources();
+ mUnstashArea = res.getDimensionPixelSize(R.dimen.taskbar_unstash_input_area);
+ int taskbarThreshold = res.getDimensionPixelSize(R.dimen.taskbar_nav_threshold);
+ int screenHeight = taskbarActivityContext.getDeviceProfile().heightPx;
+ mTaskbarThresholdY = screenHeight - taskbarThreshold;
+
+ mIsTransientTaskbar = DisplayController.isTransientTaskbar(context);
mLongPressDetector = new GestureDetector(context, new SimpleOnGestureListener() {
@Override
@@ -58,6 +84,10 @@
onLongPressDetected(motionEvent);
}
});
+
+ mTransitionCallback = mIsTransientTaskbar
+ ? taskbarActivityContext.getTranslationCallbacks()
+ : null;
}
@Override
@@ -76,28 +106,83 @@
final float y = ev.getRawY();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
+ mActivePointerId = ev.getPointerId(0);
+ mDownPos.set(ev.getX(), ev.getY());
+ mLastPos.set(mDownPos);
+
+ mHasPassedTaskbarThreshold = false;
+ mTaskbarActivityContext.setAutohideSuspendFlag(
+ FLAG_AUTOHIDE_SUSPEND_TOUCHING, true);
if (isInArea(x)) {
- mDownX = x;
- mDownY = y;
- mTaskbarActivityContext.startTaskbarUnstashHint(
- /* animateForward = */ true);
- mCanceledUnstashHint = false;
+ if (!mIsTransientTaskbar) {
+ mLongPressDownX = x;
+ mLongPressDownY = y;
+ mTaskbarActivityContext.startTaskbarUnstashHint(
+ /* animateForward = */ true);
+ mCanceledUnstashHint = false;
+ }
+ }
+ break;
+ case MotionEvent.ACTION_POINTER_UP:
+ int ptrIdx = ev.getActionIndex();
+ int ptrId = ev.getPointerId(ptrIdx);
+ if (ptrId == mActivePointerId) {
+ final int newPointerIdx = ptrIdx == 0 ? 1 : 0;
+ mDownPos.set(
+ ev.getX(newPointerIdx) - (mLastPos.x - mDownPos.x),
+ ev.getY(newPointerIdx) - (mLastPos.y - mDownPos.y));
+ mLastPos.set(ev.getX(newPointerIdx), ev.getY(newPointerIdx));
+ mActivePointerId = ev.getPointerId(newPointerIdx);
}
break;
case MotionEvent.ACTION_MOVE:
- if (!mCanceledUnstashHint
- && squaredHypot(mDownX - x, mDownY - y) > mSquaredTouchSlop) {
+ if (!mIsTransientTaskbar
+ && !mCanceledUnstashHint
+ && squaredHypot(mLongPressDownX - x, mLongPressDownY - y)
+ > mSquaredTouchSlop) {
mTaskbarActivityContext.startTaskbarUnstashHint(
/* animateForward = */ false);
mCanceledUnstashHint = true;
}
+
+ int pointerIndex = ev.findPointerIndex(mActivePointerId);
+ if (pointerIndex == INVALID_POINTER_ID) {
+ break;
+ }
+ mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
+
+ if (mIsTransientTaskbar) {
+ float dY = mLastPos.y - mDownPos.y;
+ boolean passedTaskbarThreshold = dY < 0
+ && mLastPos.y < mTaskbarThresholdY;
+
+ if (!mHasPassedTaskbarThreshold
+ && passedTaskbarThreshold) {
+ mHasPassedTaskbarThreshold = true;
+
+ mTaskbarActivityContext.onSwipeToUnstashTaskbar();
+ }
+
+ if (dY < 0) {
+ dY = -OverScroll.dampedScroll(-dY, mTaskbarThresholdY);
+ if (mTransitionCallback != null) {
+ mTransitionCallback.onActionMove(dY);
+ }
+ }
+ }
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
- if (!mCanceledUnstashHint) {
+ if (!mIsTransientTaskbar && !mCanceledUnstashHint) {
mTaskbarActivityContext.startTaskbarUnstashHint(
/* animateForward = */ false);
}
+ mTaskbarActivityContext.setAutohideSuspendFlag(
+ FLAG_AUTOHIDE_SUSPEND_TOUCHING, false);
+ if (mTransitionCallback != null) {
+ mTransitionCallback.onActionEnd();
+ }
+ mHasPassedTaskbarThreshold = false;
break;
}
}
@@ -111,7 +196,9 @@
}
private void onLongPressDetected(MotionEvent motionEvent) {
- if (mTaskbarActivityContext != null && isInArea(motionEvent.getRawX())) {
+ if (mTaskbarActivityContext != null
+ && isInArea(motionEvent.getRawX())
+ && !mIsTransientTaskbar) {
boolean taskBarPressed = mTaskbarActivityContext.onLongPressToUnstashTaskbar();
if (taskBarPressed) {
setActive(motionEvent);
diff --git a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
index 8ad17cb..897b559 100644
--- a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
+++ b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
@@ -53,6 +53,7 @@
import androidx.annotation.Nullable;
import androidx.core.graphics.ColorUtils;
+import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
@@ -120,10 +121,9 @@
mContentView = findViewById(R.id.content_view);
mSwipeUpShift = getResources().getDimension(R.dimen.allset_swipe_up_shift);
- boolean isTablet = InvariantDeviceProfile.INSTANCE.get(getApplicationContext())
- .getDeviceProfile(this).isTablet;
+ DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(this).getDeviceProfile(this);
TextView subtitle = findViewById(R.id.subtitle);
- subtitle.setText(isTablet
+ subtitle.setText(dp.isTablet
? R.string.allset_description_tablet : R.string.allset_description);
TextView tv = findViewById(R.id.navigation_settings);
@@ -135,14 +135,22 @@
} catch (URISyntaxException e) {
Log.e(LOG_TAG, "Failed to parse system nav settings intent", e);
}
- finish();
});
- findViewById(R.id.hint).setAccessibilityDelegate(new SkipButtonAccessibilityDelegate());
+ TextView hintTextView = findViewById(R.id.hint);
+ if (!dp.isGestureMode) {
+ hintTextView.setText(R.string.allset_button_hint);
+ }
+ hintTextView.setAccessibilityDelegate(new SkipButtonAccessibilityDelegate());
mTISBindHelper = new TISBindHelper(this, this::onTISConnected);
mVibrator = getSystemService(Vibrator.class);
mAnimatedBackground = findViewById(R.id.animated_background);
+ // There's a bug in the currently used external Lottie library (v5.2.0), and it doesn't load
+ // the correct animation from the raw resources when configuration changes, so we need to
+ // manually load the resource and pass it to Lottie.
+ mAnimatedBackground.setAnimation(getResources().openRawResource(R.raw.all_set_page_bg),
+ null);
startBackgroundAnimation();
}
diff --git a/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java b/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
index 4badf30..e7bf7e2 100644
--- a/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
+++ b/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
@@ -44,10 +44,10 @@
import androidx.annotation.Nullable;
import com.android.launcher3.R;
-import com.android.launcher3.testing.shared.ResourceUtils;
import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.testing.shared.ResourceUtils;
import com.android.launcher3.util.DisplayController;
-import com.android.launcher3.util.DisplayController.NavigationMode;
+import com.android.launcher3.util.NavigationMode;
import com.android.quickstep.util.MotionPauseDetector;
import com.android.quickstep.util.NavBarPosition;
import com.android.quickstep.util.TriggerSwipeUpTouchTracker;
diff --git a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
index fa7bc04..d7ff0be 100644
--- a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
@@ -33,7 +33,6 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Build;
-import android.view.SurfaceControl;
import android.view.View;
import android.view.ViewOutlineProvider;
@@ -53,9 +52,11 @@
import com.android.quickstep.RemoteTargetGluer;
import com.android.quickstep.SwipeUpAnimationLogic;
import com.android.quickstep.SwipeUpAnimationLogic.RunningWindowAnim;
+import com.android.quickstep.util.RecordingSurfaceTransaction;
import com.android.quickstep.util.RectFSpringAnim;
+import com.android.quickstep.util.SurfaceTransaction;
+import com.android.quickstep.util.SurfaceTransaction.MockProperties;
import com.android.quickstep.util.TransformParams;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
@TargetApi(Build.VERSION_CODES.R)
abstract class SwipeUpGestureTutorialController extends TutorialController {
@@ -415,21 +416,23 @@
private class FakeTransformParams extends TransformParams {
@Override
- public SurfaceParams[] createSurfaceParams(BuilderProxy proxy) {
- SurfaceParams.Builder builder = new SurfaceParams.Builder((SurfaceControl) null);
- proxy.onBuildTargetParams(builder, null, this);
- return new SurfaceParams[] {builder.build()};
+ public SurfaceTransaction createSurfaceParams(BuilderProxy proxy) {
+ RecordingSurfaceTransaction transaction = new RecordingSurfaceTransaction();
+ proxy.onBuildTargetParams(transaction.mockProperties, null, this);
+ return transaction;
}
@Override
- public void applySurfaceParams(SurfaceParams[] params) {
- SurfaceParams p = params[0];
- mFakeTaskView.setAnimationMatrix(p.matrix);
- mFakePreviousTaskView.setAnimationMatrix(p.matrix);
- mFakeTaskViewRect.set(p.windowCrop);
- mFakeTaskViewRadius = p.cornerRadius;
- mFakeTaskView.invalidateOutline();
- mFakePreviousTaskView.invalidateOutline();
+ public void applySurfaceParams(SurfaceTransaction params) {
+ if (params instanceof RecordingSurfaceTransaction) {
+ MockProperties p = ((RecordingSurfaceTransaction) params).mockProperties;
+ mFakeTaskView.setAnimationMatrix(p.matrix);
+ mFakePreviousTaskView.setAnimationMatrix(p.matrix);
+ mFakeTaskViewRect.set(p.windowCrop);
+ mFakeTaskViewRadius = p.cornerRadius;
+ mFakeTaskView.invalidateOutline();
+ mFakePreviousTaskView.invalidateOutline();
+ }
}
}
}
diff --git a/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java b/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
index bd0250d..2ccdfa3 100644
--- a/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
+++ b/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
@@ -40,15 +40,14 @@
import com.android.launcher3.AutoInstallsLayout;
import com.android.launcher3.R;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.logging.InstanceId;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.logging.StatsLogManager.StatsLogger;
import com.android.launcher3.model.DeviceGridState;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.DisplayController.Info;
-import com.android.launcher3.util.DisplayController.NavigationMode;
import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.launcher3.util.NavigationMode;
import com.android.launcher3.util.SettingsCache;
import org.xmlpull.v1.XmlPullParser;
@@ -179,11 +178,9 @@
logger::log);
SharedPreferences prefs = getPrefs(mContext);
- if (FeatureFlags.ENABLE_THEMED_ICONS.get()) {
- logger.log(prefs.getBoolean(KEY_THEMED_ICONS, false)
- ? LAUNCHER_THEMED_ICON_ENABLED
- : LAUNCHER_THEMED_ICON_DISABLED);
- }
+ logger.log(prefs.getBoolean(KEY_THEMED_ICONS, false)
+ ? LAUNCHER_THEMED_ICON_ENABLED
+ : LAUNCHER_THEMED_ICON_DISABLED);
mLoggablePrefs.forEach((key, lp) -> logger.log(() ->
prefs.getBoolean(key, lp.defaultValue) ? lp.eventIdOn : lp.eventIdOff));
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index 45c8036..0ef4597 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -62,11 +62,14 @@
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.util.Executors;
+import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.LogConfig;
import com.android.launcher3.views.ActivityContext;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import com.android.systemui.shared.system.SysUiStatsLog;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Optional;
import java.util.OptionalInt;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -85,6 +88,7 @@
private static final String TAG = "StatsLog";
private static final String LATENCY_TAG = "StatsLatencyLog";
+ private static final String IMPRESSION_TAG = "StatsImpressionLog";
private static final boolean IS_VERBOSE = Utilities.isPropertyEnabled(LogConfig.STATSLOG);
private static final InstanceId DEFAULT_INSTANCE_ID = InstanceId.fakeInstanceId(0);
// LauncherAtom.ItemInfo.getDefaultInstance() should be used but until launcher proto migrates
@@ -119,7 +123,12 @@
@Override
protected StatsLatencyLogger createLatencyLogger() {
- return new StatsCompatLatencyLogger(mContext, mActivityContext);
+ return new StatsCompatLatencyLogger();
+ }
+
+ @Override
+ protected StatsImpressionLogger createImpressionLogger() {
+ return new StatsCompatImpressionLogger();
}
/**
@@ -190,7 +199,8 @@
getCardinality(info), // cardinality = 16;
info.getWidget().getSpanX(), // span_x = 17 [default = 1];
info.getWidget().getSpanY(), // span_y = 18 [default = 1];
- getAttributes(info) /* attributes */
+ getAttributes(info) /* attributes = 19 [(log_mode) = MODE_BYTES] */,
+ info.getIsKidsMode() /* is_kids_mode = 20 */
);
}
@@ -216,6 +226,7 @@
private Optional<String> mEditText = Optional.empty();
private SliceItem mSliceItem;
private LauncherAtom.Slice mSlice;
+ private Optional<Integer> mCardinality = Optional.empty();
StatsCompatLogger(Context context, ActivityContext activityContext) {
mContext = context;
@@ -303,6 +314,12 @@
}
@Override
+ public StatsLogger withCardinality(int cardinality) {
+ this.mCardinality = Optional.of(cardinality);
+ return this;
+ }
+
+ @Override
public void log(EventEnum event) {
if (!Utilities.ATLEAST_R) {
return;
@@ -336,8 +353,9 @@
appState.getModel().enqueueModelUpdateTask(
new BaseModelUpdateTask() {
@Override
- public void execute(LauncherAppState app, BgDataModel dataModel,
- AllAppsList apps) {
+ public void execute(@NonNull final LauncherAppState app,
+ @NonNull final BgDataModel dataModel,
+ @NonNull final AllAppsList apps) {
FolderInfo folderInfo = dataModel.folders.get(mItemInfo.container);
write(event, applyOverwrites(mItemInfo.buildProto(folderInfo)));
}
@@ -419,6 +437,7 @@
if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
return;
}
+ int cardinality = mCardinality.orElseGet(() -> getCardinality(atomInfo));
SysUiStatsLog.write(
SysUiStatsLog.LAUNCHER_EVENT,
SysUiStatsLog.LAUNCHER_UICHANGED__ACTION__DEFAULT_ACTION /* deprecated */,
@@ -444,7 +463,7 @@
atomInfo.getFolderIcon().getFromLabelState().getNumber() /* fromState */,
atomInfo.getFolderIcon().getToLabelState().getNumber() /* toState */,
atomInfo.getFolderIcon().getLabelInfo() /* edittext */,
- getCardinality(atomInfo) /* cardinality */,
+ cardinality /* cardinality */,
getFeatures(atomInfo) /* features */,
getSearchAttributes(atomInfo) /* searchAttributes */,
getAttributes(atomInfo) /* attributes */
@@ -456,18 +475,12 @@
* Helps to construct and log statsd compatible latency events.
*/
private static class StatsCompatLatencyLogger implements StatsLatencyLogger {
- private final Context mContext;
- private final Optional<ActivityContext> mActivityContext;
private InstanceId mInstanceId = DEFAULT_INSTANCE_ID;
private LatencyType mType = LatencyType.UNKNOWN;
private int mPackageId = 0;
private long mLatencyInMillis;
private int mQueryLength = -1;
-
- StatsCompatLatencyLogger(Context context, ActivityContext activityContext) {
- mContext = context;
- mActivityContext = Optional.ofNullable(activityContext);
- }
+ private int mSubEventType = 0;
@Override
public StatsLatencyLogger withInstanceId(InstanceId instanceId) {
@@ -500,6 +513,12 @@
}
@Override
+ public StatsLatencyLogger withSubEventType(int type) {
+ this.mSubEventType = type;
+ return this;
+ }
+
+ @Override
public void log(EventEnum event) {
if (IS_VERBOSE) {
String name = (event instanceof Enum) ? ((Enum) event).name() :
@@ -516,7 +535,98 @@
mPackageId, // package_id
mLatencyInMillis, // latency_in_millis
mType.getId(), //type
- mQueryLength // query_length
+ mQueryLength, // query_length
+ mSubEventType // sub_event_type
+ );
+ }
+ }
+
+ /**
+ * Helps to construct and log statsd compatible impression events.
+ */
+ private static class StatsCompatImpressionLogger implements StatsImpressionLogger {
+ private final IntArray mResultTypeList = new IntArray();
+ private final IntArray mResultCountList = new IntArray();
+ private final List<Boolean> mAboveKeyboardList = new ArrayList<>();
+ private InstanceId mInstanceId = DEFAULT_INSTANCE_ID;
+ private State mLauncherState = State.UNKNOWN;
+ private int mQueryLength = -1;
+
+ @Override
+ public StatsImpressionLogger withInstanceId(InstanceId instanceId) {
+ this.mInstanceId = instanceId;
+ return this;
+ }
+
+ @Override
+ public StatsImpressionLogger withState(State state) {
+ this.mLauncherState = state;
+ return this;
+ }
+
+ @Override
+ public StatsImpressionLogger withQueryLength(int queryLength) {
+ this.mQueryLength = queryLength;
+ return this;
+ }
+
+ @Override
+ public StatsImpressionLogger withResultType(IntArray resultType) {
+ this.mResultTypeList.clear();
+ this.mResultTypeList.addAll(resultType);
+ return this;
+ }
+
+ @Override
+ public StatsImpressionLogger withResultCount(IntArray resultCount) {
+ this.mResultCountList.clear();
+ this.mResultCountList.addAll(resultCount);
+ return this;
+ }
+
+ @Override
+ public StatsImpressionLogger withAboveKeyboard(List<Boolean> aboveKeyboard) {
+ this.mAboveKeyboardList.clear();
+ this.mAboveKeyboardList.addAll(aboveKeyboard);
+ return this;
+ }
+
+ @Override
+ public void log(EventEnum event) {
+ boolean [] mAboveKeyboard = new boolean[mAboveKeyboardList.size()];
+ for (int i = 0; i < mAboveKeyboardList.size(); i++) {
+ mAboveKeyboard[i] = mAboveKeyboardList.get(i);
+ }
+ if (IS_VERBOSE) {
+ String name = (event instanceof Enum) ? ((Enum) event).name() :
+ event.getId() + "";
+ StringBuilder logStringBuilder = new StringBuilder("\n");
+ logStringBuilder.append(String.format("InstanceId:%s ", mInstanceId));
+ logStringBuilder.append(String.format("ImpressionEvent:%s ", name));
+ logStringBuilder.append(String.format("LauncherState = %s ", mLauncherState));
+ logStringBuilder.append(String.format("QueryLength = %s ", mQueryLength));
+ for (int i = 0; i < mResultTypeList.size(); i++) {
+ logStringBuilder.append(String.format(
+ "\n ResultType = %s with ResultCount = %s with is_above_keyboard = %s",
+ mResultTypeList.get(i), mResultCountList.get(i),
+ mAboveKeyboard[i]));
+ }
+ Log.d(IMPRESSION_TAG, logStringBuilder.toString());
+ }
+
+
+
+ SysUiStatsLog.write(SysUiStatsLog.LAUNCHER_IMPRESSION_EVENT,
+ event.getId(), // event_id
+ mInstanceId.getId(), // instance_id
+ mLauncherState.getLauncherState(), // state
+ mQueryLength, // query_length
+ //result type list
+ mResultTypeList.toArray(),
+ // result count list
+ mResultCountList.toArray(),
+ // above keyboard list
+ mAboveKeyboard
);
}
}
diff --git a/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java b/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java
new file mode 100644
index 0000000..60065fb
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java
@@ -0,0 +1,364 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.util;
+
+import android.util.ArraySet;
+
+import androidx.annotation.NonNull;
+
+import java.io.PrintWriter;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Utility class for tracking gesture navigation events as they happen, then detecting and reporting
+ * known issues at log dump time.
+ */
+public class ActiveGestureErrorDetector {
+
+ /**
+ * Enums associated to gesture navigation events.
+ */
+ public enum GestureEvent {
+ MOTION_DOWN, MOTION_UP, SET_END_TARGET, SET_END_TARGET_HOME, SET_END_TARGET_NEW_TASK,
+ ON_SETTLED_ON_END_TARGET, START_RECENTS_ANIMATION, FINISH_RECENTS_ANIMATION,
+ CANCEL_RECENTS_ANIMATION, SET_ON_PAGE_TRANSITION_END_CALLBACK, CANCEL_CURRENT_ANIMATION,
+ CLEANUP_SCREENSHOT, SCROLLER_ANIMATION_ABORTED, TASK_APPEARED, EXPECTING_TASK_APPEARED,
+ FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER,
+
+ /**
+ * These GestureEvents are specifically associated to state flags that get set in
+ * {@link com.android.quickstep.MultiStateCallback}. If a state flag needs to be tracked
+ * for error detection, an enum should be added here and that state flag-enum pair should
+ * be added to the state flag's container class' {@code getTrackedEventForState} method.
+ */
+ STATE_GESTURE_STARTED, STATE_GESTURE_COMPLETED, STATE_GESTURE_CANCELLED,
+ STATE_END_TARGET_ANIMATION_FINISHED, STATE_RECENTS_SCROLLING_FINISHED,
+ STATE_CAPTURE_SCREENSHOT, STATE_SCREENSHOT_CAPTURED, STATE_HANDLER_INVALIDATED,
+ STATE_RECENTS_ANIMATION_CANCELED, STATE_LAUNCHER_DRAWN(true, false);
+
+ public final boolean mLogEvent;
+ public final boolean mTrackEvent;
+
+ GestureEvent() {
+ this(false, true);
+ }
+
+ GestureEvent(boolean logEvent, boolean trackEvent) {
+ mLogEvent = logEvent;
+ mTrackEvent = trackEvent;
+ }
+ }
+
+ private ActiveGestureErrorDetector() {}
+
+ protected static void analyseAndDump(
+ @NonNull String prefix,
+ @NonNull PrintWriter writer,
+ List<ActiveGestureLog.EventLog> eventLogs) {
+ writer.println(prefix + "ActiveGestureErrorDetector:");
+ for (int i = 0; i < eventLogs.size(); i++) {
+ ActiveGestureLog.EventLog eventLog = eventLogs.get(i);
+ if (eventLog == null) {
+ continue;
+ }
+ int gestureId = eventLog.logId;
+ writer.println(prefix + "\tError messages for gesture ID: " + gestureId);
+
+ boolean errorDetected = false;
+ // Use a Set since the order is inherently checked in the loop.
+ final Set<GestureEvent> encounteredEvents = new ArraySet<>();
+ // Set flags and check order of operations.
+ for (ActiveGestureLog.EventEntry eventEntry : eventLog.eventEntries) {
+ GestureEvent gestureEvent = eventEntry.getGestureEvent();
+ if (gestureEvent == null) {
+ continue;
+ }
+ encounteredEvents.add(gestureEvent);
+ switch (gestureEvent) {
+ case MOTION_UP:
+ errorDetected |= printErrorIfTrue(
+ !encounteredEvents.contains(GestureEvent.MOTION_DOWN),
+ prefix,
+ /* errorMessage= */ "Motion up detected before/without"
+ + " motion down.",
+ writer);
+ break;
+ case ON_SETTLED_ON_END_TARGET:
+ errorDetected |= printErrorIfTrue(
+ !encounteredEvents.contains(GestureEvent.SET_END_TARGET),
+ prefix,
+ /* errorMessage= */ "onSettledOnEndTarget called "
+ + "before/without setEndTarget.",
+ writer);
+ break;
+ case FINISH_RECENTS_ANIMATION:
+ errorDetected |= printErrorIfTrue(
+ !encounteredEvents.contains(GestureEvent.START_RECENTS_ANIMATION),
+ prefix,
+ /* errorMessage= */ "finishRecentsAnimation called "
+ + "before/without startRecentsAnimation.",
+ writer);
+ break;
+ case CANCEL_RECENTS_ANIMATION:
+ errorDetected |= printErrorIfTrue(
+ !encounteredEvents.contains(GestureEvent.START_RECENTS_ANIMATION),
+ prefix,
+ /* errorMessage= */ "cancelRecentsAnimation called "
+ + "before/without startRecentsAnimation.",
+ writer);
+ break;
+ case CLEANUP_SCREENSHOT:
+ errorDetected |= printErrorIfTrue(
+ !encounteredEvents.contains(GestureEvent.STATE_SCREENSHOT_CAPTURED),
+ prefix,
+ /* errorMessage= */ "recents activity screenshot was "
+ + "cleaned up before/without STATE_SCREENSHOT_CAPTURED "
+ + "being set.",
+ writer);
+ break;
+ case SCROLLER_ANIMATION_ABORTED:
+ errorDetected |= printErrorIfTrue(
+ encounteredEvents.contains(GestureEvent.SET_END_TARGET_HOME)
+ && !encounteredEvents.contains(
+ GestureEvent.ON_SETTLED_ON_END_TARGET),
+ prefix,
+ /* errorMessage= */ "recents view scroller animation "
+ + "aborted after setting end target HOME, but before"
+ + " settling on end target.",
+ writer);
+ break;
+ case TASK_APPEARED:
+ errorDetected |= printErrorIfTrue(
+ !encounteredEvents.contains(GestureEvent.SET_END_TARGET_NEW_TASK),
+ prefix,
+ /* errorMessage= */ "onTasksAppeared called "
+ + "before/without setting end target to new task",
+ writer);
+ errorDetected |= printErrorIfTrue(
+ !encounteredEvents.contains(GestureEvent.EXPECTING_TASK_APPEARED),
+ prefix,
+ /* errorMessage= */ "onTasksAppeared was not expected to be called",
+ writer);
+ break;
+ case EXPECTING_TASK_APPEARED:
+ errorDetected |= printErrorIfTrue(
+ !encounteredEvents.contains(GestureEvent.SET_END_TARGET_NEW_TASK),
+ prefix,
+ /* errorMessage= */ "expecting onTasksAppeared to be called "
+ + "before/without setting end target to new task",
+ writer);
+ break;
+ case STATE_GESTURE_COMPLETED:
+ errorDetected |= printErrorIfTrue(
+ !encounteredEvents.contains(GestureEvent.MOTION_UP),
+ prefix,
+ /* errorMessage= */ "STATE_GESTURE_COMPLETED set "
+ + "before/without motion up.",
+ writer);
+ errorDetected |= printErrorIfTrue(
+ !encounteredEvents.contains(GestureEvent.STATE_GESTURE_STARTED),
+ prefix,
+ /* errorMessage= */ "STATE_GESTURE_COMPLETED set "
+ + "before/without STATE_GESTURE_STARTED.",
+ writer);
+ break;
+ case STATE_GESTURE_CANCELLED:
+ errorDetected |= printErrorIfTrue(
+ !encounteredEvents.contains(GestureEvent.MOTION_UP),
+ prefix,
+ /* errorMessage= */ "STATE_GESTURE_CANCELLED set "
+ + "before/without motion up.",
+ writer);
+ errorDetected |= printErrorIfTrue(
+ !encounteredEvents.contains(GestureEvent.STATE_GESTURE_STARTED),
+ prefix,
+ /* errorMessage= */ "STATE_GESTURE_CANCELLED set "
+ + "before/without STATE_GESTURE_STARTED.",
+ writer);
+ break;
+ case STATE_SCREENSHOT_CAPTURED:
+ errorDetected |= printErrorIfTrue(
+ !encounteredEvents.contains(GestureEvent.STATE_CAPTURE_SCREENSHOT),
+ prefix,
+ /* errorMessage= */ "STATE_SCREENSHOT_CAPTURED set "
+ + "before/without STATE_CAPTURE_SCREENSHOT.",
+ writer);
+ break;
+ case STATE_RECENTS_SCROLLING_FINISHED:
+ errorDetected |= printErrorIfTrue(
+ !encounteredEvents.contains(
+ GestureEvent.SET_ON_PAGE_TRANSITION_END_CALLBACK),
+ prefix,
+ /* errorMessage= */ "STATE_RECENTS_SCROLLING_FINISHED "
+ + "set before/without calling "
+ + "setOnPageTransitionEndCallback.",
+ writer);
+ break;
+ case STATE_RECENTS_ANIMATION_CANCELED:
+ errorDetected |= printErrorIfTrue(
+ !encounteredEvents.contains(
+ GestureEvent.START_RECENTS_ANIMATION),
+ prefix,
+ /* errorMessage= */ "STATE_RECENTS_ANIMATION_CANCELED "
+ + "set before/without startRecentsAnimation.",
+ writer);
+ break;
+ case MOTION_DOWN:
+ case SET_END_TARGET:
+ case SET_END_TARGET_HOME:
+ case SET_END_TARGET_NEW_TASK:
+ case START_RECENTS_ANIMATION:
+ case SET_ON_PAGE_TRANSITION_END_CALLBACK:
+ case CANCEL_CURRENT_ANIMATION:
+ case FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER:
+ case STATE_GESTURE_STARTED:
+ case STATE_END_TARGET_ANIMATION_FINISHED:
+ case STATE_CAPTURE_SCREENSHOT:
+ case STATE_HANDLER_INVALIDATED:
+ case STATE_LAUNCHER_DRAWN:
+ default:
+ // No-Op
+ }
+ }
+
+ // Check that all required events were found.
+ errorDetected |= printErrorIfTrue(
+ !encounteredEvents.contains(GestureEvent.MOTION_DOWN),
+ prefix,
+ /* errorMessage= */ "Motion down never detected.",
+ writer);
+ errorDetected |= printErrorIfTrue(
+ !encounteredEvents.contains(GestureEvent.MOTION_UP),
+ prefix,
+ /* errorMessage= */ "Motion up never detected.",
+ writer);
+
+ errorDetected |= printErrorIfTrue(
+ /* condition= */ encounteredEvents.contains(GestureEvent.SET_END_TARGET)
+ && !encounteredEvents.contains(GestureEvent.ON_SETTLED_ON_END_TARGET),
+ prefix,
+ /* errorMessage= */ "setEndTarget was called, but "
+ + "onSettledOnEndTarget wasn't.",
+ writer);
+ errorDetected |= printErrorIfTrue(
+ /* condition= */ encounteredEvents.contains(GestureEvent.SET_END_TARGET)
+ && !encounteredEvents.contains(
+ GestureEvent.STATE_END_TARGET_ANIMATION_FINISHED),
+ prefix,
+ /* errorMessage= */ "setEndTarget was called, but "
+ + "STATE_END_TARGET_ANIMATION_FINISHED was never set.",
+ writer);
+ errorDetected |= printErrorIfTrue(
+ /* condition= */ encounteredEvents.contains(GestureEvent.SET_END_TARGET)
+ && !encounteredEvents.contains(
+ GestureEvent.STATE_RECENTS_SCROLLING_FINISHED),
+ prefix,
+ /* errorMessage= */ "setEndTarget was called, but "
+ + "STATE_RECENTS_SCROLLING_FINISHED was never set.",
+ writer);
+ errorDetected |= printErrorIfTrue(
+ /* condition= */ encounteredEvents.contains(
+ GestureEvent.STATE_END_TARGET_ANIMATION_FINISHED)
+ && encounteredEvents.contains(
+ GestureEvent.STATE_RECENTS_SCROLLING_FINISHED)
+ && !encounteredEvents.contains(GestureEvent.ON_SETTLED_ON_END_TARGET),
+ prefix,
+ /* errorMessage= */ "STATE_END_TARGET_ANIMATION_FINISHED and "
+ + "STATE_RECENTS_SCROLLING_FINISHED were set, but onSettledOnEndTarget "
+ + "wasn't called.",
+ writer);
+
+ errorDetected |= printErrorIfTrue(
+ /* condition= */ encounteredEvents.contains(
+ GestureEvent.START_RECENTS_ANIMATION)
+ && !encounteredEvents.contains(GestureEvent.FINISH_RECENTS_ANIMATION)
+ && !encounteredEvents.contains(GestureEvent.CANCEL_RECENTS_ANIMATION),
+ prefix,
+ /* errorMessage= */ "startRecentsAnimation was called, but "
+ + "finishRecentsAnimation and cancelRecentsAnimation weren't.",
+ writer);
+
+ errorDetected |= printErrorIfTrue(
+ /* condition= */ encounteredEvents.contains(GestureEvent.STATE_GESTURE_STARTED)
+ && !encounteredEvents.contains(GestureEvent.STATE_GESTURE_COMPLETED)
+ && !encounteredEvents.contains(GestureEvent.STATE_GESTURE_CANCELLED),
+ prefix,
+ /* errorMessage= */ "STATE_GESTURE_STARTED was set, but "
+ + "STATE_GESTURE_COMPLETED and STATE_GESTURE_CANCELLED weren't.",
+ writer);
+
+ errorDetected |= printErrorIfTrue(
+ /* condition= */ encounteredEvents.contains(
+ GestureEvent.STATE_CAPTURE_SCREENSHOT)
+ && !encounteredEvents.contains(GestureEvent.STATE_SCREENSHOT_CAPTURED),
+ prefix,
+ /* errorMessage= */ "STATE_CAPTURE_SCREENSHOT was set, but "
+ + "STATE_SCREENSHOT_CAPTURED wasn't.",
+ writer);
+
+ errorDetected |= printErrorIfTrue(
+ /* condition= */ encounteredEvents.contains(
+ GestureEvent.SET_ON_PAGE_TRANSITION_END_CALLBACK)
+ && !encounteredEvents.contains(
+ GestureEvent.STATE_RECENTS_SCROLLING_FINISHED),
+ prefix,
+ /* errorMessage= */ "setOnPageTransitionEndCallback called, but "
+ + "STATE_RECENTS_SCROLLING_FINISHED wasn't set.",
+ writer);
+
+ errorDetected |= printErrorIfTrue(
+ /* condition= */ encounteredEvents.contains(
+ GestureEvent.FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER)
+ && !encounteredEvents.contains(GestureEvent.CANCEL_CURRENT_ANIMATION)
+ && !encounteredEvents.contains(GestureEvent.STATE_HANDLER_INVALIDATED),
+ prefix,
+ /* errorMessage= */ "AbsSwipeUpHandler.cancelCurrentAnimation "
+ + "wasn't called and STATE_HANDLER_INVALIDATED wasn't set.",
+ writer);
+
+ errorDetected |= printErrorIfTrue(
+ /* condition= */ encounteredEvents.contains(
+ GestureEvent.STATE_RECENTS_ANIMATION_CANCELED)
+ && !encounteredEvents.contains(GestureEvent.CLEANUP_SCREENSHOT),
+ prefix,
+ /* errorMessage= */ "STATE_RECENTS_ANIMATION_CANCELED was set but "
+ + "the task screenshot wasn't cleaned up.",
+ writer);
+
+ errorDetected |= printErrorIfTrue(
+ /* condition= */ encounteredEvents.contains(
+ GestureEvent.EXPECTING_TASK_APPEARED)
+ && !encounteredEvents.contains(GestureEvent.TASK_APPEARED),
+ prefix,
+ /* errorMessage= */ "onTaskAppeared was expected to be called but wasn't.",
+ writer);
+
+ if (!errorDetected) {
+ writer.println(prefix + "\t\tNo errors detected.");
+ }
+ }
+ }
+
+ private static boolean printErrorIfTrue(
+ boolean condition, String prefix, String errorMessage, PrintWriter writer) {
+ if (!condition) {
+ return false;
+ }
+ writer.println(prefix + "\t\t- " + errorMessage);
+ return true;
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java b/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java
index fabfc4b..23fdd58 100644
--- a/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java
+++ b/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java
@@ -15,15 +15,26 @@
*/
package com.android.quickstep.util;
-import android.content.Context;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
-import com.android.launcher3.logging.EventLogArray;
-import com.android.launcher3.util.MainThreadInitializedObject;
+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;
+import java.util.Objects;
/**
* A log to keep track of the active gesture.
*/
-public class ActiveGestureLog extends EventLogArray {
+public class ActiveGestureLog {
+
+ private static final int MAX_GESTURES_TRACKED = 10;
public static final ActiveGestureLog INSTANCE = new ActiveGestureLog();
@@ -33,7 +44,306 @@
*/
public static final String INTENT_EXTRA_LOG_TRACE_ID = "INTENT_EXTRA_LOG_TRACE_ID";
+ private static final int TYPE_ONE_OFF = 0;
+ private static final int TYPE_FLOAT = 1;
+ private static final int TYPE_INTEGER = 2;
+ private static final int TYPE_BOOL_TRUE = 3;
+ private static final int TYPE_BOOL_FALSE = 4;
+ private static final int TYPE_INPUT_CONSUMER = 5;
+ private static final int TYPE_GESTURE_EVENT = 6;
+
+ private final EventLog[] logs;
+ private int nextIndex;
+ private int mCurrentLogId = 100;
+
private ActiveGestureLog() {
- super("touch_interaction_log", 40);
+ this.logs = new EventLog[MAX_GESTURES_TRACKED];
+ this.nextIndex = 0;
+ }
+
+ /**
+ * Track the given event for error detection.
+ *
+ * @param gestureEvent GestureEvent representing an event during the current gesture's
+ * execution.
+ */
+ public void trackEvent(@Nullable ActiveGestureErrorDetector.GestureEvent gestureEvent) {
+ addLog(TYPE_GESTURE_EVENT, "", 0, CompoundString.NO_OP, gestureEvent);
+ }
+
+ public void addLog(String event) {
+ addLog(event, null);
+ }
+
+ public void addLog(String event, int extras) {
+ addLog(event, extras, null);
+ }
+
+ public void addLog(String event, boolean extras) {
+ addLog(event, extras, null);
+ }
+
+ public void addLog(CompoundString compoundString) {
+ addLog(TYPE_INPUT_CONSUMER, "", 0, compoundString, null);
+ }
+
+ /**
+ * Adds a log and track the associated event for error detection.
+ *
+ * @param gestureEvent GestureEvent representing the event being logged.
+ */
+ public void addLog(
+ String event, @Nullable ActiveGestureErrorDetector.GestureEvent gestureEvent) {
+ addLog(TYPE_ONE_OFF, event, 0, CompoundString.NO_OP, gestureEvent);
+ }
+
+ public void addLog(
+ String event,
+ int extras,
+ @Nullable ActiveGestureErrorDetector.GestureEvent gestureEvent) {
+ addLog(TYPE_INTEGER, event, extras, CompoundString.NO_OP, gestureEvent);
+ }
+
+ public void addLog(
+ String event,
+ boolean extras,
+ @Nullable ActiveGestureErrorDetector.GestureEvent gestureEvent) {
+ addLog(
+ extras ? TYPE_BOOL_TRUE : TYPE_BOOL_FALSE,
+ event,
+ 0,
+ CompoundString.NO_OP,
+ gestureEvent);
+ }
+
+ private void addLog(
+ int type,
+ String event,
+ float extras,
+ CompoundString compoundString,
+ @Nullable ActiveGestureErrorDetector.GestureEvent gestureEvent) {
+ EventLog lastEventLog = logs[(nextIndex + logs.length - 1) % logs.length];
+ if (lastEventLog == null || mCurrentLogId != lastEventLog.logId) {
+ EventLog eventLog = new EventLog(mCurrentLogId);
+ EventEntry eventEntry = new EventEntry();
+
+ eventEntry.update(type, event, extras, compoundString, gestureEvent);
+ eventLog.eventEntries.add(eventEntry);
+ logs[nextIndex] = eventLog;
+ nextIndex = (nextIndex + 1) % logs.length;
+ return;
+ }
+
+ // Update the last EventLog
+ List<EventEntry> lastEventEntries = lastEventLog.eventEntries;
+ EventEntry lastEntry = lastEventEntries.size() > 0
+ ? lastEventEntries.get(lastEventEntries.size() - 1) : null;
+
+ // Update the last EventEntry if it's a duplicate
+ if (isEntrySame(lastEntry, type, event, extras, compoundString, gestureEvent)) {
+ lastEntry.duplicateCount++;
+ return;
+ }
+ EventEntry eventEntry = new EventEntry();
+
+ eventEntry.update(type, event, extras, compoundString, gestureEvent);
+ lastEventEntries.add(eventEntry);
+ }
+
+ public void clear() {
+ Arrays.fill(logs, null);
+ }
+
+ public void dump(String prefix, PrintWriter writer) {
+ writer.println(prefix + "ActiveGestureLog history:");
+ SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSSZ ", Locale.US);
+ Date date = new Date();
+ ArrayList<EventLog> eventLogs = new ArrayList<>();
+
+ for (int i = 0; i < logs.length; i++) {
+ EventLog eventLog = logs[(nextIndex + i) % logs.length];
+ if (eventLog == null) {
+ continue;
+ }
+ eventLogs.add(eventLog);
+ writer.println(prefix + "\tLogs for logId: " + eventLog.logId);
+
+ for (EventEntry eventEntry : eventLog.eventEntries) {
+ date.setTime(eventEntry.time);
+
+ StringBuilder msg = new StringBuilder(prefix + "\t\t").append(sdf.format(date))
+ .append(eventEntry.event);
+ switch (eventEntry.type) {
+ case TYPE_BOOL_FALSE:
+ msg.append(": false");
+ break;
+ case TYPE_BOOL_TRUE:
+ msg.append(": true");
+ break;
+ case TYPE_FLOAT:
+ msg.append(": ").append(eventEntry.extras);
+ break;
+ case TYPE_INTEGER:
+ msg.append(": ").append((int) eventEntry.extras);
+ break;
+ case TYPE_INPUT_CONSUMER:
+ msg.append(eventEntry.mCompoundString);
+ break;
+ case TYPE_GESTURE_EVENT:
+ continue;
+ default: // fall out
+ }
+ if (eventEntry.duplicateCount > 0) {
+ msg.append(" & ").append(eventEntry.duplicateCount).append(" similar events");
+ }
+ writer.println(msg);
+ }
+ }
+
+ if (FeatureFlags.ENABLE_GESTURE_ERROR_DETECTION.get()) {
+ ActiveGestureErrorDetector.analyseAndDump(prefix + '\t', writer, eventLogs);
+ }
+ }
+
+ /**
+ * Increments and returns the current log ID. This should be used every time a new log trace
+ * is started.
+ */
+ public int incrementLogId() {
+ return mCurrentLogId++;
+ }
+
+ /** Returns the current log ID. This should be used when a log trace is being reused. */
+ public int getLogId() {
+ return mCurrentLogId;
+ }
+
+ private boolean isEntrySame(
+ EventEntry entry,
+ int type,
+ String event,
+ float extras,
+ CompoundString compoundString,
+ ActiveGestureErrorDetector.GestureEvent gestureEvent) {
+ return entry != null
+ && entry.type == type
+ && entry.event.equals(event)
+ && Float.compare(entry.extras, extras) == 0
+ && entry.mCompoundString.equals(compoundString)
+ && entry.gestureEvent == gestureEvent;
+ }
+
+ /** A single event entry. */
+ protected static class EventEntry {
+
+ private int type;
+ private String event;
+ private float extras;
+ @NonNull private CompoundString mCompoundString;
+ private ActiveGestureErrorDetector.GestureEvent gestureEvent;
+ private long time;
+ private int duplicateCount;
+
+ private EventEntry() {}
+
+ @Nullable
+ protected ActiveGestureErrorDetector.GestureEvent getGestureEvent() {
+ return gestureEvent;
+ }
+
+ private void update(
+ int type,
+ String event,
+ float extras,
+ @NonNull CompoundString compoundString,
+ ActiveGestureErrorDetector.GestureEvent gestureEvent) {
+ this.type = type;
+ this.event = event;
+ this.extras = extras;
+ this.mCompoundString = compoundString;
+ this.gestureEvent = gestureEvent;
+ time = System.currentTimeMillis();
+ duplicateCount = 0;
+ }
+ }
+
+ /** An entire log of entries associated with a single log ID */
+ protected static class EventLog {
+
+ protected final List<EventEntry> eventEntries = new ArrayList<>();
+ protected final int logId;
+
+ private EventLog(int logId) {
+ this.logId = logId;
+ }
+ }
+
+ /** A buildable string stored as an array for memory efficiency. */
+ public static class CompoundString {
+
+ public static final CompoundString NO_OP = new CompoundString();
+
+ private final List<String> mSubstrings;
+
+ private final boolean mIsNoOp;
+
+ private CompoundString() {
+ this(null);
+ }
+
+ public CompoundString(String substring) {
+ mIsNoOp = substring == null;
+ if (mIsNoOp) {
+ mSubstrings = null;
+ return;
+ }
+ mSubstrings = new ArrayList<>();
+ mSubstrings.add(substring);
+ }
+
+ public CompoundString append(CompoundString substring) {
+ if (mIsNoOp) {
+ return this;
+ }
+ mSubstrings.addAll(substring.mSubstrings);
+
+ return this;
+ }
+
+ public CompoundString append(String substring) {
+ if (mIsNoOp) {
+ return this;
+ }
+ mSubstrings.add(substring);
+
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ if (mIsNoOp) {
+ return "ERROR: cannot use No-Op compound string";
+ }
+ StringBuilder sb = new StringBuilder();
+ for (String substring : mSubstrings) {
+ sb.append(substring);
+ }
+
+ return sb.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mIsNoOp, mSubstrings);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof CompoundString)) {
+ return false;
+ }
+ CompoundString other = (CompoundString) obj;
+ return (mIsNoOp == other.mIsNoOp) && Objects.equals(mSubstrings, other.mSubstrings);
+ }
}
}
diff --git a/quickstep/src/com/android/quickstep/util/AnimUtils.java b/quickstep/src/com/android/quickstep/util/AnimUtils.java
new file mode 100644
index 0000000..b7b7825
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/AnimUtils.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.util;
+
+/**
+ * Utility class containing methods to help manage animations, interpolators, and timings.
+ */
+public class AnimUtils {
+ /**
+ * Fetches device-specific timings for the Overview > Split animation
+ * (splitscreen initiated from Overview).
+ */
+ public static SplitAnimationTimings getDeviceOverviewToSplitTimings(boolean isTablet) {
+ return isTablet
+ ? SplitAnimationTimings.TABLET_OVERVIEW_TO_SPLIT
+ : SplitAnimationTimings.PHONE_OVERVIEW_TO_SPLIT;
+ }
+
+ /**
+ * Fetches device-specific timings for the Split > Confirm animation
+ * (splitscreen confirmed by selecting a second app).
+ */
+ public static SplitAnimationTimings getDeviceSplitToConfirmTimings(boolean isTablet) {
+ return isTablet
+ ? SplitAnimationTimings.TABLET_SPLIT_TO_CONFIRM
+ : SplitAnimationTimings.PHONE_SPLIT_TO_CONFIRM;
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/util/BaseDepthController.java b/quickstep/src/com/android/quickstep/util/BaseDepthController.java
index 4030630..877e28a 100644
--- a/quickstep/src/com/android/quickstep/util/BaseDepthController.java
+++ b/quickstep/src/com/android/quickstep/util/BaseDepthController.java
@@ -24,6 +24,8 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.util.MultiPropertyFactory;
+import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
import com.android.systemui.shared.system.BlurUtils;
/**
@@ -31,7 +33,7 @@
*/
public class BaseDepthController {
- public static final FloatProperty<BaseDepthController> DEPTH =
+ private static final FloatProperty<BaseDepthController> DEPTH =
new FloatProperty<BaseDepthController>("depth") {
@Override
public void setValue(BaseDepthController depthController, float depth) {
@@ -44,7 +46,15 @@
}
};
+ private static final int DEPTH_INDEX_STATE_TRANSITION = 0;
+ private static final int DEPTH_INDEX_WIDGET = 1;
+ private static final int DEPTH_INDEX_COUNT = 2;
+
protected final Launcher mLauncher;
+ /** Property to set the depth for state transition. */
+ public final MultiProperty stateDepth;
+ /** Property to set the depth for widget picker. */
+ public final MultiProperty widgetDepth;
/**
* Blur radius when completely zoomed out, in pixels.
@@ -57,7 +67,7 @@
* Ratio from 0 to 1, where 0 is fully zoomed out, and 1 is zoomed in.
* @see android.service.wallpaper.WallpaperService.Engine#onZoomChanged(float)
*/
- protected float mDepth;
+ private float mDepth;
protected SurfaceControl mSurface;
@@ -78,6 +88,11 @@
mLauncher = activity;
mMaxBlurRadius = activity.getResources().getInteger(R.integer.max_depth_blur_radius);
mWallpaperManager = activity.getSystemService(WallpaperManager.class);
+
+ MultiPropertyFactory<BaseDepthController> depthProperty =
+ new MultiPropertyFactory<>(this, DEPTH, DEPTH_INDEX_COUNT, Float::max);
+ stateDepth = depthProperty.get(DEPTH_INDEX_STATE_TRANSITION);
+ widgetDepth = depthProperty.get(DEPTH_INDEX_WIDGET);
}
protected void setCrossWindowBlursEnabled(boolean isEnabled) {
@@ -129,7 +144,7 @@
}
}
- protected void setDepth(float depth) {
+ private void setDepth(float depth) {
depth = Utilities.boundToRange(depth, 0, 1);
// Round out the depth to dedupe frequent, non-perceptable updates
int depthI = (int) (depth * 256);
diff --git a/quickstep/src/com/android/quickstep/util/BaseUnfoldMoveFromCenterAnimator.java b/quickstep/src/com/android/quickstep/util/BaseUnfoldMoveFromCenterAnimator.java
index 143042f..2a513ee 100644
--- a/quickstep/src/com/android/quickstep/util/BaseUnfoldMoveFromCenterAnimator.java
+++ b/quickstep/src/com/android/quickstep/util/BaseUnfoldMoveFromCenterAnimator.java
@@ -16,12 +16,14 @@
package com.android.quickstep.util;
import android.annotation.CallSuper;
+import android.view.Surface.Rotation;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator;
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener;
+import com.android.systemui.unfold.updates.RotationChangeProvider;
import java.util.HashMap;
import java.util.Map;
@@ -32,15 +34,20 @@
public abstract class BaseUnfoldMoveFromCenterAnimator implements TransitionProgressListener {
private final UnfoldMoveFromCenterAnimator mMoveFromCenterAnimation;
+ private final RotationChangeProvider mRotationChangeProvider;
private final Map<ViewGroup, Boolean> mOriginalClipToPadding = new HashMap<>();
private final Map<ViewGroup, Boolean> mOriginalClipChildren = new HashMap<>();
+ private final UnfoldMoveFromCenterRotationListener mRotationListener =
+ new UnfoldMoveFromCenterRotationListener();
private boolean mAnimationInProgress = false;
- public BaseUnfoldMoveFromCenterAnimator(WindowManager windowManager) {
+ public BaseUnfoldMoveFromCenterAnimator(WindowManager windowManager,
+ RotationChangeProvider rotationChangeProvider) {
mMoveFromCenterAnimation = new UnfoldMoveFromCenterAnimator(windowManager,
new LauncherViewsMoveFromCenterTranslationApplier());
+ mRotationChangeProvider = rotationChangeProvider;
}
@CallSuper
@@ -50,6 +57,7 @@
mMoveFromCenterAnimation.updateDisplayProperties();
onPrepareViewsForAnimation();
onTransitionProgress(0f);
+ mRotationChangeProvider.addCallback(mRotationListener);
}
@CallSuper
@@ -62,6 +70,7 @@
@Override
public void onTransitionFinished() {
mAnimationInProgress = false;
+ mRotationChangeProvider.removeCallback(mRotationListener);
mMoveFromCenterAnimation.onTransitionFinished();
clearRegisteredViews();
}
@@ -109,4 +118,14 @@
view.setClipChildren(originalClipChildren);
}
}
+
+ private class UnfoldMoveFromCenterRotationListener implements
+ RotationChangeProvider.RotationListener {
+
+ @Override
+ public void onRotationChanged(@Rotation int newRotation) {
+ mMoveFromCenterAnimation.updateDisplayProperties(newRotation);
+ updateRegisteredViewsIfNeeded();
+ }
+ }
}
diff --git a/quickstep/src/com/android/quickstep/util/DesktopTask.java b/quickstep/src/com/android/quickstep/util/DesktopTask.java
new file mode 100644
index 0000000..433d23f
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/DesktopTask.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.util;
+
+import com.android.quickstep.views.TaskView;
+import com.android.systemui.shared.recents.model.Task;
+
+import java.util.ArrayList;
+
+/**
+ * A {@link Task} container that can contain N number of tasks that are part of the desktop in
+ * recent tasks list.
+ */
+public class DesktopTask extends GroupTask {
+
+ public ArrayList<Task> tasks;
+
+ public DesktopTask(ArrayList<Task> tasks) {
+ super(tasks.get(0), null, null, TaskView.Type.DESKTOP);
+ this.tasks = tasks;
+ }
+
+ @Override
+ public boolean containsTask(int taskId) {
+ for (Task task : tasks) {
+ if (task.key.id == taskId) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean hasMultipleTasks() {
+ return true;
+ }
+
+ @Override
+ public DesktopTask copy() {
+ return new DesktopTask(tasks);
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/util/GroupTask.java b/quickstep/src/com/android/quickstep/util/GroupTask.java
index f30d00c..2be4f0a 100644
--- a/quickstep/src/com/android/quickstep/util/GroupTask.java
+++ b/quickstep/src/com/android/quickstep/util/GroupTask.java
@@ -20,6 +20,7 @@
import androidx.annotation.Nullable;
import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds;
+import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.Task;
/**
@@ -27,24 +28,25 @@
* are represented as an app-pair in the recents task list.
*/
public class GroupTask {
- public @NonNull Task task1;
- public @Nullable Task task2;
- public @Nullable
- SplitBounds mSplitBounds;
+ @NonNull
+ public final Task task1;
+ @Nullable
+ public final Task task2;
+ @Nullable
+ public final SplitBounds mSplitBounds;
+ @TaskView.Type
+ public final int taskViewType;
- public GroupTask(@NonNull Task t1, @Nullable Task t2,
- @Nullable SplitBounds splitBounds) {
+ public GroupTask(@NonNull Task t1, @Nullable Task t2, @Nullable SplitBounds splitBounds) {
+ this(t1, t2, splitBounds, t2 != null ? TaskView.Type.GROUPED : TaskView.Type.SINGLE);
+ }
+
+ protected GroupTask(@NonNull Task t1, @Nullable Task t2, @Nullable SplitBounds splitBounds,
+ @TaskView.Type int taskViewType) {
task1 = t1;
task2 = t2;
mSplitBounds = splitBounds;
- }
-
- public GroupTask(@NonNull GroupTask group) {
- task1 = new Task(group.task1);
- task2 = group.task2 != null
- ? new Task(group.task2)
- : null;
- mSplitBounds = group.mSplitBounds;
+ this.taskViewType = taskViewType;
}
public boolean containsTask(int taskId) {
@@ -54,4 +56,14 @@
public boolean hasMultipleTasks() {
return task2 != null;
}
+
+ /**
+ * Create a copy of this instance
+ */
+ public GroupTask copy() {
+ return new GroupTask(
+ new Task(task1),
+ task2 != null ? new Task(task2) : null,
+ mSplitBounds);
+ }
}
diff --git a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java
index 97be437..170c622 100644
--- a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java
+++ b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java
@@ -18,13 +18,11 @@
import static com.android.launcher3.LauncherAnimUtils.HOTSEAT_SCALE_PROPERTY_FACTORY;
import static com.android.launcher3.LauncherAnimUtils.SCALE_INDEX_UNFOLD_ANIMATION;
import static com.android.launcher3.LauncherAnimUtils.WORKSPACE_SCALE_PROPERTY_FACTORY;
-import static com.android.launcher3.Utilities.comp;
import android.annotation.Nullable;
import android.util.FloatProperty;
import android.util.MathUtils;
import android.view.WindowManager;
-import android.view.WindowManagerGlobal;
import androidx.core.view.OneShotPreDrawListener;
@@ -34,6 +32,7 @@
import com.android.launcher3.util.HorizontalInsettableView;
import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener;
+import com.android.systemui.unfold.updates.RotationChangeProvider;
import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider;
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider;
@@ -62,16 +61,17 @@
public LauncherUnfoldAnimationController(
Launcher launcher,
WindowManager windowManager,
- UnfoldTransitionProgressProvider unfoldTransitionProgressProvider) {
+ UnfoldTransitionProgressProvider unfoldTransitionProgressProvider,
+ RotationChangeProvider rotationChangeProvider) {
mLauncher = launcher;
mProgressProvider = new ScopedUnfoldTransitionProgressProvider(
unfoldTransitionProgressProvider);
mUnfoldMoveFromCenterHotseatAnimator = new UnfoldMoveFromCenterHotseatAnimator(launcher,
- windowManager);
+ windowManager, rotationChangeProvider);
mUnfoldMoveFromCenterWorkspaceAnimator = new UnfoldMoveFromCenterWorkspaceAnimator(launcher,
- windowManager);
+ windowManager, rotationChangeProvider);
mNaturalOrientationProgressProvider = new NaturalRotationUnfoldProgressProvider(launcher,
- WindowManagerGlobal.getWindowManagerService(), mProgressProvider);
+ rotationChangeProvider, mProgressProvider);
mNaturalOrientationProgressProvider.init();
// Animated in all orientations
@@ -134,7 +134,7 @@
@Override
public void onTransitionProgress(float progress) {
if (mQsbInsettable != null) {
- float insetPercentage = comp(progress) * MAX_WIDTH_INSET_FRACTION;
+ float insetPercentage = (1 - progress) * MAX_WIDTH_INSET_FRACTION;
mQsbInsettable.setHorizontalInsets(insetPercentage);
}
}
diff --git a/quickstep/src/com/android/quickstep/util/LayoutUtils.java b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
index d0856be..f7136a5 100644
--- a/quickstep/src/com/android/quickstep/util/LayoutUtils.java
+++ b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
@@ -23,7 +23,7 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.util.DisplayController;
-import com.android.launcher3.util.DisplayController.NavigationMode;
+import com.android.launcher3.util.NavigationMode;
import com.android.quickstep.LauncherActivityInterface;
public class LayoutUtils {
diff --git a/quickstep/src/com/android/quickstep/util/LogUtils.kt b/quickstep/src/com/android/quickstep/util/LogUtils.kt
new file mode 100644
index 0000000..bad8506
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/LogUtils.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.util
+
+import android.util.Pair
+import com.android.internal.logging.InstanceIdSequence
+import com.android.launcher3.logging.InstanceId
+
+object LogUtils {
+ /**
+ * @return a [Pair] of two InstanceIds but with different types, one that can be used by framework
+ * (if needing to pass through an intent or such) and one used in Launcher
+ */
+ @JvmStatic
+ fun getShellShareableInstanceId():
+ Pair<com.android.internal.logging.InstanceId, InstanceId> {
+ val internalInstanceId = InstanceIdSequence(InstanceId.INSTANCE_ID_MAX).newInstanceId()
+ val launcherInstanceId = InstanceId(internalInstanceId.id)
+ return Pair(internalInstanceId, launcherInstanceId)
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
index e758f5b..69ed2f8 100644
--- a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
+++ b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
@@ -194,7 +194,11 @@
}
if (mIsPaused != isPaused) {
mIsPaused = isPaused;
- Log.d(TAG, "onMotionPauseChanged, paused=" + mIsPaused + " reason=" + reason);
+ String logString = "onMotionPauseChanged, paused=" + mIsPaused + " reason=" + reason;
+ if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+ Log.d(TAG, logString);
+ }
+ ActiveGestureLog.INSTANCE.addLog(logString);
boolean isFirstDetectedPause = !mHasEverBeenPaused && mIsPaused;
if (mIsPaused) {
AccessibilityManagerCompat.sendPauseDetectedEventToTest(mContext);
diff --git a/quickstep/src/com/android/quickstep/util/NavBarPosition.java b/quickstep/src/com/android/quickstep/util/NavBarPosition.java
index 527a6d2..59c8263 100644
--- a/quickstep/src/com/android/quickstep/util/NavBarPosition.java
+++ b/quickstep/src/com/android/quickstep/util/NavBarPosition.java
@@ -15,12 +15,12 @@
*/
package com.android.quickstep.util;
-import static com.android.launcher3.util.DisplayController.NavigationMode.NO_BUTTON;
+import static com.android.launcher3.util.NavigationMode.NO_BUTTON;
import android.view.Surface;
import com.android.launcher3.util.DisplayController.Info;
-import com.android.launcher3.util.DisplayController.NavigationMode;
+import com.android.launcher3.util.NavigationMode;
/**
* Utility class to check nav bar position.
diff --git a/quickstep/src/com/android/quickstep/util/OverviewToSplitTimings.java b/quickstep/src/com/android/quickstep/util/OverviewToSplitTimings.java
new file mode 100644
index 0000000..e189a66
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/OverviewToSplitTimings.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.util;
+
+import static com.android.launcher3.anim.Interpolators.EMPHASIZED;
+import static com.android.launcher3.anim.Interpolators.INSTANT;
+
+import android.view.animation.Interpolator;
+
+/**
+ * Timings for the Overview > OverviewSplitSelect animation.
+ */
+abstract class OverviewToSplitTimings implements SplitAnimationTimings {
+ // Overwritten by device-specific timings
+ abstract public int getPlaceholderFadeInStart();
+ abstract public int getPlaceholderFadeInEnd();
+ abstract public int getPlaceholderIconFadeInStart();
+ abstract public int getPlaceholderIconFadeInEnd();
+ abstract public int getStagedRectSlideStart();
+ abstract public int getStagedRectSlideEnd();
+ abstract public int getGridSlideStart();
+ abstract public int getGridSlideStagger();
+ abstract public int getGridSlideDuration();
+
+ // Common timings
+ public int getIconFadeStart() { return 0; }
+ public int getIconFadeEnd() { return 83; }
+ public int getActionsFadeStart() { return 0; }
+ public int getActionsFadeEnd() { return 83; }
+ public int getInstructionsContainerFadeInStart() { return 167; }
+ public int getInstructionsContainerFadeInEnd() { return 250; }
+ public int getInstructionsTextFadeInStart() { return 217; }
+ public int getInstructionsTextFadeInEnd() { return 300; }
+ public int getInstructionsUnfoldStart() { return 167; }
+ public int getInstructionsUnfoldEnd() { return 500; }
+ public Interpolator getGridSlidePrimaryInterpolator() { return EMPHASIZED; }
+ public Interpolator getGridSlideSecondaryInterpolator() { return INSTANT; }
+
+ abstract public int getDuration();
+ abstract public Interpolator getStagedRectXInterpolator();
+ abstract public Interpolator getStagedRectYInterpolator();
+ abstract public Interpolator getStagedRectScaleXInterpolator();
+ abstract public Interpolator getStagedRectScaleYInterpolator();
+
+ public float getGridSlideStartOffset() {
+ return (float) getGridSlideStart() / getDuration();
+ }
+ public float getGridSlideStaggerOffset() {
+ return (float) getGridSlideStagger() / getDuration();
+ }
+ public float getGridSlideDurationOffset() {
+ return (float) getGridSlideDuration() / getDuration();
+ }
+ public float getActionsFadeStartOffset() {
+ return (float) getActionsFadeStart() / getDuration();
+ }
+ public float getActionsFadeEndOffset() {
+ return (float) getActionsFadeEnd() / getDuration();
+ }
+ public float getIconFadeStartOffset() {
+ return (float) getIconFadeStart() / getDuration();
+ }
+ public float getIconFadeEndOffset() {
+ return (float) getIconFadeEnd() / getDuration();
+ }
+ public float getInstructionsContainerFadeInStartOffset() {
+ return (float) getInstructionsContainerFadeInStart() / getDuration();
+ }
+ public float getInstructionsContainerFadeInEndOffset() {
+ return (float) getInstructionsContainerFadeInEnd() / getDuration();
+ }
+ public float getInstructionsTextFadeInStartOffset() {
+ return (float) getInstructionsTextFadeInStart() / getDuration();
+ }
+ public float getInstructionsTextFadeInEndOffset() {
+ return (float) getInstructionsTextFadeInEnd() / getDuration();
+ }
+ public float getInstructionsUnfoldStartOffset() {
+ return (float) getInstructionsUnfoldStart() / getDuration();
+ }
+ public float getInstructionsUnfoldEndOffset() {
+ return (float) getInstructionsUnfoldEnd() / getDuration();
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/util/PhoneOverviewToSplitTimings.java b/quickstep/src/com/android/quickstep/util/PhoneOverviewToSplitTimings.java
new file mode 100644
index 0000000..f1dde53
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/PhoneOverviewToSplitTimings.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.util;
+
+import static com.android.launcher3.anim.Interpolators.EMPHASIZED;
+
+import android.view.animation.Interpolator;
+
+/**
+ * Timings for the Overview > OverviewSplitSelect animation on phones.
+ */
+public class PhoneOverviewToSplitTimings
+ extends OverviewToSplitTimings implements SplitAnimationTimings {
+ public int getPlaceholderFadeInStart() { return 0; }
+ public int getPlaceholderFadeInEnd() { return 133; }
+ public int getPlaceholderIconFadeInStart() { return 83; }
+ public int getPlaceholderIconFadeInEnd() { return 167; }
+ public int getStagedRectSlideStart() { return 0; }
+ public int getStagedRectSlideEnd() { return 333; }
+ public int getGridSlideStart() { return 100; }
+ public int getGridSlideStagger() { return 0; }
+ public int getGridSlideDuration() { return 417; }
+
+ public int getDuration() { return PHONE_ENTER_DURATION; }
+ public Interpolator getStagedRectXInterpolator() { return EMPHASIZED; }
+ public Interpolator getStagedRectYInterpolator() { return EMPHASIZED; }
+ public Interpolator getStagedRectScaleXInterpolator() { return EMPHASIZED; }
+ public Interpolator getStagedRectScaleYInterpolator() { return EMPHASIZED; }
+}
diff --git a/quickstep/src/com/android/quickstep/util/PhoneSplitToConfirmTimings.java b/quickstep/src/com/android/quickstep/util/PhoneSplitToConfirmTimings.java
new file mode 100644
index 0000000..3d9e09e
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/PhoneSplitToConfirmTimings.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.util;
+
+/**
+ * Timings for the OverviewSplitSelect > confirmed animation on phones.
+ */
+public class PhoneSplitToConfirmTimings
+ extends SplitToConfirmTimings implements SplitAnimationTimings {
+ public int getPlaceholderFadeInStart() { return 0; }
+ public int getPlaceholderFadeInEnd() { return 133; }
+ public int getPlaceholderIconFadeInStart() { return 50; }
+ public int getPlaceholderIconFadeInEnd() { return 133; }
+ public int getStagedRectSlideStart() { return 0; }
+ public int getStagedRectSlideEnd() { return 333; }
+
+ public int getDuration() { return PHONE_CONFIRM_DURATION; }
+}
diff --git a/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java b/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
index fb32581..e928b27 100644
--- a/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
+++ b/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
@@ -21,7 +21,7 @@
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.util.DisplayController.NavigationMode.NO_BUTTON;
+import static com.android.launcher3.util.NavigationMode.NO_BUTTON;
import android.content.SharedPreferences;
@@ -29,7 +29,6 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
import com.android.launcher3.appprediction.AppsDividerView;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.hybridhotseat.HotseatPredictionController;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.statemanager.StateManager.StateListener;
@@ -88,8 +87,7 @@
});
}
- if (DisplayController.getNavigationMode(launcher) == NO_BUTTON
- && FeatureFlags.ENABLE_ALL_APPS_EDU.get()) {
+ if (DisplayController.getNavigationMode(launcher) == NO_BUTTON) {
stateManager.addStateListener(new StateListener<LauncherState>() {
private static final int MAX_NUM_SWIPES_TO_TRIGGER_EDU = 3;
diff --git a/quickstep/src/com/android/quickstep/util/RecordingSurfaceTransaction.java b/quickstep/src/com/android/quickstep/util/RecordingSurfaceTransaction.java
new file mode 100644
index 0000000..a2f48dd
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/RecordingSurfaceTransaction.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.util;
+
+/**
+ * Extension for {@link SurfaceTransaction} which records the commands for mocking
+ */
+public class RecordingSurfaceTransaction extends SurfaceTransaction {
+
+ /**
+ * A mock builder which can be used for recording values
+ */
+ public final MockProperties mockProperties = new MockProperties();
+
+}
diff --git a/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java b/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
index ee82ae6..10f2eaa 100644
--- a/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
+++ b/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
@@ -16,23 +16,22 @@
package com.android.quickstep.util;
import android.animation.AnimatorSet;
-
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import android.view.RemoteAnimationTarget;
public abstract class RemoteAnimationProvider {
- public abstract AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets);
+ public abstract AnimatorSet createWindowAnimation(RemoteAnimationTarget[] appTargets,
+ RemoteAnimationTarget[] wallpaperTargets);
/**
* @return the target with the lowest opaque layer for a certain app animation, or null.
*/
- public static RemoteAnimationTargetCompat findLowestOpaqueLayerTarget(
- RemoteAnimationTargetCompat[] appTargets, int mode) {
+ public static RemoteAnimationTarget findLowestOpaqueLayerTarget(
+ RemoteAnimationTarget[] appTargets, int mode) {
int lowestLayer = Integer.MAX_VALUE;
int lowestLayerIndex = -1;
for (int i = appTargets.length - 1; i >= 0; i--) {
- RemoteAnimationTargetCompat target = appTargets[i];
+ RemoteAnimationTarget target = appTargets[i];
if (target.mode == mode && !target.isTranslucent) {
int layer = target.prefixOrderIndex;
if (layer < lowestLayer) {
diff --git a/quickstep/src/com/android/quickstep/util/RemoteFadeOutAnimationListener.java b/quickstep/src/com/android/quickstep/util/RemoteFadeOutAnimationListener.java
index 81c124f..382cf79 100644
--- a/quickstep/src/com/android/quickstep/util/RemoteFadeOutAnimationListener.java
+++ b/quickstep/src/com/android/quickstep/util/RemoteFadeOutAnimationListener.java
@@ -15,14 +15,14 @@
*/
package com.android.quickstep.util;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
+import static android.view.RemoteAnimationTarget.MODE_CLOSING;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.view.RemoteAnimationTarget;
+import android.view.SurfaceControl.Transaction;
import com.android.quickstep.RemoteAnimationTargets;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.TransactionCompat;
/**
* Animation listener which fades out the closing targets
@@ -32,24 +32,24 @@
private final RemoteAnimationTargets mTarget;
private boolean mFirstFrame = true;
- public RemoteFadeOutAnimationListener(RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets) {
+ public RemoteFadeOutAnimationListener(RemoteAnimationTarget[] appTargets,
+ RemoteAnimationTarget[] wallpaperTargets) {
mTarget = new RemoteAnimationTargets(appTargets, wallpaperTargets,
- new RemoteAnimationTargetCompat[0], MODE_CLOSING);
+ new RemoteAnimationTarget[0], MODE_CLOSING);
}
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
- TransactionCompat t = new TransactionCompat();
+ Transaction t = new Transaction();
if (mFirstFrame) {
- for (RemoteAnimationTargetCompat target : mTarget.unfilteredApps) {
+ for (RemoteAnimationTarget target : mTarget.unfilteredApps) {
t.show(target.leash);
}
mFirstFrame = false;
}
float alpha = 1 - valueAnimator.getAnimatedFraction();
- for (RemoteAnimationTargetCompat app : mTarget.apps) {
+ for (RemoteAnimationTarget app : mTarget.apps) {
t.setAlpha(app.leash, alpha);
}
t.apply();
diff --git a/quickstep/src/com/android/quickstep/util/SplitAnimationTimings.java b/quickstep/src/com/android/quickstep/util/SplitAnimationTimings.java
new file mode 100644
index 0000000..7dc1b32
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/SplitAnimationTimings.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.util;
+
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+
+import android.view.animation.Interpolator;
+
+/**
+ * An interface that supports the centralization of timing information for splitscreen animations.
+ */
+public interface SplitAnimationTimings {
+ int TABLET_ENTER_DURATION = 866;
+ int TABLET_CONFIRM_DURATION = 500;
+
+ int PHONE_ENTER_DURATION = 517;
+ int PHONE_CONFIRM_DURATION = 333;
+
+ int ABORT_DURATION = 500;
+
+ SplitAnimationTimings TABLET_OVERVIEW_TO_SPLIT = new TabletOverviewToSplitTimings();
+ SplitAnimationTimings TABLET_HOME_TO_SPLIT = new TabletHomeToSplitTimings();
+ SplitAnimationTimings TABLET_SPLIT_TO_CONFIRM = new TabletSplitToConfirmTimings();
+
+ SplitAnimationTimings PHONE_OVERVIEW_TO_SPLIT = new PhoneOverviewToSplitTimings();
+ SplitAnimationTimings PHONE_SPLIT_TO_CONFIRM = new PhoneSplitToConfirmTimings();
+
+ // Shared methods
+ int getDuration();
+ int getPlaceholderFadeInStart();
+ int getPlaceholderFadeInEnd();
+ int getPlaceholderIconFadeInStart();
+ int getPlaceholderIconFadeInEnd();
+ int getStagedRectSlideStart();
+ int getStagedRectSlideEnd();
+ Interpolator getStagedRectXInterpolator();
+ Interpolator getStagedRectYInterpolator();
+ Interpolator getStagedRectScaleXInterpolator();
+ Interpolator getStagedRectScaleYInterpolator();
+ default float getPlaceholderFadeInStartOffset() {
+ return (float) getPlaceholderFadeInStart() / getDuration();
+ }
+ default float getPlaceholderFadeInEndOffset() {
+ return (float) getPlaceholderFadeInEnd() / getDuration();
+ }
+ default float getPlaceholderIconFadeInStartOffset() {
+ return (float) getPlaceholderIconFadeInStart() / getDuration();
+ }
+ default float getPlaceholderIconFadeInEndOffset() {
+ return (float) getPlaceholderIconFadeInEnd() / getDuration();
+ }
+ default float getStagedRectSlideStartOffset() {
+ return (float) getStagedRectSlideStart() / getDuration();
+ }
+ default float getStagedRectSlideEndOffset() {
+ return (float) getStagedRectSlideEnd() / getDuration();
+ }
+
+ // Defaults for OverviewToSplit
+ default float getGridSlideStartOffset() { return 0; }
+ default float getGridSlideStaggerOffset() { return 0; }
+ default float getGridSlideDurationOffset() { return 0; }
+ default float getActionsFadeStartOffset() { return 0; }
+ default float getActionsFadeEndOffset() { return 0; }
+ default float getIconFadeStartOffset() { return 0; }
+ default float getIconFadeEndOffset() { return 0; }
+ default float getInstructionsContainerFadeInStartOffset() { return 0; }
+ default float getInstructionsContainerFadeInEndOffset() { return 0; }
+ default float getInstructionsTextFadeInStartOffset() { return 0; }
+ default float getInstructionsTextFadeInEndOffset() { return 0; }
+ default float getInstructionsUnfoldStartOffset() { return 0; }
+ default float getInstructionsUnfoldEndOffset() { return 0; }
+ default Interpolator getGridSlidePrimaryInterpolator() { return LINEAR; }
+ default Interpolator getGridSlideSecondaryInterpolator() { return LINEAR; }
+
+ // Defaults for HomeToSplit
+ default float getScrimFadeInStartOffset() { return 0; }
+ default float getScrimFadeInEndOffset() { return 0; }
+
+ // Defaults for SplitToConfirm
+ default float getInstructionsFadeStartOffset() { return 0; }
+ default float getInstructionsFadeEndOffset() { return 0; }
+}
+
diff --git a/quickstep/src/com/android/quickstep/util/SplitScreenBounds.java b/quickstep/src/com/android/quickstep/util/SplitScreenBounds.java
deleted file mode 100644
index 483a1c6..0000000
--- a/quickstep/src/com/android/quickstep/util/SplitScreenBounds.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep.util;
-
-import static android.view.Surface.ROTATION_0;
-import static android.view.Surface.ROTATION_180;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.os.Build;
-import android.view.WindowManager;
-import android.view.WindowMetrics;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.UiThread;
-
-import com.android.launcher3.R;
-import com.android.launcher3.util.DisplayController;
-import com.android.launcher3.util.WindowBounds;
-
-import java.util.ArrayList;
-
-/**
- * Utility class to hold the information abound a window bounds for split screen
- */
-@TargetApi(Build.VERSION_CODES.R)
-public class SplitScreenBounds {
-
- public static final SplitScreenBounds INSTANCE = new SplitScreenBounds();
- private final ArrayList<OnChangeListener> mListeners = new ArrayList<>();
-
- @Nullable
- private WindowBounds mBounds;
-
- private SplitScreenBounds() { }
-
- @UiThread
- public void setSecondaryWindowBounds(@NonNull WindowBounds bounds) {
- if (!bounds.equals(mBounds)) {
- mBounds = bounds;
- for (OnChangeListener listener : mListeners) {
- listener.onSecondaryWindowBoundsChanged();
- }
- }
- }
-
- public @NonNull WindowBounds getSecondaryWindowBounds(Context context) {
- if (mBounds == null) {
- mBounds = createDefaultWindowBounds(context);
- }
- return mBounds;
- }
-
- /**
- * Creates window bounds as 50% of device size
- */
- private static WindowBounds createDefaultWindowBounds(Context context) {
- WindowMetrics wm = context.getSystemService(WindowManager.class).getMaximumWindowMetrics();
- WindowBounds bounds = WindowBounds.fromWindowMetrics(wm);
-
- int rotation = DisplayController.INSTANCE.get(context).getInfo().rotation;
- int halfDividerSize = context.getResources()
- .getDimensionPixelSize(R.dimen.multi_window_task_divider_size) / 2;
-
- if (rotation == ROTATION_0 || rotation == ROTATION_180) {
- bounds.bounds.top = bounds.insets.top + bounds.availableSize.y / 2 + halfDividerSize;
- bounds.insets.top = 0;
- } else {
- bounds.bounds.left = bounds.insets.left + bounds.availableSize.x / 2 + halfDividerSize;
- bounds.insets.left = 0;
- }
- return new WindowBounds(bounds.bounds, bounds.insets);
- }
-
- public void addOnChangeListener(OnChangeListener listener) {
- mListeners.add(listener);
- }
-
- public void removeOnChangeListener(OnChangeListener listener) {
- mListeners.remove(listener);
- }
-
- /**
- * Interface to receive window bounds changes
- */
- public interface OnChangeListener {
-
- /**
- * Called when window bounds for secondary window changes
- */
- void onSecondaryWindowBoundsChanged();
- }
-}
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index 0ca5574..c263fe8 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -22,24 +22,38 @@
import static com.android.launcher3.Utilities.postAsyncCallback;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.SplitConfigurationOptions.DEFAULT_SPLIT_RATIO;
-import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
-import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
+import static com.android.launcher3.util.SplitConfigurationOptions.getOppositeStagePosition;
import android.annotation.NonNull;
+import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.ActivityThread;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ShortcutInfo;
import android.os.Handler;
import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
import android.text.TextUtils;
+import android.util.Log;
+import android.util.Pair;
import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
+import android.window.IRemoteTransition;
+import android.window.IRemoteTransitionFinishedCallback;
+import android.window.RemoteTransition;
import android.window.TransitionInfo;
import androidx.annotation.Nullable;
+import com.android.internal.logging.InstanceId;
+import com.android.launcher3.logging.StatsLogManager;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.testing.TestLogging;
@@ -49,14 +63,11 @@
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.RemoteAnimationAdapterCompat;
import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.RemoteTransitionCompat;
-import com.android.systemui.shared.system.RemoteTransitionRunner;
import java.util.function.Consumer;
@@ -65,44 +76,77 @@
* and is in the process of either a) selecting a second app or b) exiting intention to invoke split
*/
public class SplitSelectStateController {
+ private static final String TAG = "SplitSelectStateCtor";
private final Context mContext;
private final Handler mHandler;
+ private StatsLogManager mStatsLogManager;
private final SystemUiProxy mSystemUiProxy;
private final StateManager mStateManager;
private final DepthController mDepthController;
private @StagePosition int mStagePosition;
+ private ItemInfo mItemInfo;
private Intent mInitialTaskIntent;
private int mInitialTaskId = INVALID_TASK_ID;
+ private Intent mSecondTaskIntent;
private int mSecondTaskId = INVALID_TASK_ID;
- private String mSecondTaskPackageName;
private boolean mRecentsAnimationRunning;
+ @Nullable
+ private UserHandle mUser;
/** If not null, this is the TaskView we want to launch from */
@Nullable
private GroupedTaskView mLaunchingTaskView;
+ /** Represents where split is intended to be invoked from. */
+ private StatsLogManager.EventEnum mSplitEvent;
+
+ private FloatingTaskView mFirstFloatingTaskView;
public SplitSelectStateController(Context context, Handler handler, StateManager stateManager,
- DepthController depthController) {
+ DepthController depthController, StatsLogManager statsLogManager) {
mContext = context;
mHandler = handler;
+ mStatsLogManager = statsLogManager;
mSystemUiProxy = SystemUiProxy.INSTANCE.get(mContext);
mStateManager = stateManager;
mDepthController = depthController;
}
/**
- * To be called after first task selected
+ * To be called after first task selected in Overview.
*/
- public void setInitialTaskSelect(int taskId, @StagePosition int stagePosition) {
- mInitialTaskId = taskId;
- mStagePosition = stagePosition;
- mInitialTaskIntent = null;
+ public void setInitialTaskSelect(Task task, @StagePosition int stagePosition,
+ StatsLogManager.EventEnum splitEvent, ItemInfo itemInfo) {
+ mInitialTaskId = task.key.id;
+ setInitialData(stagePosition, splitEvent, itemInfo);
}
- public void setInitialTaskSelect(Intent intent, @StagePosition int stagePosition) {
+ /**
+ * To be called after first task selected from home or all apps.
+ */
+ public void setInitialTaskSelect(Intent intent, @StagePosition int stagePosition,
+ @NonNull ItemInfo itemInfo, StatsLogManager.EventEnum splitEvent) {
mInitialTaskIntent = intent;
+ mUser = itemInfo.user;
+ mItemInfo = itemInfo;
+ setInitialData(stagePosition, splitEvent, itemInfo);
+ }
+
+ /**
+ * To be called after first task selected from using a split shortcut from the fullscreen
+ * running app.
+ */
+ public void setInitialTaskSelect(ActivityManager.RunningTaskInfo info,
+ @StagePosition int stagePosition, @NonNull ItemInfo itemInfo,
+ StatsLogManager.EventEnum splitEvent) {
+ mInitialTaskId = info.taskId;
+ setInitialData(stagePosition, splitEvent, itemInfo);
+ }
+
+ private void setInitialData(@StagePosition int stagePosition,
+ StatsLogManager.EventEnum splitEvent, ItemInfo itemInfo) {
+ mItemInfo = itemInfo;
mStagePosition = stagePosition;
- mInitialTaskId = INVALID_TASK_ID;
+ mSplitEvent = splitEvent;
}
/**
@@ -110,23 +154,17 @@
* to be launched. Call after launcher side animations are complete.
*/
public void launchSplitTasks(Consumer<Boolean> callback) {
- final Intent fillInIntent;
- if (mInitialTaskIntent != null) {
- fillInIntent = new Intent();
- if (TextUtils.equals(mInitialTaskIntent.getComponent().getPackageName(),
- mSecondTaskPackageName)) {
- fillInIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
- }
- } else {
- fillInIntent = null;
- }
- final PendingIntent pendingIntent =
- mInitialTaskIntent == null ? null : PendingIntent.getActivity(mContext, 0,
- mInitialTaskIntent, FLAG_MUTABLE);
- launchTasks(mInitialTaskId, pendingIntent, fillInIntent, mSecondTaskId, mStagePosition,
- callback, false /* freezeTaskList */, DEFAULT_SPLIT_RATIO);
- }
+ Pair<InstanceId, com.android.launcher3.logging.InstanceId> instanceIds =
+ LogUtils.getShellShareableInstanceId();
+ launchTasks(mInitialTaskId, mInitialTaskIntent, mSecondTaskId, mSecondTaskIntent,
+ mStagePosition, callback, false /* freezeTaskList */, DEFAULT_SPLIT_RATIO,
+ instanceIds.first);
+ mStatsLogManager.logger()
+ .withItemInfo(mItemInfo)
+ .withInstanceId(instanceIds.second)
+ .log(mSplitEvent);
+ }
/**
* To be called as soon as user selects the second task (even if animations aren't complete)
@@ -134,16 +172,17 @@
*/
public void setSecondTask(Task task) {
mSecondTaskId = task.key.id;
- if (mInitialTaskIntent != null) {
- mSecondTaskPackageName = task.getTopComponent().getPackageName();
- }
+ }
+
+ public void setSecondTask(Intent intent) {
+ mSecondTaskIntent = intent;
}
/**
* To be called when we want to launch split pairs from an existing GroupedTaskView.
*/
- public void launchTasks(GroupedTaskView groupedTaskView,
- Consumer<Boolean> callback, boolean freezeTaskList) {
+ public void launchTasks(GroupedTaskView groupedTaskView, Consumer<Boolean> callback,
+ boolean freezeTaskList) {
mLaunchingTaskView = groupedTaskView;
TaskView.TaskIdAttributeContainer[] taskIdAttributeContainers =
groupedTaskView.getTaskIdAttributeContainers();
@@ -159,130 +198,224 @@
*/
public void launchTasks(int taskId1, int taskId2, @StagePosition int stagePosition,
Consumer<Boolean> callback, boolean freezeTaskList, float splitRatio) {
- launchTasks(taskId1, null /* taskPendingIntent */, null /* fillInIntent */, taskId2,
- stagePosition, callback, freezeTaskList, splitRatio);
+ launchTasks(taskId1, null /* intent1 */, taskId2, null /* intent2 */, stagePosition,
+ callback, freezeTaskList, splitRatio, null);
}
/**
* To be called when we want to launch split pairs from Overview. Split can be initiated from
* either Overview or home, or all apps. Either both taskIds are set, or a pending intent + a
* fill in intent with a taskId2 are set.
- * @param taskPendingIntent is null when split is initiated from Overview
+ * @param intent1 is null when split is initiated from Overview
* @param stagePosition representing location of task1
+ * @param shellInstanceId loggingId to be used by shell, will be non-null for actions that
+ * create a split instance, null for cases that bring existing instaces to the
+ * foreground (quickswitch, launching previous pairs from overview)
*/
- public void launchTasks(int taskId1, @Nullable PendingIntent taskPendingIntent,
- @Nullable Intent fillInIntent, int taskId2, @StagePosition int stagePosition,
- Consumer<Boolean> callback, boolean freezeTaskList, float splitRatio) {
+ public void launchTasks(int taskId1, @Nullable Intent intent1, int taskId2,
+ @Nullable Intent intent2, @StagePosition int stagePosition,
+ Consumer<Boolean> callback, boolean freezeTaskList, float splitRatio,
+ @Nullable InstanceId shellInstanceId) {
TestLogging.recordEvent(
TestProtocol.SEQUENCE_MAIN, "launchSplitTasks");
- // Assume initial task is for top/left part of screen
- final int[] taskIds = stagePosition == STAGE_POSITION_TOP_OR_LEFT
- ? new int[]{taskId1, taskId2}
- : new int[]{taskId2, taskId1};
+ final ActivityOptions options1 = ActivityOptions.makeBasic();
+ if (freezeTaskList) {
+ options1.setFreezeRecentTasksReordering();
+ }
if (TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) {
- RemoteSplitLaunchTransitionRunner animationRunner =
- new RemoteSplitLaunchTransitionRunner(taskId1, taskPendingIntent, taskId2,
- callback);
- mSystemUiProxy.startTasks(taskIds[0], null /* mainOptions */, taskIds[1],
- null /* sideOptions */, STAGE_POSITION_BOTTOM_OR_RIGHT, splitRatio,
- new RemoteTransitionCompat(animationRunner, MAIN_EXECUTOR,
- ActivityThread.currentActivityThread().getApplicationThread()));
- // TODO: handle intent + task with shell transition
+ final RemoteSplitLaunchTransitionRunner animationRunner =
+ new RemoteSplitLaunchTransitionRunner(taskId1, taskId2, callback);
+ final RemoteTransition remoteTransition = new RemoteTransition(animationRunner,
+ ActivityThread.currentActivityThread().getApplicationThread());
+ if (intent1 == null && intent2 == null) {
+ mSystemUiProxy.startTasks(taskId1, options1.toBundle(), taskId2,
+ null /* options2 */, stagePosition, splitRatio, remoteTransition,
+ shellInstanceId);
+ } else if (intent2 == null) {
+ launchIntentOrShortcut(intent1, options1, taskId2, stagePosition, splitRatio,
+ remoteTransition, shellInstanceId);
+ } else if (intent1 == null) {
+ launchIntentOrShortcut(intent2, options1, taskId1,
+ getOppositeStagePosition(stagePosition), splitRatio, remoteTransition,
+ shellInstanceId);
+ } else {
+ mSystemUiProxy.startIntents(getPendingIntent(intent1), options1.toBundle(),
+ getPendingIntent(intent2), null /* options2 */, stagePosition,
+ splitRatio, remoteTransition, shellInstanceId);
+ }
} else {
- RemoteSplitLaunchAnimationRunner animationRunner =
- new RemoteSplitLaunchAnimationRunner(taskId1, taskPendingIntent, taskId2,
- callback);
+ final RemoteSplitLaunchAnimationRunner animationRunner =
+ new RemoteSplitLaunchAnimationRunner(taskId1, taskId2, callback);
final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter(
- RemoteAnimationAdapterCompat.wrapRemoteAnimationRunner(animationRunner),
- 300, 150,
+ animationRunner, 300, 150,
ActivityThread.currentActivityThread().getApplicationThread());
- ActivityOptions mainOpts = ActivityOptions.makeBasic();
- if (freezeTaskList) {
- mainOpts.setFreezeRecentTasksReordering();
- }
- if (taskPendingIntent == null) {
- mSystemUiProxy.startTasksWithLegacyTransition(taskIds[0], mainOpts.toBundle(),
- taskIds[1], null /* sideOptions */, STAGE_POSITION_BOTTOM_OR_RIGHT,
- splitRatio, adapter);
+ if (intent1 == null && intent2 == null) {
+ mSystemUiProxy.startTasksWithLegacyTransition(taskId1, options1.toBundle(),
+ taskId2, null /* options2 */, stagePosition, splitRatio, adapter,
+ shellInstanceId);
+ } else if (intent2 == null) {
+ launchIntentOrShortcutLegacy(intent1, options1, taskId2, stagePosition, splitRatio,
+ adapter, shellInstanceId);
+ } else if (intent1 == null) {
+ launchIntentOrShortcutLegacy(intent2, options1, taskId1,
+ getOppositeStagePosition(stagePosition), splitRatio, adapter,
+ shellInstanceId);
} else {
- mSystemUiProxy.startIntentAndTaskWithLegacyTransition(taskPendingIntent,
- fillInIntent, taskId2, mainOpts.toBundle(), null /* sideOptions */,
- stagePosition, splitRatio, adapter);
+ mSystemUiProxy.startIntentsWithLegacyTransition(getPendingIntent(intent1),
+ options1.toBundle(), getPendingIntent(intent2), null /* options2 */,
+ stagePosition, splitRatio, adapter, shellInstanceId);
}
}
}
+ private void launchIntentOrShortcut(Intent intent, ActivityOptions options1, int taskId,
+ @StagePosition int stagePosition, float splitRatio, RemoteTransition remoteTransition,
+ @Nullable InstanceId shellInstanceId) {
+ PendingIntent pendingIntent = getPendingIntent(intent);
+ final ShortcutInfo shortcutInfo = getShortcutInfo(intent,
+ pendingIntent.getCreatorUserHandle());
+ if (shortcutInfo != null) {
+ mSystemUiProxy.startShortcutAndTask(shortcutInfo,
+ options1.toBundle(), taskId, null /* options2 */, stagePosition,
+ splitRatio, remoteTransition, shellInstanceId);
+ } else {
+ mSystemUiProxy.startIntentAndTask(pendingIntent, options1.toBundle(), taskId,
+ null /* options2 */, stagePosition, splitRatio, remoteTransition,
+ shellInstanceId);
+ }
+ }
+
+ private void launchIntentOrShortcutLegacy(Intent intent, ActivityOptions options1, int taskId,
+ @StagePosition int stagePosition, float splitRatio, RemoteAnimationAdapter adapter,
+ @Nullable InstanceId shellInstanceId) {
+ PendingIntent pendingIntent = getPendingIntent(intent);
+ final ShortcutInfo shortcutInfo = getShortcutInfo(intent,
+ pendingIntent.getCreatorUserHandle());
+ if (shortcutInfo != null) {
+ mSystemUiProxy.startShortcutAndTaskWithLegacyTransition(shortcutInfo,
+ options1.toBundle(), taskId, null /* options2 */, stagePosition,
+ splitRatio, adapter, shellInstanceId);
+ } else {
+ mSystemUiProxy.startIntentAndTaskWithLegacyTransition(pendingIntent,
+ options1.toBundle(), taskId, null /* options2 */, stagePosition, splitRatio,
+ adapter, shellInstanceId);
+ }
+ }
+
+ private PendingIntent getPendingIntent(Intent intent) {
+ return intent == null ? null : (mUser != null
+ ? PendingIntent.getActivityAsUser(mContext, 0, intent,
+ FLAG_MUTABLE, null /* options */, mUser)
+ : PendingIntent.getActivity(mContext, 0, intent, FLAG_MUTABLE));
+ }
+
public @StagePosition int getActiveSplitStagePosition() {
return mStagePosition;
}
+ public StatsLogManager.EventEnum getSplitEvent() {
+ return mSplitEvent;
+ }
+
public void setRecentsAnimationRunning(boolean running) {
- this.mRecentsAnimationRunning = running;
+ mRecentsAnimationRunning = running;
+ }
+
+ @Nullable
+ private ShortcutInfo getShortcutInfo(Intent intent, UserHandle userHandle) {
+ if (intent == null || intent.getPackage() == null) {
+ return null;
+ }
+
+ final String shortcutId = intent.getStringExtra(ShortcutKey.EXTRA_SHORTCUT_ID);
+ if (shortcutId == null) {
+ return null;
+ }
+
+ try {
+ final Context context = mContext.createPackageContextAsUser(
+ intent.getPackage(), 0 /* flags */, userHandle);
+ return new ShortcutInfo.Builder(context, shortcutId).build();
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(TAG, "Failed to create a ShortcutInfo for " + intent.getPackage());
+ }
+
+ return null;
}
/**
* Requires Shell Transitions
*/
- private class RemoteSplitLaunchTransitionRunner implements RemoteTransitionRunner {
+ private class RemoteSplitLaunchTransitionRunner extends IRemoteTransition.Stub {
private final int mInitialTaskId;
- private final PendingIntent mInitialTaskPendingIntent;
private final int mSecondTaskId;
private final Consumer<Boolean> mSuccessCallback;
- RemoteSplitLaunchTransitionRunner(int initialTaskId, PendingIntent initialTaskPendingIntent,
- int secondTaskId, Consumer<Boolean> callback) {
+ RemoteSplitLaunchTransitionRunner(int initialTaskId, int secondTaskId,
+ Consumer<Boolean> callback) {
mInitialTaskId = initialTaskId;
- mInitialTaskPendingIntent = initialTaskPendingIntent;
mSecondTaskId = secondTaskId;
mSuccessCallback = callback;
}
@Override
- public void startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction t, @NonNull Runnable finishCallback) {
- TaskViewUtils.composeRecentsSplitLaunchAnimator(mLaunchingTaskView, mStateManager,
- mDepthController, mInitialTaskId, mInitialTaskPendingIntent, mSecondTaskId,
- info, t, () -> {
- finishCallback.run();
- if (mSuccessCallback != null) {
- mSuccessCallback.accept(true);
- }
- });
- // After successful launch, call resetState
- resetState();
+ public void startAnimation(IBinder transition, TransitionInfo info,
+ SurfaceControl.Transaction t,
+ IRemoteTransitionFinishedCallback finishedCallback) {
+ final Runnable finishAdapter = () -> {
+ try {
+ finishedCallback.onTransitionFinished(null /* wct */, null /* sct */);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to call transition finished callback", e);
+ }
+ };
+
+ MAIN_EXECUTOR.execute(() -> {
+ TaskViewUtils.composeRecentsSplitLaunchAnimator(mLaunchingTaskView, mStateManager,
+ mDepthController, mInitialTaskId, mSecondTaskId, info, t, () -> {
+ finishAdapter.run();
+ if (mSuccessCallback != null) {
+ mSuccessCallback.accept(true);
+ }
+ });
+ // After successful launch, call resetState
+ resetState();
+ });
}
+
+ @Override
+ public void mergeAnimation(IBinder transition, TransitionInfo info,
+ SurfaceControl.Transaction t, IBinder mergeTarget,
+ IRemoteTransitionFinishedCallback finishedCallback) { }
}
/**
* LEGACY
* Remote animation runner for animation to launch an app.
*/
- private class RemoteSplitLaunchAnimationRunner implements RemoteAnimationRunnerCompat {
+ private class RemoteSplitLaunchAnimationRunner extends RemoteAnimationRunnerCompat {
private final int mInitialTaskId;
- private final PendingIntent mInitialTaskPendingIntent;
private final int mSecondTaskId;
private final Consumer<Boolean> mSuccessCallback;
- RemoteSplitLaunchAnimationRunner(int initialTaskId, PendingIntent initialTaskPendingIntent,
- int secondTaskId, Consumer<Boolean> successCallback) {
+ RemoteSplitLaunchAnimationRunner(int initialTaskId, int secondTaskId,
+ Consumer<Boolean> successCallback) {
mInitialTaskId = initialTaskId;
- mInitialTaskPendingIntent = initialTaskPendingIntent;
mSecondTaskId = secondTaskId;
mSuccessCallback = successCallback;
}
@Override
- public void onAnimationStart(int transit, RemoteAnimationTargetCompat[] apps,
- RemoteAnimationTargetCompat[] wallpapers, RemoteAnimationTargetCompat[] nonApps,
+ public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
Runnable finishedCallback) {
postAsyncCallback(mHandler,
() -> TaskViewUtils.composeRecentsSplitLaunchAnimatorLegacy(
- mLaunchingTaskView, mInitialTaskId, mInitialTaskPendingIntent,
- mSecondTaskId, apps, wallpapers, nonApps, mStateManager,
- mDepthController, () -> {
+ mLaunchingTaskView, mInitialTaskId, mSecondTaskId, apps, wallpapers,
+ nonApps, mStateManager, mDepthController, () -> {
finishedCallback.run();
if (mSuccessCallback != null) {
mSuccessCallback.accept(true);
@@ -292,7 +425,7 @@
}
@Override
- public void onAnimationCancelled() {
+ public void onAnimationCancelled(boolean isKeyguardOccluded) {
postAsyncCallback(mHandler, () -> {
if (mSuccessCallback != null) {
// Launching legacy tasks while recents animation is running will always cause
@@ -311,9 +444,12 @@
mInitialTaskId = INVALID_TASK_ID;
mInitialTaskIntent = null;
mSecondTaskId = INVALID_TASK_ID;
+ mSecondTaskIntent = null;
mStagePosition = SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
mRecentsAnimationRunning = false;
mLaunchingTaskView = null;
+ mItemInfo = null;
+ mSplitEvent = null;
}
/**
@@ -321,7 +457,7 @@
* chosen
*/
public boolean isSplitSelectActive() {
- return isInitialTaskIntentSet() && mSecondTaskId == INVALID_TASK_ID;
+ return isInitialTaskIntentSet() && !isSecondTaskIntentSet();
}
/**
@@ -329,7 +465,7 @@
* be launched
*/
public boolean isBothSplitAppsConfirmed() {
- return isInitialTaskIntentSet() && mSecondTaskId != INVALID_TASK_ID;
+ return isInitialTaskIntentSet() && isSecondTaskIntentSet();
}
private boolean isInitialTaskIntentSet() {
@@ -339,4 +475,16 @@
public int getInitialTaskId() {
return mInitialTaskId;
}
+
+ private boolean isSecondTaskIntentSet() {
+ return (mSecondTaskId != INVALID_TASK_ID || mSecondTaskIntent != null);
+ }
+
+ public void setFirstFloatingTaskView(FloatingTaskView floatingTaskView) {
+ mFirstFloatingTaskView = floatingTaskView;
+ }
+
+ public FloatingTaskView getFirstFloatingTaskView() {
+ return mFirstFloatingTaskView;
+ }
}
diff --git a/quickstep/src/com/android/quickstep/util/SplitToConfirmTimings.java b/quickstep/src/com/android/quickstep/util/SplitToConfirmTimings.java
new file mode 100644
index 0000000..f5b00cf
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/SplitToConfirmTimings.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.util;
+
+import static com.android.launcher3.anim.Interpolators.EMPHASIZED;
+
+import android.view.animation.Interpolator;
+
+/**
+ * Timings for the OverviewSplitSelect > confirmed animation.
+ */
+abstract class SplitToConfirmTimings implements SplitAnimationTimings {
+ // Overwritten by device-specific timings
+ abstract public int getPlaceholderFadeInStart();
+ abstract public int getPlaceholderFadeInEnd();
+ abstract public int getPlaceholderIconFadeInStart();
+ abstract public int getPlaceholderIconFadeInEnd();
+ abstract public int getStagedRectSlideStart();
+ abstract public int getStagedRectSlideEnd();
+
+ // Common timings
+ public int getInstructionsFadeStart() { return 0; }
+ public int getInstructionsFadeEnd() { return 67; }
+ public Interpolator getStagedRectXInterpolator() { return EMPHASIZED; }
+ public Interpolator getStagedRectYInterpolator() { return EMPHASIZED; }
+ public Interpolator getStagedRectScaleXInterpolator() { return EMPHASIZED; }
+ public Interpolator getStagedRectScaleYInterpolator() { return EMPHASIZED; }
+
+ abstract public int getDuration();
+
+ public float getInstructionsFadeStartOffset() {
+ return (float) getInstructionsFadeStart() / getDuration();
+ }
+ public float getInstructionsFadeEndOffset() {
+ return (float) getInstructionsFadeEnd() / getDuration();
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java b/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java
new file mode 100644
index 0000000..e8a4b0a
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.util;
+
+import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_FULLSCREEN_WITH_KEYBOARD_SHORTCUTS;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE;
+
+import android.content.Intent;
+import android.view.View;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
+
+/** Handles when the stage split lands on the home screen. */
+public class SplitToWorkspaceController {
+
+ private final Launcher mLauncher;
+ private final SplitSelectStateController mController;
+
+ public SplitToWorkspaceController(Launcher launcher, SplitSelectStateController controller) {
+ mLauncher = launcher;
+ mController = controller;
+ }
+
+ /**
+ * Handles second app selection from stage split. If the item can't be opened in split or
+ * it's not in stage split state, we pass it onto Launcher's default item click handler.
+ */
+ public boolean handleSecondAppSelectionForSplit(View view) {
+ if ((!ENABLE_SPLIT_FROM_FULLSCREEN_WITH_KEYBOARD_SHORTCUTS.get()
+ && !ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get())
+ || !mController.isSplitSelectActive()) {
+ return false;
+ }
+ Object tag = view.getTag();
+ Intent intent;
+ if (tag instanceof WorkspaceItemInfo) {
+ final WorkspaceItemInfo workspaceItemInfo = (WorkspaceItemInfo) tag;
+ intent = workspaceItemInfo.intent;
+ } else if (tag instanceof com.android.launcher3.model.data.AppInfo) {
+ final com.android.launcher3.model.data.AppInfo appInfo =
+ (com.android.launcher3.model.data.AppInfo) tag;
+ intent = appInfo.intent;
+ } else {
+ return false;
+ }
+ mController.setSecondTask(intent);
+ mController.launchSplitTasks(aBoolean -> mLauncher.getDragLayer().removeView(
+ mController.getFirstFloatingTaskView()));
+ return true;
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java b/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java
new file mode 100644
index 0000000..24d8326
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.util;
+
+import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_FULLSCREEN_WITH_KEYBOARD_SHORTCUTS;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_KEYBOARD_SHORTCUT_SPLIT_LEFT_TOP;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_KEYBOARD_SHORTCUT_SPLIT_RIGHT_BOTTOM;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.app.ActivityManager;
+import android.content.Intent;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.view.View;
+
+import androidx.annotation.BinderThread;
+
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.R;
+import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.uioverrides.QuickstepLauncher;
+import com.android.quickstep.OverviewCommandHelper;
+import com.android.quickstep.OverviewComponentObserver;
+import com.android.quickstep.RecentsAnimationCallbacks;
+import com.android.quickstep.RecentsAnimationController;
+import com.android.quickstep.RecentsAnimationDeviceState;
+import com.android.quickstep.RecentsAnimationTargets;
+import com.android.quickstep.RecentsModel;
+import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.views.FloatingTaskView;
+import com.android.quickstep.views.RecentsView;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+
+/** Transitions app from fullscreen to stage split when triggered from keyboard shortcuts. */
+public class SplitWithKeyboardShortcutController {
+
+ private final QuickstepLauncher mLauncher;
+ private final SplitSelectStateController mController;
+ private final OverviewComponentObserver mOverviewComponentObserver;
+
+ private final int mSplitPlaceholderSize;
+ private final int mSplitPlaceholderInset;
+
+ public SplitWithKeyboardShortcutController(QuickstepLauncher launcher,
+ SplitSelectStateController controller) {
+ mLauncher = launcher;
+ mController = controller;
+ RecentsAnimationDeviceState deviceState = new RecentsAnimationDeviceState(
+ launcher.getApplicationContext());
+ mOverviewComponentObserver = new OverviewComponentObserver(launcher.getApplicationContext(),
+ deviceState);
+
+ mSplitPlaceholderSize = mLauncher.getResources().getDimensionPixelSize(
+ R.dimen.split_placeholder_size);
+ mSplitPlaceholderInset = mLauncher.getResources().getDimensionPixelSize(
+ R.dimen.split_placeholder_inset);
+ }
+
+ @BinderThread
+ public void enterStageSplit(boolean leftOrTop) {
+ if (!ENABLE_SPLIT_FROM_FULLSCREEN_WITH_KEYBOARD_SHORTCUTS.get()) {
+ return;
+ }
+ RecentsAnimationCallbacks callbacks = new RecentsAnimationCallbacks(
+ SystemUiProxy.INSTANCE.get(mLauncher.getApplicationContext()),
+ false /* allowMinimizeSplitScreen */);
+ SplitWithKeyboardShortcutRecentsAnimationListener listener =
+ new SplitWithKeyboardShortcutRecentsAnimationListener(leftOrTop);
+
+ MAIN_EXECUTOR.execute(() -> {
+ callbacks.addListener(listener);
+ UI_HELPER_EXECUTOR.execute(
+ // Transition from fullscreen app to enter stage split in launcher with
+ // recents animation.
+ () -> ActivityManagerWrapper.getInstance().startRecentsActivity(
+ mOverviewComponentObserver.getOverviewIntent(),
+ SystemClock.uptimeMillis(), callbacks, null, null));
+ });
+ }
+
+ public void onDestroy() {
+ mOverviewComponentObserver.onDestroy();
+ }
+
+ private class SplitWithKeyboardShortcutRecentsAnimationListener implements
+ RecentsAnimationCallbacks.RecentsAnimationListener {
+
+ private final boolean mLeftOrTop;
+ private final Rect mTempRect = new Rect();
+
+ private SplitWithKeyboardShortcutRecentsAnimationListener(boolean leftOrTop) {
+ mLeftOrTop = leftOrTop;
+ }
+
+ @Override
+ public void onRecentsAnimationStart(RecentsAnimationController controller,
+ RecentsAnimationTargets targets) {
+ ActivityManager.RunningTaskInfo runningTaskInfo =
+ ActivityManagerWrapper.getInstance().getRunningTask();
+ mController.setInitialTaskSelect(runningTaskInfo,
+ mLeftOrTop ? STAGE_POSITION_TOP_OR_LEFT : STAGE_POSITION_BOTTOM_OR_RIGHT,
+ null /* itemInfo */,
+ mLeftOrTop ? LAUNCHER_KEYBOARD_SHORTCUT_SPLIT_LEFT_TOP
+ : LAUNCHER_KEYBOARD_SHORTCUT_SPLIT_RIGHT_BOTTOM);
+
+ RecentsView recentsView = mLauncher.getOverviewPanel();
+ recentsView.getPagedOrientationHandler().getInitialSplitPlaceholderBounds(
+ mSplitPlaceholderSize, mSplitPlaceholderInset, mLauncher.getDeviceProfile(),
+ mController.getActiveSplitStagePosition(), mTempRect);
+
+ PendingAnimation anim = new PendingAnimation(
+ SplitAnimationTimings.TABLET_HOME_TO_SPLIT.getDuration());
+ RectF startingTaskRect = new RectF();
+ final FloatingTaskView floatingTaskView = FloatingTaskView.getFloatingTaskView(
+ mLauncher, mLauncher.getDragLayer(),
+ controller.screenshotTask(runningTaskInfo.taskId).thumbnail,
+ null /* icon */, startingTaskRect);
+ RecentsModel.INSTANCE.get(mLauncher.getApplicationContext())
+ .getIconCache()
+ .updateIconInBackground(
+ Task.from(new Task.TaskKey(runningTaskInfo), runningTaskInfo,
+ false /* isLocked */),
+ (task) -> {
+ if (task.thumbnail != null) {
+ floatingTaskView.setIcon(task.thumbnail.thumbnail);
+ }
+ });
+ floatingTaskView.setAlpha(1);
+ floatingTaskView.addStagingAnimation(anim, startingTaskRect, mTempRect,
+ false /* fadeWithThumbnail */, true /* isStagedTask */);
+ mController.setFirstFloatingTaskView(floatingTaskView);
+
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ controller.finish(true /* toRecents */, null /* onFinishComplete */,
+ false /* sendUserLeaveHint */);
+ }
+ });
+ anim.buildAnim().start();
+ }
+ };
+}
diff --git a/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java b/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
index de527a7..ad54a70 100644
--- a/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
+++ b/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
@@ -35,7 +35,6 @@
import androidx.annotation.Nullable;
-import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.CellLayout;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Hotseat;
@@ -47,8 +46,10 @@
import com.android.launcher3.Workspace;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.SpringAnimationBuilder;
+import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.states.StateAnimationConfig;
+import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.launcher3.util.DynamicResource;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.plugins.ResourceProvider;
@@ -122,8 +123,7 @@
if (grid.isVerticalBarLayout()) {
for (int i = hotseatIcons.getChildCount() - 1; i >= 0; i--) {
View child = hotseatIcons.getChildAt(i);
- CellLayout.LayoutParams lp =
- ((CellLayout.LayoutParams) child.getLayoutParams());
+ CellLayoutLayoutParams lp = ((CellLayoutLayoutParams) child.getLayoutParams());
addStaggeredAnimationForView(child, lp.cellY + 1, totalRows, duration);
}
} else {
@@ -193,7 +193,7 @@
// Set up springs on workspace items.
for (int i = itemsContainer.getChildCount() - 1; i >= 0; i--) {
View child = itemsContainer.getChildAt(i);
- CellLayout.LayoutParams lp = ((CellLayout.LayoutParams) child.getLayoutParams());
+ CellLayoutLayoutParams lp = ((CellLayoutLayoutParams) child.getLayoutParams());
addStaggeredAnimationForView(child, lp.cellY + lp.cellVSpan, totalRows, duration);
}
@@ -295,11 +295,11 @@
}
private void addDepthAnimationForState(Launcher launcher, LauncherState state, long duration) {
- if (!(launcher instanceof BaseQuickstepLauncher)) {
+ if (!(launcher instanceof QuickstepLauncher)) {
return;
}
PendingAnimation builder = new PendingAnimation(duration);
- DepthController depthController = ((BaseQuickstepLauncher) launcher).getDepthController();
+ DepthController depthController = ((QuickstepLauncher) launcher).getDepthController();
depthController.setStateWithAnimation(state, new StateAnimationConfig(), builder);
mAnimators.play(builder.buildAnim());
}
diff --git a/quickstep/src/com/android/quickstep/util/SurfaceTransaction.java b/quickstep/src/com/android/quickstep/util/SurfaceTransaction.java
new file mode 100644
index 0000000..7ab285d
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/SurfaceTransaction.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.util;
+
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
+
+/**
+ * Helper class for building a {@link Transaction}.
+ */
+public class SurfaceTransaction {
+
+ private final Transaction mTransaction = new Transaction();
+ private final float[] mTmpValues = new float[9];
+
+ /**
+ * Creates a new builder for the provided surface
+ */
+ public SurfaceProperties forSurface(SurfaceControl surface) {
+ return surface.isValid() ? new SurfaceProperties(surface) : new MockProperties();
+ }
+
+ /**
+ * Returns the final transaction
+ */
+ public Transaction getTransaction() {
+ return mTransaction;
+ }
+
+ /**
+ * Utility class to update surface params in a transaction
+ */
+ public class SurfaceProperties {
+
+ private final SurfaceControl mSurface;
+
+ SurfaceProperties(SurfaceControl surface) {
+ mSurface = surface;
+ }
+
+ /**
+ * @param alpha The alpha value to apply to the surface.
+ * @return this Builder
+ */
+ public SurfaceProperties setAlpha(float alpha) {
+ mTransaction.setAlpha(mSurface, alpha);
+ return this;
+ }
+
+ /**
+ * @param matrix The matrix to apply to the surface.
+ * @return this Builder
+ */
+ public SurfaceProperties setMatrix(Matrix matrix) {
+ mTransaction.setMatrix(mSurface, matrix, mTmpValues);
+ return this;
+ }
+
+ /**
+ * @param windowCrop The window crop to apply to the surface.
+ * @return this Builder
+ */
+ public SurfaceProperties setWindowCrop(Rect windowCrop) {
+ mTransaction.setWindowCrop(mSurface, windowCrop);
+ return this;
+ }
+
+ /**
+ * @param relativeLayer The relative layer.
+ * @return this Builder
+ */
+ public SurfaceProperties setLayer(int relativeLayer) {
+ mTransaction.setLayer(mSurface, relativeLayer);
+ return this;
+ }
+
+ /**
+ * @param radius the Radius for rounded corners to apply to the surface.
+ * @return this Builder
+ */
+ public SurfaceProperties setCornerRadius(float radius) {
+ mTransaction.setCornerRadius(mSurface, radius);
+ return this;
+ }
+
+ /**
+ * @param radius the Radius for the shadows to apply to the surface.
+ * @return this Builder
+ */
+ public SurfaceProperties setShadowRadius(float radius) {
+ mTransaction.setShadowRadius(mSurface, radius);
+ return this;
+ }
+ }
+
+ /**
+ * Extension of {@link SurfaceProperties} which just stores all the values locally
+ */
+ public class MockProperties extends SurfaceProperties {
+
+ public float alpha = -1;
+ public Matrix matrix = null;
+ public Rect windowCrop = null;
+ public float cornerRadius = 0;
+ public float shadowRadius = 0;
+
+ protected MockProperties() {
+ super(null);
+ }
+
+ @Override
+ public SurfaceProperties setAlpha(float alpha) {
+ this.alpha = alpha;
+ return this;
+ }
+
+ @Override
+ public SurfaceProperties setMatrix(Matrix matrix) {
+ this.matrix = matrix;
+ return this;
+ }
+
+ @Override
+ public SurfaceProperties setWindowCrop(Rect windowCrop) {
+ this.windowCrop = windowCrop;
+ return this;
+ }
+
+ @Override
+ public SurfaceProperties setLayer(int relativeLayer) {
+ return this;
+ }
+
+ @Override
+ public SurfaceProperties setCornerRadius(float radius) {
+ this.cornerRadius = radius;
+ return this;
+ }
+
+ @Override
+ public SurfaceProperties setShadowRadius(float radius) {
+ this.shadowRadius = radius;
+ return this;
+ }
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java b/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java
index 1200208..95473dc 100644
--- a/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java
+++ b/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java
@@ -25,7 +25,6 @@
import android.view.ViewRootImpl;
import com.android.quickstep.RemoteAnimationTargets.ReleaseCheck;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
import java.util.function.Consumer;
@@ -70,18 +69,12 @@
* @param params The surface parameters to apply. DO NOT MODIFY the list after passing into
* this method to avoid synchronization issues.
*/
- public void scheduleApply(final SurfaceParams... params) {
+ public void scheduleApply(SurfaceTransaction params) {
View view = mTargetViewRootImpl.getView();
if (view == null) {
return;
}
- Transaction t = new Transaction();
- for (int i = params.length - 1; i >= 0; i--) {
- SurfaceParams surfaceParams = params[i];
- if (surfaceParams.surface.isValid()) {
- surfaceParams.applyTo(t);
- }
- }
+ Transaction t = params.getTransaction();
mLastSequenceNumber++;
final int toApplySeqNo = mLastSequenceNumber;
@@ -102,7 +95,7 @@
}
/**
- * Creates an instance of SyncRtSurfaceTransactionApplier, deferring until the target view is
+ * Creates an instance of SurfaceTransactionApplier, deferring until the target view is
* attached if necessary.
*/
public static void create(
diff --git a/quickstep/src/com/android/quickstep/util/TabletHomeToSplitTimings.java b/quickstep/src/com/android/quickstep/util/TabletHomeToSplitTimings.java
new file mode 100644
index 0000000..bf8612a
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/TabletHomeToSplitTimings.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.util;
+
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+
+import android.view.animation.Interpolator;
+
+/**
+ * Timings for the Home > OverviewSplitSelect animation on tablets.
+ */
+public class TabletHomeToSplitTimings
+ extends TabletOverviewToSplitTimings implements SplitAnimationTimings {
+ @Override
+ public Interpolator getStagedRectXInterpolator() { return LINEAR; }
+ @Override
+ public Interpolator getStagedRectScaleXInterpolator() { return LINEAR; }
+ @Override
+ public Interpolator getStagedRectScaleYInterpolator() { return LINEAR; }
+
+ public int getScrimFadeInStart() { return 0; }
+ public int getScrimFadeInEnd() { return 167; }
+
+ public float getScrimFadeInStartOffset() {
+ return (float) getScrimFadeInStart() / getDuration();
+ }
+ public float getScrimFadeInEndOffset() {
+ return (float) getScrimFadeInEnd() / getDuration();
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/util/TabletOverviewToSplitTimings.java b/quickstep/src/com/android/quickstep/util/TabletOverviewToSplitTimings.java
new file mode 100644
index 0000000..cbf46bf
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/TabletOverviewToSplitTimings.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.util;
+
+import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
+
+import android.view.animation.Interpolator;
+
+/**
+ * Timings for the Overview > OverviewSplitSelect animation on tablets.
+ */
+public class TabletOverviewToSplitTimings
+ extends OverviewToSplitTimings implements SplitAnimationTimings {
+ public int getPlaceholderFadeInStart() { return 0; }
+ public int getPlaceholderFadeInEnd() { return 133; }
+ public int getPlaceholderIconFadeInStart() { return 167; }
+ public int getPlaceholderIconFadeInEnd() { return 250; }
+ public int getStagedRectSlideStart() { return 0; }
+ public int getStagedRectSlideEnd() { return 417; }
+ public int getGridSlideStart() { return 67; }
+ public int getGridSlideStagger() { return 16; }
+ 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; }
+}
diff --git a/quickstep/src/com/android/quickstep/util/TabletSplitToConfirmTimings.java b/quickstep/src/com/android/quickstep/util/TabletSplitToConfirmTimings.java
new file mode 100644
index 0000000..3756b4a
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/TabletSplitToConfirmTimings.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.util;
+
+/**
+ * Timings for the OverviewSplitSelect > confirmed animation on tablets.
+ */
+public class TabletSplitToConfirmTimings
+ extends SplitToConfirmTimings implements SplitAnimationTimings {
+ public int getPlaceholderFadeInStart() { return 0; }
+ public int getPlaceholderFadeInEnd() { return 133; }
+ public int getPlaceholderIconFadeInStart() { return 167; }
+ public int getPlaceholderIconFadeInEnd() { return 250; }
+ public int getStagedRectSlideStart() { return 0; }
+ public int getStagedRectSlideEnd() { return 500; }
+
+ public int getDuration() { return TABLET_CONFIRM_DURATION; }
+}
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index d37300c..b476c12 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -15,7 +15,8 @@
*/
package com.android.quickstep.util;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+
import static com.android.launcher3.states.RotationHelper.deltaRotation;
import static com.android.launcher3.touch.PagedOrientationHandler.MATRIX_POST_TRANSLATE;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
@@ -25,7 +26,6 @@
import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS;
import static com.android.quickstep.util.RecentsOrientedState.postDisplayRotation;
import static com.android.quickstep.util.RecentsOrientedState.preDisplayRotation;
-import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_FULLSCREEN;
import android.animation.TimeInterpolator;
import android.content.Context;
@@ -35,6 +35,7 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.Log;
+import android.view.RemoteAnimationTarget;
import androidx.annotation.NonNull;
@@ -46,11 +47,10 @@
import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.BaseActivityInterface;
import com.android.quickstep.TaskAnimationManager;
-import com.android.quickstep.views.TaskThumbnailView.PreviewPositionHelper;
+import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties;
import com.android.quickstep.views.TaskView.FullscreenDrawParams;
import com.android.systemui.shared.recents.model.ThumbnailData;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder;
+import com.android.systemui.shared.recents.utilities.PreviewPositionHelper;
/**
* A utility class which emulates the layout behavior of TaskView and RecentsView
@@ -102,7 +102,7 @@
private boolean mLayoutValid = false;
private int mOrientationStateId;
private SplitBounds mSplitBounds;
- private boolean mDrawsBelowRecents;
+ private Boolean mDrawsBelowRecents = null;
private boolean mIsGridTask;
private int mTaskRectTranslationX;
private int mTaskRectTranslationY;
@@ -171,8 +171,11 @@
/**
* Sets the targets which the simulator will control
*/
- public void setPreview(RemoteAnimationTargetCompat runningTarget) {
- setPreviewBounds(runningTarget.startScreenSpaceBounds, runningTarget.contentInsets);
+ public void setPreview(RemoteAnimationTarget runningTarget) {
+ setPreviewBounds(
+ runningTarget.startBounds == null
+ ? runningTarget.screenSpaceBounds : runningTarget.startBounds,
+ runningTarget.contentInsets);
}
/**
@@ -181,7 +184,7 @@
*
* @param splitInfo set to {@code null} when not in staged split mode
*/
- public void setPreview(RemoteAnimationTargetCompat runningTarget, SplitBounds splitInfo) {
+ public void setPreview(RemoteAnimationTarget runningTarget, SplitBounds splitInfo) {
setPreview(runningTarget);
mSplitBounds = splitInfo;
if (mSplitBounds == null) {
@@ -191,6 +194,7 @@
mStagePosition = mThumbnailPosition.equals(splitInfo.leftTopBounds) ?
STAGE_POSITION_TOP_OR_LEFT :
STAGE_POSITION_BOTTOM_OR_RIGHT;
+ mPositionHelper.setSplitBounds(convertSplitBounds(mSplitBounds), mStagePosition);
}
/**
@@ -317,9 +321,9 @@
// mIsRecentsRtl is the inverse of TaskView RTL.
boolean isRtlEnabled = !mIsRecentsRtl;
mPositionHelper.updateThumbnailMatrix(
- mThumbnailPosition, mThumbnailData,
- mTaskRect.width(), mTaskRect.height(),
- mDp, mOrientationState.getRecentsActivityRotation(), isRtlEnabled);
+ mThumbnailPosition, mThumbnailData, mTaskRect.width(), mTaskRect.height(),
+ mDp.widthPx, mDp.heightPx, mDp.taskbarSize, mDp.isTablet,
+ mOrientationState.getRecentsActivityRotation(), isRtlEnabled);
mPositionHelper.getMatrix().invert(mInversePositionMatrix);
if (DEBUG) {
Log.d(TAG, " taskRect: " + mTaskRect);
@@ -386,18 +390,19 @@
@Override
public void onBuildTargetParams(
- Builder builder, RemoteAnimationTargetCompat app, TransformParams params) {
- builder.withMatrix(mMatrix)
- .withWindowCrop(mTmpCropRect)
- .withCornerRadius(getCurrentCornerRadius());
+ SurfaceProperties builder, RemoteAnimationTarget app, TransformParams params) {
+ builder.setMatrix(mMatrix)
+ .setWindowCrop(mTmpCropRect)
+ .setCornerRadius(getCurrentCornerRadius());
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ // If mDrawsBelowRecents is unset, no reordering will be enforced.
+ if (mDrawsBelowRecents != null) {
// In legacy transitions, the animation leashes remain in same hierarchy in the
// TaskDisplayArea, so we don't want to bump the layer too high otherwise it will
// conflict with layers that WM core positions (ie. the input consumers). For shell
// transitions, the animation leashes are reparented to an animation container so we
// can bump layers as needed.
- builder.withLayer(mDrawsBelowRecents
+ builder.setLayer(mDrawsBelowRecents
? Integer.MIN_VALUE + 1
: ENABLE_SHELL_TRANSITIONS ? Integer.MAX_VALUE : 0);
}
@@ -417,4 +422,15 @@
return Math.max(Math.abs(mTempPoint[0]), Math.abs(mTempPoint[1]));
}
+ /**
+ * TODO(b/254378592): Remove this after consolidation of classes
+ */
+ public static com.android.wm.shell.util.SplitBounds convertSplitBounds(SplitBounds bounds) {
+ return new com.android.wm.shell.util.SplitBounds(
+ bounds.leftTopBounds,
+ bounds.rightBottomBounds,
+ bounds.leftTopTaskId,
+ bounds.rightBottomTaskId
+ );
+ }
}
diff --git a/quickstep/src/com/android/quickstep/util/TransformParams.java b/quickstep/src/com/android/quickstep/util/TransformParams.java
index a7f25d4..aa9a45b 100644
--- a/quickstep/src/com/android/quickstep/util/TransformParams.java
+++ b/quickstep/src/com/android/quickstep/util/TransformParams.java
@@ -15,16 +15,18 @@
*/
package com.android.quickstep.util;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+
import android.util.FloatProperty;
+import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.Interpolators;
import com.android.quickstep.RemoteAnimationTargets;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
-import com.android.systemui.shared.system.TransactionCompat;
+import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties;
public class TransformParams {
@@ -113,8 +115,7 @@
* Sets the SyncRtSurfaceTransactionApplierCompat that will apply the SurfaceParams that
* are computed based on these TransformParams.
*/
- public TransformParams setSyncTransactionApplier(
- SurfaceTransactionApplier applier) {
+ public TransformParams setSyncTransactionApplier(SurfaceTransactionApplier applier) {
mSyncTransactionApplier = applier;
return this;
}
@@ -137,28 +138,26 @@
return this;
}
- public SurfaceParams[] createSurfaceParams(BuilderProxy proxy) {
+ public SurfaceTransaction createSurfaceParams(BuilderProxy proxy) {
RemoteAnimationTargets targets = mTargetSet;
- final int appLength = targets.unfilteredApps.length;
- final int wallpaperLength = targets.wallpapers != null ? targets.wallpapers.length : 0;
- SurfaceParams[] surfaceParams = new SurfaceParams[appLength + wallpaperLength];
+ SurfaceTransaction transaction = new SurfaceTransaction();
mRecentsSurface = getRecentsSurface(targets);
- for (int i = 0; i < appLength; i++) {
- RemoteAnimationTargetCompat app = targets.unfilteredApps[i];
- SurfaceParams.Builder builder = new SurfaceParams.Builder(app.leash);
+ for (int i = 0; i < targets.unfilteredApps.length; i++) {
+ RemoteAnimationTarget app = targets.unfilteredApps[i];
+ SurfaceProperties builder = transaction.forSurface(app.leash);
if (app.mode == targets.targetMode) {
- if (app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
+ int activityType = app.windowConfiguration.getActivityType();
+ if (activityType == ACTIVITY_TYPE_HOME) {
mHomeBuilderProxy.onBuildTargetParams(builder, app, this);
} else {
// Fade out Assistant overlay.
- if (app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_ASSISTANT
- && app.isNotInRecents) {
+ if (activityType == ACTIVITY_TYPE_ASSISTANT && app.isNotInRecents) {
float progress = Utilities.boundToRange(getProgress(), 0, 1);
- builder.withAlpha(1 - Interpolators.DEACCEL_2_5.getInterpolation(progress));
+ builder.setAlpha(1 - Interpolators.DEACCEL_2_5.getInterpolation(progress));
} else {
- builder.withAlpha(getTargetAlpha());
+ builder.setAlpha(getTargetAlpha());
}
proxy.onBuildTargetParams(builder, app, this);
@@ -166,22 +165,22 @@
} else {
mBaseBuilderProxy.onBuildTargetParams(builder, app, this);
}
- surfaceParams[i] = builder.build();
}
+
// always put wallpaper layer to bottom.
+ final int wallpaperLength = targets.wallpapers != null ? targets.wallpapers.length : 0;
for (int i = 0; i < wallpaperLength; i++) {
- RemoteAnimationTargetCompat wallpaper = targets.wallpapers[i];
- surfaceParams[appLength + i] = new SurfaceParams.Builder(wallpaper.leash)
- .withLayer(Integer.MIN_VALUE).build();
+ RemoteAnimationTarget wallpaper = targets.wallpapers[i];
+ transaction.forSurface(wallpaper.leash).setLayer(Integer.MIN_VALUE);
}
- return surfaceParams;
+ return transaction;
}
private static SurfaceControl getRecentsSurface(RemoteAnimationTargets targets) {
for (int i = 0; i < targets.unfilteredApps.length; i++) {
- RemoteAnimationTargetCompat app = targets.unfilteredApps[i];
+ RemoteAnimationTarget app = targets.unfilteredApps[i];
if (app.mode == targets.targetMode) {
- if (app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_RECENTS) {
+ if (app.windowConfiguration.getActivityType() == ACTIVITY_TYPE_RECENTS) {
return app.leash;
}
} else {
@@ -213,15 +212,11 @@
return mTargetSet;
}
- public void applySurfaceParams(SurfaceParams... params) {
+ public void applySurfaceParams(SurfaceTransaction builder) {
if (mSyncTransactionApplier != null) {
- mSyncTransactionApplier.scheduleApply(params);
+ mSyncTransactionApplier.scheduleApply(builder);
} else {
- TransactionCompat t = new TransactionCompat();
- for (SurfaceParams param : params) {
- SyncRtSurfaceTransactionApplierCompat.applyParams(t, param);
- }
- t.apply();
+ builder.getTransaction().apply();
}
}
@@ -229,9 +224,9 @@
public interface BuilderProxy {
BuilderProxy NO_OP = (builder, app, params) -> { };
- BuilderProxy ALWAYS_VISIBLE = (builder, app, params) ->builder.withAlpha(1);
+ BuilderProxy ALWAYS_VISIBLE = (builder, app, params) -> builder.setAlpha(1);
- void onBuildTargetParams(SurfaceParams.Builder builder,
- RemoteAnimationTargetCompat app, TransformParams params);
+ void onBuildTargetParams(SurfaceProperties builder,
+ RemoteAnimationTarget app, TransformParams params);
}
}
diff --git a/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterHotseatAnimator.java b/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterHotseatAnimator.java
index dc97dd6..01a997a 100644
--- a/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterHotseatAnimator.java
+++ b/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterHotseatAnimator.java
@@ -21,6 +21,7 @@
import com.android.launcher3.Hotseat;
import com.android.launcher3.Launcher;
+import com.android.systemui.unfold.updates.RotationChangeProvider;
/**
* Animation that moves hotseat icons from center to the sides (final position)
@@ -29,8 +30,9 @@
private final Launcher mLauncher;
- public UnfoldMoveFromCenterHotseatAnimator(Launcher launcher, WindowManager windowManager) {
- super(windowManager);
+ public UnfoldMoveFromCenterHotseatAnimator(Launcher launcher, WindowManager windowManager,
+ RotationChangeProvider rotationChangeProvider) {
+ super(windowManager, rotationChangeProvider);
mLauncher = launcher;
}
diff --git a/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterWorkspaceAnimator.java b/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterWorkspaceAnimator.java
index 354d157..95a4b8f 100644
--- a/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterWorkspaceAnimator.java
+++ b/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterWorkspaceAnimator.java
@@ -22,6 +22,7 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.ShortcutAndWidgetContainer;
import com.android.launcher3.Workspace;
+import com.android.systemui.unfold.updates.RotationChangeProvider;
/**
* Animation that moves launcher icons and widgets from center to the sides (final position)
@@ -30,8 +31,9 @@
private final Launcher mLauncher;
- public UnfoldMoveFromCenterWorkspaceAnimator(Launcher launcher, WindowManager windowManager) {
- super(windowManager);
+ public UnfoldMoveFromCenterWorkspaceAnimator(Launcher launcher, WindowManager windowManager,
+ RotationChangeProvider rotationChangeProvider) {
+ super(windowManager, rotationChangeProvider);
mLauncher = launcher;
}
diff --git a/quickstep/src/com/android/quickstep/util/WorkspaceRevealAnim.java b/quickstep/src/com/android/quickstep/util/WorkspaceRevealAnim.java
index 5eb543e..34fa7f1 100644
--- a/quickstep/src/com/android/quickstep/util/WorkspaceRevealAnim.java
+++ b/quickstep/src/com/android/quickstep/util/WorkspaceRevealAnim.java
@@ -32,7 +32,6 @@
import android.util.FloatProperty;
import android.view.View;
-import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.Hotseat;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
@@ -41,6 +40,7 @@
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.states.StateAnimationConfig;
+import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.launcher3.util.DynamicResource;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.plugins.ResourceProvider;
@@ -84,9 +84,9 @@
}
// Add depth controller animation.
- if (launcher instanceof BaseQuickstepLauncher) {
+ if (launcher instanceof QuickstepLauncher) {
PendingAnimation depthBuilder = new PendingAnimation(DURATION_MS);
- DepthController depth = ((BaseQuickstepLauncher) launcher).getDepthController();
+ DepthController depth = ((QuickstepLauncher) launcher).getDepthController();
depth.setStateWithAnimation(NORMAL, new StateAnimationConfig(), depthBuilder);
mAnimators.play(depthBuilder.buildAnim());
}
diff --git a/quickstep/src/com/android/quickstep/views/ClearAllButton.java b/quickstep/src/com/android/quickstep/views/ClearAllButton.java
index 50be5ea..d098ffc 100644
--- a/quickstep/src/com/android/quickstep/views/ClearAllButton.java
+++ b/quickstep/src/com/android/quickstep/views/ClearAllButton.java
@@ -99,6 +99,10 @@
return false;
}
+ public float getScrollAlpha() {
+ return mScrollAlpha;
+ }
+
public void setContentAlpha(float alpha) {
if (mContentAlpha != alpha) {
mContentAlpha = alpha;
diff --git a/quickstep/src/com/android/quickstep/views/DesktopTaskView.java b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
new file mode 100644
index 0000000..8c43fd1
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
@@ -0,0 +1,470 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.views;
+
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
+
+import android.content.Context;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.drawable.ShapeDrawable;
+import android.graphics.drawable.shapes.RoundRectShape;
+import android.os.SystemProperties;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.MotionEvent;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.touch.PagedOrientationHandler;
+import com.android.launcher3.util.RunnableList;
+import com.android.quickstep.RecentsModel;
+import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.TaskThumbnailCache;
+import com.android.quickstep.util.CancellableTask;
+import com.android.quickstep.util.RecentsOrientedState;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.recents.model.ThumbnailData;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * TaskView that contains all tasks that are part of the desktop.
+ */
+// TODO(b/249371338): TaskView needs to be refactored to have better support for N tasks.
+public class DesktopTaskView extends TaskView {
+
+ /** Flag to indicate whether desktop mode is available on the device */
+ public static final boolean DESKTOP_MODE_SUPPORTED = SystemProperties.getBoolean(
+ "persist.wm.debug.desktop_mode", false);
+
+ private static final String TAG = DesktopTaskView.class.getSimpleName();
+
+ private static final boolean DEBUG = true;
+
+ private List<Task> mTasks;
+
+ private final ArrayList<TaskThumbnailView> mSnapshotViews = new ArrayList<>();
+
+ /** Maps {@code taskIds} to corresponding {@link TaskThumbnailView}s */
+ private final SparseArray<TaskThumbnailView> mSnapshotViewMap = new SparseArray<>();
+
+ private final ArrayList<CancellableTask<?>> mPendingThumbnailRequests = new ArrayList<>();
+
+ public DesktopTaskView(Context context) {
+ this(context, null);
+ }
+
+ public DesktopTaskView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public DesktopTaskView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ float[] outerRadii = new float[8];
+ Arrays.fill(outerRadii, getTaskCornerRadius());
+ RoundRectShape shape = new RoundRectShape(outerRadii, null, null);
+ ShapeDrawable background = new ShapeDrawable(shape);
+ background.setTint(getResources().getColor(android.R.color.system_neutral2_300));
+ // TODO(b/244348395): this should be wallpaper
+ setBackground(background);
+
+ mSnapshotViews.add(mSnapshotView);
+ }
+
+ @Override
+ public void bind(Task task, RecentsOrientedState orientedState) {
+ bind(Collections.singletonList(task), orientedState);
+ }
+
+ /**
+ * Updates this desktop task to the gives task list defined in {@code tasks}
+ */
+ public void bind(List<Task> tasks, RecentsOrientedState orientedState) {
+ if (DEBUG) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("bind tasks=").append(tasks.size()).append("\n");
+ for (Task task : tasks) {
+ sb.append(" key=").append(task.key).append("\n");
+ }
+ Log.d(TAG, sb.toString());
+ }
+ if (tasks.isEmpty()) {
+ return;
+ }
+ cancelPendingLoadTasks();
+
+ mTasks = tasks;
+ mSnapshotViewMap.clear();
+
+ // Ensure there are equal number of snapshot views and tasks.
+ // More tasks than views, add views. More views than tasks, remove views.
+ // TODO(b/251586230): use a ViewPool for creating TaskThumbnailViews
+ if (mSnapshotViews.size() > mTasks.size()) {
+ int diff = mSnapshotViews.size() - mTasks.size();
+ for (int i = 0; i < diff; i++) {
+ TaskThumbnailView snapshotView = mSnapshotViews.remove(0);
+ removeView(snapshotView);
+ }
+ } else if (mSnapshotViews.size() < mTasks.size()) {
+ int diff = mTasks.size() - mSnapshotViews.size();
+ for (int i = 0; i < diff; i++) {
+ TaskThumbnailView snapshotView = new TaskThumbnailView(getContext());
+ mSnapshotViews.add(snapshotView);
+ addView(snapshotView, new LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
+ }
+ }
+
+ for (int i = 0; i < mTasks.size(); i++) {
+ Task task = mTasks.get(i);
+ TaskThumbnailView snapshotView = mSnapshotViews.get(i);
+ snapshotView.bind(task);
+ mSnapshotViewMap.put(task.key.id, snapshotView);
+ }
+
+ updateTaskIdContainer();
+ updateTaskIdAttributeContainer();
+
+ setOrientationState(orientedState);
+ }
+
+ private void updateTaskIdContainer() {
+ // TODO(b/249371338): TaskView expects the array to have at least 2 elements.
+ // At least 2 elements in the array
+ mTaskIdContainer = new int[Math.max(mTasks.size(), 2)];
+ for (int i = 0; i < mTasks.size(); i++) {
+ mTaskIdContainer[i] = mTasks.get(i).key.id;
+ }
+ }
+
+ private void updateTaskIdAttributeContainer() {
+ // TODO(b/249371338): TaskView expects the array to have at least 2 elements.
+ // At least 2 elements in the array
+ mTaskIdAttributeContainer = new TaskIdAttributeContainer[Math.max(mTasks.size(), 2)];
+ for (int i = 0; i < mTasks.size(); i++) {
+ Task task = mTasks.get(i);
+ TaskThumbnailView thumbnailView = mSnapshotViewMap.get(task.key.id);
+ mTaskIdAttributeContainer[i] = createAttributeContainer(task, thumbnailView);
+ }
+ }
+
+ private TaskIdAttributeContainer createAttributeContainer(Task task,
+ TaskThumbnailView thumbnailView) {
+ return new TaskIdAttributeContainer(task, thumbnailView, null, STAGE_POSITION_UNDEFINED);
+ }
+
+ @Nullable
+ @Override
+ public Task getTask() {
+ // TODO(b/249371338): returning first task. This won't work well with multiple tasks.
+ return mTasks.size() > 0 ? mTasks.get(0) : null;
+ }
+
+ @Override
+ public TaskThumbnailView getThumbnail() {
+ // TODO(b/249371338): returning single thumbnail. This won't work well with multiple tasks.
+ Task task = getTask();
+ if (task != null) {
+ return mSnapshotViewMap.get(task.key.id);
+ }
+ return null;
+ }
+
+ @Override
+ public boolean containsTaskId(int taskId) {
+ // Thumbnail map contains taskId -> thumbnail map. Use the keys for contains
+ return mSnapshotViewMap.contains(taskId);
+ }
+
+ @Override
+ public void onTaskListVisibilityChanged(boolean visible, int changes) {
+ cancelPendingLoadTasks();
+ if (visible) {
+ RecentsModel model = RecentsModel.INSTANCE.get(getContext());
+ TaskThumbnailCache thumbnailCache = model.getThumbnailCache();
+
+ if (needsUpdate(changes, FLAG_UPDATE_THUMBNAIL)) {
+ for (Task task : mTasks) {
+ CancellableTask<?> thumbLoadRequest =
+ thumbnailCache.updateThumbnailInBackground(task, thumbnailData -> {
+ TaskThumbnailView thumbnailView = mSnapshotViewMap.get(task.key.id);
+ if (thumbnailView != null) {
+ thumbnailView.setThumbnail(task, thumbnailData);
+ }
+ });
+ if (thumbLoadRequest != null) {
+ mPendingThumbnailRequests.add(thumbLoadRequest);
+ }
+ }
+ }
+ } else {
+ if (needsUpdate(changes, FLAG_UPDATE_THUMBNAIL)) {
+ for (Task task : mTasks) {
+ TaskThumbnailView thumbnailView = mSnapshotViewMap.get(task.key.id);
+ if (thumbnailView != null) {
+ thumbnailView.setThumbnail(null, null);
+ }
+ // Reset the task thumbnail ref
+ task.thumbnail = null;
+ }
+ }
+ }
+ }
+
+ @Override
+ public void setOrientationState(RecentsOrientedState orientationState) {
+ // TODO(b/249371338): this copies logic from TaskView
+ PagedOrientationHandler orientationHandler = orientationState.getOrientationHandler();
+ boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
+ DeviceProfile deviceProfile = mActivity.getDeviceProfile();
+
+ LayoutParams iconParams = (LayoutParams) mIconView.getLayoutParams();
+
+ int thumbnailTopMargin = deviceProfile.overviewTaskThumbnailTopMarginPx;
+ int taskIconHeight = deviceProfile.overviewTaskIconSizePx;
+ int taskMargin = deviceProfile.overviewTaskMarginPx;
+
+ orientationHandler.setTaskIconParams(iconParams, taskMargin, taskIconHeight,
+ thumbnailTopMargin, isRtl);
+
+ LayoutParams snapshotParams = (LayoutParams) mSnapshotView.getLayoutParams();
+ snapshotParams.topMargin = thumbnailTopMargin;
+
+ for (int i = 0; i < mSnapshotViewMap.size(); i++) {
+ TaskThumbnailView thumbnailView = mSnapshotViewMap.valueAt(i);
+ thumbnailView.setLayoutParams(snapshotParams);
+ }
+ }
+
+ @Override
+ protected void cancelPendingLoadTasks() {
+ for (CancellableTask<?> cancellableTask : mPendingThumbnailRequests) {
+ cancellableTask.cancel();
+ }
+ mPendingThumbnailRequests.clear();
+ }
+
+ @Override
+ public boolean offerTouchToChildren(MotionEvent event) {
+ return false;
+ }
+
+ @Override
+ protected boolean showTaskMenuWithContainer(IconView iconView) {
+ return false;
+ }
+
+ @Override
+ public RunnableList launchTasks() {
+ SystemUiProxy.INSTANCE.get(getContext()).showDesktopApps();
+ getRecentsView().startHome();
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public RunnableList launchTaskAnimated() {
+ return launchTasks();
+ }
+
+ @Override
+ public void launchTask(@NonNull Consumer<Boolean> callback, boolean freezeTaskList) {
+ launchTasks();
+ callback.accept(true);
+ }
+
+ @Override
+ void refreshThumbnails(@Nullable HashMap<Integer, ThumbnailData> thumbnailDatas) {
+ // Sets new thumbnails based on the incoming data and refreshes the rest.
+ // Create a copy of the thumbnail map, so we can track thumbnails that need refreshing.
+ SparseArray<TaskThumbnailView> thumbnailsToRefresh = mSnapshotViewMap.clone();
+ if (thumbnailDatas != null) {
+ for (Task task : mTasks) {
+ int key = task.key.id;
+ TaskThumbnailView thumbnailView = thumbnailsToRefresh.get(key);
+ ThumbnailData thumbnailData = thumbnailDatas.get(key);
+ if (thumbnailView != null && thumbnailData != null) {
+ thumbnailView.setThumbnail(task, thumbnailData);
+ // Remove this thumbnail from the list that should be refreshed.
+ thumbnailsToRefresh.remove(key);
+ }
+ }
+ }
+
+ // Refresh the rest that were not updated.
+ for (int i = 0; i < thumbnailsToRefresh.size(); i++) {
+ thumbnailsToRefresh.valueAt(i).refresh();
+ }
+ }
+
+ @Override
+ public TaskThumbnailView[] getThumbnails() {
+ TaskThumbnailView[] thumbnails = new TaskThumbnailView[mSnapshotViewMap.size()];
+ for (int i = 0; i < thumbnails.length; i++) {
+ thumbnails[i] = mSnapshotViewMap.valueAt(i);
+ }
+ return thumbnails;
+ }
+
+ @Override
+ public void onRecycle() {
+ resetPersistentViewTransforms();
+ // Clear any references to the thumbnail (it will be re-read either from the cache or the
+ // system on next bind)
+ for (Task task : mTasks) {
+ TaskThumbnailView thumbnailView = mSnapshotViewMap.get(task.key.id);
+ if (thumbnailView != null) {
+ thumbnailView.setThumbnail(task, null);
+ }
+ }
+ setOverlayEnabled(false);
+ onTaskListVisibilityChanged(false);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ int containerWidth = MeasureSpec.getSize(widthMeasureSpec);
+ int containerHeight = MeasureSpec.getSize(heightMeasureSpec);
+
+ setMeasuredDimension(containerWidth, containerHeight);
+
+ int thumbnails = mSnapshotViewMap.size();
+ if (thumbnails == 0) {
+ return;
+ }
+
+ int windowWidth = mActivity.getDeviceProfile().widthPx;
+ int windowHeight = mActivity.getDeviceProfile().heightPx;
+
+ float scaleWidth = containerWidth / (float) windowWidth;
+ float scaleHeight = containerHeight / (float) windowHeight;
+
+ if (DEBUG) {
+ Log.d(TAG,
+ "onMeasure: container=[" + containerWidth + "," + containerHeight + "] window=["
+ + windowWidth + "," + windowHeight + "] scale=[" + scaleWidth + ","
+ + scaleHeight + "]");
+ }
+
+ // Desktop tile is a shrunk down version of launcher and freeform task thumbnails.
+ for (int i = 0; i < mTasks.size(); i++) {
+ Task task = mTasks.get(i);
+ Rect taskSize = task.appBounds;
+ if (taskSize == null) {
+ // Default to quarter of the desktop if we did not get app bounds.
+ taskSize = new Rect(0, 0, windowWidth / 4, windowHeight / 4);
+ }
+
+ int thumbWidth = (int) (taskSize.width() * scaleWidth);
+ int thumbHeight = (int) (taskSize.height() * scaleHeight);
+
+ TaskThumbnailView thumbnailView = mSnapshotViewMap.get(task.key.id);
+ if (thumbnailView != null) {
+ thumbnailView.measure(MeasureSpec.makeMeasureSpec(thumbWidth, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(thumbHeight, MeasureSpec.EXACTLY));
+
+ // Position the task to the same position as it would be on the desktop
+ Point positionInParent = task.positionInParent;
+ if (positionInParent == null) {
+ positionInParent = new Point(0, 0);
+ }
+ int taskX = (int) (positionInParent.x * scaleWidth);
+ int taskY = (int) (positionInParent.y * scaleHeight);
+ thumbnailView.setX(taskX);
+ thumbnailView.setY(taskY);
+
+ if (DEBUG) {
+ Log.d(TAG, "onMeasure: task=" + task.key + " thumb=[" + thumbWidth + ","
+ + thumbHeight + "]" + " pos=[" + taskX + "," + taskY + "]");
+ }
+ }
+ }
+ }
+
+ @Override
+ public void setOverlayEnabled(boolean overlayEnabled) {
+ // Intentional no-op to prevent setting smart actions overlay on thumbnails
+ }
+
+ @Override
+ public void setFullscreenProgress(float progress) {
+ // TODO(b/249371338): this copies parent implementation and makes it work for N thumbs
+ progress = Utilities.boundToRange(progress, 0, 1);
+ mFullscreenProgress = progress;
+ for (int i = 0; i < mSnapshotViewMap.size(); i++) {
+ TaskThumbnailView thumbnailView = mSnapshotViewMap.valueAt(i);
+ thumbnailView.getTaskOverlay().setFullscreenProgress(progress);
+ updateSnapshotRadius();
+ }
+ }
+
+ @Override
+ protected void updateSnapshotRadius() {
+ for (int i = 0; i < mSnapshotViewMap.size(); i++) {
+ mSnapshotViewMap.valueAt(i).setFullscreenParams(mCurrentFullscreenParams);
+ }
+ }
+
+ @Override
+ protected void setIconsAndBannersTransitionProgress(float progress, boolean invert) {
+ // no-op
+ }
+
+ @Override
+ public void setColorTint(float amount, int tintColor) {
+ for (int i = 0; i < mSnapshotViewMap.size(); i++) {
+ mSnapshotViewMap.valueAt(i).setDimAlpha(amount);
+ }
+ }
+
+ @Override
+ protected void applyThumbnailSplashAlpha() {
+ for (int i = 0; i < mSnapshotViewMap.size(); i++) {
+ mSnapshotViewMap.valueAt(i).setSplashAlpha(mTaskThumbnailSplashAlpha);
+ }
+ }
+
+ @Override
+ void setThumbnailVisibility(int visibility) {
+ for (int i = 0; i < mSnapshotViewMap.size(); i++) {
+ mSnapshotViewMap.valueAt(i).setVisibility(visibility);
+ }
+ }
+
+ @Override
+ protected boolean confirmSecondSplitSelectApp() {
+ // Desktop tile can't be in split screen
+ return false;
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
index 76552a3..96504af 100644
--- a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
+++ b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
@@ -385,4 +385,12 @@
mBanner.setLayerType(View.LAYER_TYPE_HARDWARE, layerPaint);
mBanner.setLayerPaint(layerPaint);
}
+
+ void setBannerVisibility(int visibility) {
+ if (mBanner == null) {
+ return;
+ }
+
+ mBanner.setVisibility(visibility);
+ }
}
diff --git a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
index d93f015..1d421b2 100644
--- a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
@@ -1,8 +1,6 @@
package com.android.quickstep.views;
import static com.android.launcher3.AbstractFloatingView.TYPE_TASK_MENU;
-import static com.android.launcher3.anim.Interpolators.ACCEL;
-import static com.android.launcher3.anim.Interpolators.FASTER_OUT_SLOWER_IN;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.Interpolators.clampToProgress;
@@ -13,8 +11,10 @@
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
+import android.util.FloatProperty;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
@@ -24,7 +24,6 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BaseActivity;
import com.android.launcher3.InsettableFrameLayout;
-import com.android.launcher3.LauncherAnimUtils;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.PendingAnimation;
@@ -32,7 +31,9 @@
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.launcher3.views.BaseDragLayer;
+import com.android.quickstep.util.AnimUtils;
import com.android.quickstep.util.MultiValueUpdateListener;
+import com.android.quickstep.util.SplitAnimationTimings;
import com.android.quickstep.util.TaskCornerRadius;
import com.android.systemui.shared.system.QuickStepContract;
@@ -42,7 +43,8 @@
* which will have the thumbnail from the provided existing TaskView overlaying the taskview itself.
*
* Can then animate the taskview using
- * {@link #addAnimation(PendingAnimation, RectF, Rect, boolean, boolean)}
+ * {@link #addStagingAnimation(PendingAnimation, RectF, Rect, boolean, boolean)} or
+ * {@link #addConfirmAnimation(PendingAnimation, RectF, Rect, boolean, boolean)}
* giving a starting and ending bounds. Currently this is set to use the split placeholder view,
* but it could be generified.
*
@@ -50,6 +52,30 @@
*/
public class FloatingTaskView extends FrameLayout {
+ public static final FloatProperty<FloatingTaskView> PRIMARY_TRANSLATE_OFFSCREEN =
+ new FloatProperty<FloatingTaskView>("floatingTaskPrimaryTranslateOffscreen") {
+ @Override
+ public void setValue(FloatingTaskView view, float translation) {
+ ((RecentsView) view.mActivity.getOverviewPanel()).getPagedOrientationHandler()
+ .setFloatingTaskPrimaryTranslation(
+ view,
+ translation,
+ view.mActivity.getDeviceProfile()
+ );
+ }
+
+ @Override
+ public Float get(FloatingTaskView view) {
+ return ((RecentsView) view.mActivity.getOverviewPanel())
+ .getPagedOrientationHandler()
+ .getFloatingTaskPrimaryTranslation(
+ view,
+ view.mActivity.getDeviceProfile()
+ );
+ }
+ };
+
+ private int mSplitHolderSize;
private FloatingTaskThumbnailView mThumbnailView;
private SplitPlaceholderView mSplitPlaceholderView;
private RectF mStartingPosition;
@@ -73,6 +99,9 @@
mActivity = BaseActivity.fromContext(context);
mIsRtl = Utilities.isRtl(getResources());
mFullscreenParams = new FullscreenDrawParams(context);
+
+ mSplitHolderSize = context.getResources().getDimensionPixelSize(
+ R.dimen.split_placeholder_icon_size);
}
@Override
@@ -101,9 +130,8 @@
RecentsView recentsView = launcher.getOverviewPanel();
mOrientationHandler = recentsView.getPagedOrientationHandler();
- mStagePosition = recentsView.getSplitPlaceholder().getActiveSplitStagePosition();
- mSplitPlaceholderView.setIcon(icon,
- mContext.getResources().getDimensionPixelSize(R.dimen.split_placeholder_icon_size));
+ mStagePosition = recentsView.getSplitSelectController().getActiveSplitStagePosition();
+ mSplitPlaceholderView.setIcon(icon, mSplitHolderSize);
mSplitPlaceholderView.getIconView().setRotation(mOrientationHandler.getDegreesRotated());
}
@@ -169,6 +197,10 @@
mSplitPlaceholderView.getIconView().setRotation(mOrientationHandler.getDegreesRotated());
}
+ public void setIcon(Bitmap icon) {
+ mSplitPlaceholderView.setIcon(new BitmapDrawable(icon), mSplitHolderSize);
+ }
+
protected void initPosition(RectF pos, InsettableFrameLayout.LayoutParams lp) {
mStartingPosition.set(pos);
lp.ignoreInsets = true;
@@ -186,8 +218,50 @@
layout(left, lp.topMargin, left + lp.width, lp.topMargin + lp.height);
}
- public void addAnimation(PendingAnimation animation, RectF startingBounds, Rect endBounds,
- boolean fadeWithThumbnail, boolean isStagedTask) {
+ /**
+ * Animates a FloatingTaskThumbnailView and its overlapping SplitPlaceholderView when a split
+ * is staged.
+ */
+ public void addStagingAnimation(PendingAnimation animation, RectF startingBounds,
+ Rect endBounds, boolean fadeWithThumbnail, boolean isStagedTask) {
+ boolean isTablet = mActivity.getDeviceProfile().isTablet;
+ boolean splittingFromOverview = fadeWithThumbnail;
+ SplitAnimationTimings timings;
+
+ if (isTablet && splittingFromOverview) {
+ timings = SplitAnimationTimings.TABLET_OVERVIEW_TO_SPLIT;
+ } else if (!isTablet && splittingFromOverview) {
+ timings = SplitAnimationTimings.PHONE_OVERVIEW_TO_SPLIT;
+ } else {
+ // Splitting from Home is currently only available on tablets
+ timings = SplitAnimationTimings.TABLET_HOME_TO_SPLIT;
+ }
+
+ addAnimation(animation, startingBounds, endBounds, fadeWithThumbnail, isStagedTask,
+ timings);
+ }
+
+ /**
+ * Animates the FloatingTaskThumbnailView and SplitPlaceholderView for the two thumbnails
+ * when a split is confirmed.
+ */
+ public void addConfirmAnimation(PendingAnimation animation, RectF startingBounds,
+ Rect endBounds, boolean fadeWithThumbnail, boolean isStagedTask) {
+ SplitAnimationTimings timings =
+ AnimUtils.getDeviceSplitToConfirmTimings(mActivity.getDeviceProfile().isTablet);
+
+ addAnimation(animation, startingBounds, endBounds, fadeWithThumbnail, isStagedTask,
+ timings);
+ }
+
+ /**
+ * Sets up and builds a split staging animation.
+ * Called by {@link #addStagingAnimation(PendingAnimation, RectF, Rect, boolean, boolean)} and
+ * {@link #addConfirmAnimation(PendingAnimation, RectF, Rect, boolean, boolean)}.
+ */
+ public void addAnimation(PendingAnimation animation, RectF startingBounds,
+ Rect endBounds, boolean fadeWithThumbnail, boolean isStagedTask,
+ SplitAnimationTimings timings) {
mFullscreenParams.setIsStagedTask(isStagedTask);
final BaseDragLayer dragLayer = mActivity.getDragLayer();
int[] dragLayerBounds = new int[2];
@@ -201,35 +275,47 @@
RectF floatingTaskViewBounds = new RectF();
if (fadeWithThumbnail) {
- // FloatingTaskThumbnailView: thumbnail fades out to transparent
- animation.addFloat(mThumbnailView, LauncherAnimUtils.VIEW_ALPHA,
- 1, 0, clampToProgress(LINEAR, 0, 0.267f));
+ // This code block runs for the placeholder view during Overview > OverviewSplitSelect
+ // and for the selected (secondary) thumbnail during OverviewSplitSelect > Confirmed
- // SplitPlaceholderView: gray background fades in at the same time, then new icon fades
- // in
- animation.addFloat(mSplitPlaceholderView, SplitPlaceholderView.ALPHA_FLOAT,
- 0, 1, clampToProgress(LINEAR, 0, 0.267f));
- animation.addFloat(mSplitPlaceholderView, SplitPlaceholderView.ICON_ALPHA,
- 0, 1, clampToProgress(LINEAR, 0.333f, 0.5f));
+ // FloatingTaskThumbnailView: thumbnail fades out to transparent
+ animation.setViewAlpha(mThumbnailView, 0, clampToProgress(LINEAR,
+ timings.getPlaceholderFadeInStartOffset(),
+ timings.getPlaceholderFadeInEndOffset()));
+
+ // SplitPlaceholderView: gray background fades in at same time, then new icon fades in
+ fadeInSplitPlaceholder(animation, timings);
} else if (isStagedTask) {
- // Fade in the placeholder view when split is initiated from homescreen / all apps
- // icons.
+ // This code block runs for the placeholder view during Normal > OverviewSplitSelect
+ // and for the placeholder (primary) thumbnail during OverviewSplitSelect > Confirmed
+
+ // Fade in the placeholder view during Normal > OverviewSplitSelect
if (mSplitPlaceholderView.getAlpha() == 0) {
- animation.addFloat(mSplitPlaceholderView, SplitPlaceholderView.ALPHA_FLOAT,
- 0.3f, 1, ACCEL);
+ mSplitPlaceholderView.getIconView().setAlpha(0);
+ fadeInSplitPlaceholder(animation, timings);
}
+
+ // No-op for placeholder during OverviewSplitSelect > Confirmed, alpha should be set
}
MultiValueUpdateListener listener = new MultiValueUpdateListener() {
// SplitPlaceholderView: rectangle translates and stretches to new position
final FloatProp mDx = new FloatProp(0, prop.dX, 0, animDuration,
- clampToProgress(FASTER_OUT_SLOWER_IN, 0, 0.833f));
+ clampToProgress(timings.getStagedRectXInterpolator(),
+ timings.getStagedRectSlideStartOffset(),
+ timings.getStagedRectSlideEndOffset()));
final FloatProp mDy = new FloatProp(0, prop.dY, 0, animDuration,
- clampToProgress(FASTER_OUT_SLOWER_IN, 0, 0.833f));
+ clampToProgress(timings.getStagedRectYInterpolator(),
+ timings.getStagedRectSlideStartOffset(),
+ timings.getStagedRectSlideEndOffset()));
final FloatProp mTaskViewScaleX = new FloatProp(1f, prop.finalTaskViewScaleX, 0,
- animDuration, clampToProgress(FASTER_OUT_SLOWER_IN, 0, 0.833f));
+ animDuration, clampToProgress(timings.getStagedRectScaleXInterpolator(),
+ timings.getStagedRectSlideStartOffset(),
+ timings.getStagedRectSlideEndOffset()));
final FloatProp mTaskViewScaleY = new FloatProp(1f, prop.finalTaskViewScaleY, 0,
- animDuration, clampToProgress(FASTER_OUT_SLOWER_IN, 0, 0.833f));
+ animDuration, clampToProgress(timings.getStagedRectScaleYInterpolator(),
+ timings.getStagedRectSlideStartOffset(),
+ timings.getStagedRectSlideEndOffset()));
@Override
public void onUpdate(float percent, boolean initOnly) {
// Calculate the icon position.
@@ -241,9 +327,19 @@
update(floatingTaskViewBounds, percent);
}
};
+
transitionAnimator.addUpdateListener(listener);
}
+ void fadeInSplitPlaceholder(PendingAnimation animation, SplitAnimationTimings timings) {
+ animation.setViewAlpha(mSplitPlaceholderView, 1, clampToProgress(LINEAR,
+ timings.getPlaceholderFadeInStartOffset(),
+ timings.getPlaceholderFadeInEndOffset()));
+ animation.setViewAlpha(mSplitPlaceholderView.getIconView(), 1, clampToProgress(LINEAR,
+ timings.getPlaceholderIconFadeInStartOffset(),
+ timings.getPlaceholderIconFadeInEndOffset()));
+ }
+
void drawRoundedRect(Canvas canvas, Paint paint) {
if (mFullscreenParams == null) {
return;
diff --git a/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java b/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java
index 8a5f42a..6431bdf 100644
--- a/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java
+++ b/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java
@@ -25,6 +25,7 @@
import android.util.AttributeSet;
import android.util.Size;
import android.view.GhostView;
+import android.view.RemoteAnimationTarget;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
@@ -41,7 +42,6 @@
import com.android.launcher3.views.ListenerView;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
import com.android.launcher3.widget.RoundedCornerEnforcement;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
/** A view that mimics an App Widget through a launch animation. */
@TargetApi(Build.VERSION_CODES.S)
@@ -304,7 +304,7 @@
* context's theme background color.
*/
public static int getDefaultBackgroundColor(
- Context context, RemoteAnimationTargetCompat target) {
+ Context context, RemoteAnimationTarget target) {
return (target != null && target.taskInfo.taskDescription != null)
? target.taskInfo.taskDescription.getBackgroundColor()
: Themes.getColorBackground(context);
diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
index 2dff18e..2cada0a 100644
--- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
@@ -1,6 +1,5 @@
package com.android.quickstep.views;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.util.SplitConfigurationOptions.DEFAULT_SPLIT_RATIO;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
@@ -18,6 +17,7 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.RunnableList;
+import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds;
import com.android.launcher3.util.TransformingTouchDelegate;
import com.android.quickstep.RecentsModel;
@@ -25,8 +25,10 @@
import com.android.quickstep.TaskThumbnailCache;
import com.android.quickstep.util.CancellableTask;
import com.android.quickstep.util.RecentsOrientedState;
+import com.android.quickstep.util.TaskViewSimulator;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.systemui.shared.recents.utilities.PreviewPositionHelper;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import java.util.HashMap;
@@ -85,9 +87,19 @@
mTaskIdContainer[1] = secondary.key.id;
mTaskIdAttributeContainer[1] = new TaskIdAttributeContainer(secondary, mSnapshotView2,
mIconView2, STAGE_POSITION_BOTTOM_OR_RIGHT);
- mTaskIdAttributeContainer[0].setStagePosition(STAGE_POSITION_TOP_OR_LEFT);
+ mTaskIdAttributeContainer[0].setStagePosition(
+ SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT);
mSnapshotView2.bind(secondary);
mSplitBoundsConfig = splitBoundsConfig;
+ if (mSplitBoundsConfig == null) {
+ return;
+ }
+ mSnapshotView.getPreviewPositionHelper().setSplitBounds(TaskViewSimulator
+ .convertSplitBounds(splitBoundsConfig),
+ PreviewPositionHelper.STAGE_POSITION_TOP_OR_LEFT);
+ mSnapshotView2.getPreviewPositionHelper().setSplitBounds(TaskViewSimulator
+ .convertSplitBounds(splitBoundsConfig),
+ PreviewPositionHelper.STAGE_POSITION_BOTTOM_OR_RIGHT);
}
@Override
@@ -175,7 +187,7 @@
// Callbacks run from remote animation when recents animation not currently running
InteractionJankMonitorWrapper.begin(this,
InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER, "Enter form GroupedTaskView");
- recentsView.getSplitPlaceholder().launchTasks(this /*groupedTaskView*/,
+ recentsView.getSplitSelectController().launchTasks(this /*groupedTaskView*/,
success -> {
endCallback.executeAllAndDestroy();
InteractionJankMonitorWrapper.end(
@@ -190,8 +202,9 @@
@Override
public void launchTask(@NonNull Consumer<Boolean> callback, boolean freezeTaskList) {
- getRecentsView().getSplitPlaceholder().launchTasks(mTask.key.id, mSecondaryTask.key.id,
- STAGE_POSITION_TOP_OR_LEFT, callback, freezeTaskList, getSplitRatio());
+ getRecentsView().getSplitSelectController().launchTasks(mTask.key.id, mSecondaryTask.key.id,
+ SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT, callback, freezeTaskList,
+ getSplitRatio());
}
@Override
@@ -209,16 +222,23 @@
}
@Override
+ public boolean containsTaskId(int taskId) {
+ return (mTask != null && mTask.key.id == taskId)
+ || (mSecondaryTask != null && mSecondaryTask.key.id == taskId);
+ }
+
+ @Override
public TaskThumbnailView[] getThumbnails() {
return new TaskThumbnailView[]{mSnapshotView, mSnapshotView2};
}
@Override
- protected int getChildTaskIndexAtPosition(PointF position) {
- if (isCoordInView(mIconView2, position) || isCoordInView(mSnapshotView2, position)) {
+ protected int getLastSelectedChildTaskIndex() {
+ if (isCoordInView(mIconView2, mLastTouchDownPosition)
+ || isCoordInView(mSnapshotView2, mLastTouchDownPosition)) {
return 1;
}
- return super.getChildTaskIndexAtPosition(position);
+ return super.getLastSelectedChildTaskIndex();
}
private boolean isCoordInView(View v, PointF position) {
@@ -251,8 +271,7 @@
@Override
public void setOverlayEnabled(boolean overlayEnabled) {
- super.setOverlayEnabled(overlayEnabled);
- mSnapshotView2.setOverlayEnabled(overlayEnabled);
+ // Intentional no-op to prevent setting smart actions overlay on thumbnails
}
@Override
@@ -297,8 +316,8 @@
}
@Override
- protected void setIconAndDimTransitionProgress(float progress, boolean invert) {
- super.setIconAndDimTransitionProgress(progress, invert);
+ protected void setIconsAndBannersTransitionProgress(float progress, boolean invert) {
+ super.setIconsAndBannersTransitionProgress(progress, invert);
// Value set by super call
float scale = mIconView.getAlpha();
mIconView2.setAlpha(scale);
@@ -318,7 +337,29 @@
@Override
protected void applyThumbnailSplashAlpha() {
super.applyThumbnailSplashAlpha();
- mSnapshotView2.setSplashAlpha(
- Utilities.mapToRange(mOverviewProgress, 0f, 1f, 1f, 0f, LINEAR));
+ mSnapshotView2.setSplashAlpha(mTaskThumbnailSplashAlpha);
+ }
+
+ /**
+ * Sets visibility for thumbnails and associated elements (DWB banners).
+ * IconView is unaffected.
+ *
+ * When setting INVISIBLE, sets the visibility for the last selected child task.
+ * When setting VISIBLE (as a reset), sets the visibility for both tasks.
+ */
+ @Override
+ void setThumbnailVisibility(int visibility) {
+ if (visibility == VISIBLE) {
+ mSnapshotView.setVisibility(visibility);
+ mDigitalWellBeingToast.setBannerVisibility(visibility);
+ mSnapshotView2.setVisibility(visibility);
+ mDigitalWellBeingToast2.setBannerVisibility(visibility);
+ } else if (getLastSelectedChildTaskIndex() == 0) {
+ mSnapshotView.setVisibility(visibility);
+ mDigitalWellBeingToast.setBannerVisibility(visibility);
+ } else {
+ mSnapshotView2.setVisibility(visibility);
+ mDigitalWellBeingToast2.setBannerVisibility(visibility);
+ }
}
}
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index 3c5a626..6c27587 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -32,11 +32,12 @@
import androidx.annotation.Nullable;
import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.LauncherState;
+import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.popup.QuickstepSystemShortcut;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statemanager.StateManager.StateListener;
+import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.launcher3.util.PendingSplitSelectInfo;
import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.quickstep.LauncherActivityInterface;
@@ -46,7 +47,7 @@
* {@link RecentsView} used in Launcher activity
*/
@TargetApi(Build.VERSION_CODES.O)
-public class LauncherRecentsView extends RecentsView<BaseQuickstepLauncher, LauncherState>
+public class LauncherRecentsView extends RecentsView<QuickstepLauncher, LauncherState>
implements StateListener<LauncherState> {
public LauncherRecentsView(Context context) {
@@ -94,7 +95,7 @@
if (recoveryData.getStagedTaskId() == taskId) {
initiateSplitSelect(
getTaskViewByTaskId(recoveryData.getStagedTaskId()),
- recoveryData.getStagePosition()
+ recoveryData.getStagePosition(), recoveryData.getSource()
);
mActivity.finishSplitSelectRecovery();
}
@@ -104,7 +105,6 @@
@Override
public void reset() {
super.reset();
-
setLayoutRotation(Surface.ROTATION_0, Surface.ROTATION_0);
}
@@ -147,6 +147,9 @@
& CLEAR_ALL_BUTTON) != 0;
setDisallowScrollToClearAll(!hasClearAllButton);
}
+ if (mActivity.getDesktopVisibilityController() != null) {
+ mActivity.getDesktopVisibilityController().setOverviewStateEnabled(enabled);
+ }
}
@Override
@@ -162,13 +165,12 @@
}
@Override
- public void setModalStateEnabled(boolean isModalState) {
- super.setModalStateEnabled(isModalState);
+ public void setModalStateEnabled(boolean isModalState, boolean animate) {
if (isModalState) {
- mActivity.getStateManager().goToState(LauncherState.OVERVIEW_MODAL_TASK);
+ mActivity.getStateManager().goToState(LauncherState.OVERVIEW_MODAL_TASK, animate);
} else {
if (mActivity.isInState(LauncherState.OVERVIEW_MODAL_TASK)) {
- mActivity.getStateManager().goToState(LauncherState.OVERVIEW);
+ mActivity.getStateManager().goToState(LauncherState.OVERVIEW, animate);
resetModalVisuals();
}
}
@@ -187,8 +189,9 @@
@Override
public void initiateSplitSelect(TaskView taskView,
- @SplitConfigurationOptions.StagePosition int stagePosition) {
- super.initiateSplitSelect(taskView, stagePosition);
+ @SplitConfigurationOptions.StagePosition int stagePosition,
+ StatsLogManager.EventEnum splitEvent) {
+ super.initiateSplitSelect(taskView, stagePosition, splitEvent);
mActivity.getStateManager().goToState(LauncherState.OVERVIEW_SPLIT_SELECT);
}
diff --git a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
index 62ec0ef..eeabdc8 100644
--- a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
+++ b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
@@ -31,10 +31,11 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Insettable;
import com.android.launcher3.R;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.DisplayController;
-import com.android.launcher3.util.DisplayController.NavigationMode;
+import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
import com.android.launcher3.util.MultiValueAlpha;
-import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
+import com.android.launcher3.util.NavigationMode;
import com.android.quickstep.TaskOverlayFactory.OverlayUICallbacks;
import com.android.quickstep.util.LayoutUtils;
@@ -53,7 +54,9 @@
HIDDEN_NON_ZERO_ROTATION,
HIDDEN_NO_TASKS,
HIDDEN_NO_RECENTS,
- HIDDEN_SPLIT_SCREEN})
+ HIDDEN_SPLIT_SCREEN,
+ HIDDEN_SPLIT_SELECT_ACTIVE
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface ActionsHiddenFlags { }
@@ -61,6 +64,7 @@
public static final int HIDDEN_NO_TASKS = 1 << 1;
public static final int HIDDEN_NO_RECENTS = 1 << 2;
public static final int HIDDEN_SPLIT_SCREEN = 1 << 3;
+ public static final int HIDDEN_SPLIT_SELECT_ACTIVE = 1 << 4;
@IntDef(flag = true, value = {
DISABLED_SCROLLING,
@@ -78,6 +82,14 @@
private static final int INDEX_FULLSCREEN_ALPHA = 2;
private static final int INDEX_HIDDEN_FLAGS_ALPHA = 3;
private static final int INDEX_SHARE_TARGET_ALPHA = 4;
+ private static final int INDEX_SCROLL_ALPHA = 5;
+ private static final int NUM_ALPHAS = 6;
+
+ public @interface SplitButtonHiddenFlags { }
+ public static final int FLAG_IS_NOT_TABLET = 1 << 0;
+
+ public @interface SplitButtonDisabledFlags { }
+ public static final int FLAG_SINGLE_TASK = 1 << 0;
private MultiValueAlpha mMultiValueAlpha;
private Button mSplitButton;
@@ -88,6 +100,12 @@
@ActionsDisabledFlags
protected int mDisabledFlags;
+ @SplitButtonHiddenFlags
+ private int mSplitButtonHiddenFlags;
+
+ @SplitButtonDisabledFlags
+ private int mSplitButtonDisabledFlags;
+
@Nullable
protected T mCallbacks;
@@ -110,11 +128,10 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mMultiValueAlpha = new MultiValueAlpha(findViewById(R.id.action_buttons), 5);
+ mMultiValueAlpha = new MultiValueAlpha(findViewById(R.id.action_buttons), NUM_ALPHAS);
mMultiValueAlpha.setUpdateVisibility(true);
findViewById(R.id.action_screenshot).setOnClickListener(this);
-
mSplitButton = findViewById(R.id.action_split);
mSplitButton.setOnClickListener(this);
}
@@ -161,7 +178,7 @@
mHiddenFlags &= ~visibilityFlags;
}
boolean isHidden = mHiddenFlags != 0;
- mMultiValueAlpha.getProperty(INDEX_HIDDEN_FLAGS_ALPHA).setValue(isHidden ? 0 : 1);
+ mMultiValueAlpha.get(INDEX_HIDDEN_FLAGS_ALPHA).setValue(isHidden ? 0 : 1);
}
/**
@@ -180,22 +197,60 @@
}
boolean isEnabled = (mDisabledFlags & ~DISABLED_ROTATED) == 0;
LayoutUtils.setViewEnabled(this, isEnabled);
+ updateSplitButtonEnabledState();
}
- public AlphaProperty getContentAlpha() {
- return mMultiValueAlpha.getProperty(INDEX_CONTENT_ALPHA);
+ /**
+ * Updates the proper flags to indicate whether the "Split screen" button should be hidden.
+ *
+ * @param flag The flag to update.
+ * @param enable Whether to enable the hidden flag: True will cause view to be hidden.
+ */
+ public void updateSplitButtonHiddenFlags(@SplitButtonHiddenFlags int flag, boolean enable) {
+ if (enable) {
+ mSplitButtonHiddenFlags |= flag;
+ } else {
+ mSplitButtonHiddenFlags &= ~flag;
+ }
+ if (mSplitButton == null) return;
+ boolean shouldBeVisible = mSplitButtonHiddenFlags == 0;
+ mSplitButton.setVisibility(shouldBeVisible ? VISIBLE : GONE);
+ findViewById(R.id.action_split_space).setVisibility(shouldBeVisible ? VISIBLE : GONE);
}
- public AlphaProperty getVisibilityAlpha() {
- return mMultiValueAlpha.getProperty(INDEX_VISIBILITY_ALPHA);
+ /**
+ * Updates the proper flags to indicate whether the "Split screen" button should be disabled.
+ *
+ * @param flag The flag to update.
+ * @param enable Whether to enable the disable flag: True will cause view to be disabled.
+ */
+ public void updateSplitButtonDisabledFlags(@SplitButtonDisabledFlags int flag, boolean enable) {
+ if (enable) {
+ mSplitButtonDisabledFlags |= flag;
+ } else {
+ mSplitButtonDisabledFlags &= ~flag;
+ }
+ updateSplitButtonEnabledState();
}
- public AlphaProperty getFullscreenAlpha() {
- return mMultiValueAlpha.getProperty(INDEX_FULLSCREEN_ALPHA);
+ public MultiProperty getContentAlpha() {
+ return mMultiValueAlpha.get(INDEX_CONTENT_ALPHA);
}
- public AlphaProperty getShareTargetAlpha() {
- return mMultiValueAlpha.getProperty(INDEX_SHARE_TARGET_ALPHA);
+ public MultiProperty getVisibilityAlpha() {
+ return mMultiValueAlpha.get(INDEX_VISIBILITY_ALPHA);
+ }
+
+ public MultiProperty getFullscreenAlpha() {
+ return mMultiValueAlpha.get(INDEX_FULLSCREEN_ALPHA);
+ }
+
+ public MultiProperty getShareTargetAlpha() {
+ return mMultiValueAlpha.get(INDEX_SHARE_TARGET_ALPHA);
+ }
+
+ public MultiProperty getIndexScrollAlpha() {
+ return mMultiValueAlpha.get(INDEX_SCROLL_ALPHA);
}
/**
@@ -209,7 +264,9 @@
// If in 3-button mode, shift action buttons to accommodate 3-button layout.
// (Special exception for landscape tablets, where there is enough room and we don't need to
// shift the action buttons.)
- if (mDp.areNavButtonsInline && !largeScreenLandscape) {
+ if (mDp.areNavButtonsInline && !largeScreenLandscape
+ // If taskbar is in overview, overview action has dedicated space above nav buttons
+ && !FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get()) {
// Add extra horizontal spacing
int additionalPadding = mDp.hotseatBarEndOffset;
if (isLayoutRtl()) {
@@ -239,7 +296,8 @@
return 0;
}
- if (!mDp.isGestureMode && mDp.isTaskbarPresent) {
+ if (!mDp.isGestureMode && mDp.isTaskbarPresent
+ && !FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get()) {
return mDp.getOverviewActionsClaimedSpaceBelow();
}
@@ -263,12 +321,17 @@
0, 0, 0);
}
- public void setSplitButtonVisible(boolean visible) {
+ /**
+ * Enables/disables the "Split" button based on the status of mSplitButtonDisabledFlags and
+ * mDisabledFlags.
+ */
+ private void updateSplitButtonEnabledState() {
if (mSplitButton == null) {
return;
}
-
- mSplitButton.setVisibility(visible ? VISIBLE : GONE);
- findViewById(R.id.action_split_space).setVisibility(visible ? VISIBLE : GONE);
+ boolean isParentEnabled = (mDisabledFlags & ~DISABLED_ROTATED) == 0;
+ boolean shouldBeEnabled = mSplitButtonDisabledFlags == 0 && isParentEnabled;
+ mSplitButton.setEnabled(shouldBeEnabled);
}
+
}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index a153f26..80b41a7 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -39,24 +39,30 @@
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_QUICKSTEP_LIVE_TILE;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_LAUNCH_FROM_STAGED_APP;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_OVERVIEW_ACTIONS_SPLIT;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_CLEAR_ALL;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_DISMISS_SWIPE_UP;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_SWIPE_DOWN;
-import static com.android.launcher3.statehandlers.DepthController.DEPTH;
import static com.android.launcher3.touch.PagedOrientationHandler.CANVAS_TRANSLATE;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK;
import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
import static com.android.quickstep.views.ClearAllButton.DISMISS_ALPHA;
+import static com.android.quickstep.views.DesktopTaskView.DESKTOP_MODE_SUPPORTED;
+import static com.android.quickstep.views.OverviewActionsView.FLAG_IS_NOT_TABLET;
+import static com.android.quickstep.views.OverviewActionsView.FLAG_SINGLE_TASK;
import static com.android.quickstep.views.OverviewActionsView.HIDDEN_NON_ZERO_ROTATION;
import static com.android.quickstep.views.OverviewActionsView.HIDDEN_NO_RECENTS;
import static com.android.quickstep.views.OverviewActionsView.HIDDEN_NO_TASKS;
import static com.android.quickstep.views.OverviewActionsView.HIDDEN_SPLIT_SCREEN;
+import static com.android.quickstep.views.OverviewActionsView.HIDDEN_SPLIT_SELECT_ACTIVE;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -68,9 +74,13 @@
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
+import android.app.WindowConfiguration;
+import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
import android.content.LocusId;
import android.content.res.Configuration;
+import android.graphics.Bitmap;
import android.graphics.BlendMode;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -98,6 +108,7 @@
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
+import android.view.RemoteAnimationTarget;
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewGroup;
@@ -114,7 +125,6 @@
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import androidx.core.graphics.ColorUtils;
-import androidx.dynamicanimation.animation.SpringForce;
import com.android.launcher3.BaseActivity;
import com.android.launcher3.BaseActivity.MultiWindowModeChangedListener;
@@ -127,11 +137,11 @@
import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
-import com.android.launcher3.anim.SpringAnimationBuilder;
import com.android.launcher3.anim.SpringProperty;
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.cache.HandlerRunnable;
+import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.popup.QuickstepSystemShortcut;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statemanager.BaseState;
@@ -141,7 +151,6 @@
import com.android.launcher3.util.DynamicResource;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.IntSet;
-import com.android.launcher3.util.MultiValueAlpha;
import com.android.launcher3.util.ResourceBasedOverride.Overrides;
import com.android.launcher3.util.RunnableList;
import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds;
@@ -165,11 +174,16 @@
import com.android.quickstep.TaskViewUtils;
import com.android.quickstep.TopTaskTracker;
import com.android.quickstep.ViewUtils;
+import com.android.quickstep.util.ActiveGestureErrorDetector;
+import com.android.quickstep.util.ActiveGestureLog;
+import com.android.quickstep.util.AnimUtils;
+import com.android.quickstep.util.DesktopTask;
import com.android.quickstep.util.GroupTask;
import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.util.RecentsOrientedState;
-import com.android.quickstep.util.SplitScreenBounds;
+import com.android.quickstep.util.SplitAnimationTimings;
import com.android.quickstep.util.SplitSelectStateController;
+import com.android.quickstep.util.SurfaceTransaction;
import com.android.quickstep.util.SurfaceTransactionApplier;
import com.android.quickstep.util.TaskViewSimulator;
import com.android.quickstep.util.TaskVisualsChangeListener;
@@ -181,13 +195,12 @@
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import com.android.systemui.shared.system.PackageManagerWrapper;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
import com.android.wm.shell.pip.IPipAnimationListener;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
@@ -200,7 +213,7 @@
public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_TYPE>,
STATE_TYPE extends BaseState<STATE_TYPE>> extends PagedView implements Insettable,
TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback,
- TaskVisualsChangeListener, SplitScreenBounds.OnChangeListener {
+ TaskVisualsChangeListener {
private static final String TAG = "RecentsView";
private static final boolean DEBUG = false;
@@ -384,52 +397,19 @@
};
/**
- * Progress to and from the OVERVIEW state, where being in OverviewState has a value of 1, and
- * being in any other LauncherState has a value of 0.
+ * Alpha of the task thumbnail splash, where being in BackgroundAppState has a value of 1, and
+ * being in any other state has a value of 0.
*/
- public static final FloatProperty<RecentsView> OVERVIEW_PROGRESS =
- new FloatProperty<RecentsView>("overviewProgress") {
+ public static final FloatProperty<RecentsView> TASK_THUMBNAIL_SPLASH_ALPHA =
+ new FloatProperty<RecentsView>("taskThumbnailSplashAlpha") {
@Override
- public void setValue(RecentsView view, float overviewProgress) {
- view.setOverviewProgress(overviewProgress);
+ public void setValue(RecentsView view, float taskThumbnailSplashAlpha) {
+ view.setTaskThumbnailSplashAlpha(taskThumbnailSplashAlpha);
}
@Override
public Float get(RecentsView view) {
- return view.mOverviewProgress;
- }
- };
-
- public static final FloatProperty<RecentsView> FIRST_FLOATING_TASK_TRANSLATE_OFFSCREEN =
- new FloatProperty<RecentsView>("firstFloatingTaskTranslateOffscreen") {
- @Override
- public void setValue(RecentsView view, float translation) {
- view.getPagedOrientationHandler().setFloatingTaskPrimaryTranslation(
- view.mFirstFloatingTaskView,
- translation,
- view.mActivity.getDeviceProfile()
- );
- }
-
- @Override
- public Float get(RecentsView view) {
- return view.getPagedOrientationHandler().getFloatingTaskPrimaryTranslation(
- view.mFirstFloatingTaskView,
- view.mActivity.getDeviceProfile()
- );
- }
- };
-
- public static final FloatProperty<RecentsView> SPLIT_INSTRUCTIONS_FADE =
- new FloatProperty<RecentsView>("splitInstructionsFade") {
- @Override
- public void setValue(RecentsView view, float fade) {
- view.mSplitInstructionsView.setAlpha(1 - fade);
- }
-
- @Override
- public Float get(RecentsView view) {
- return 1 - view.mSplitInstructionsView.getAlpha();
+ return view.mTaskThumbnailSplashAlpha;
}
};
@@ -445,9 +425,6 @@
private static final float ANIMATION_DISMISS_PROGRESS_MIDPOINT = 0.5f;
private static final float END_DISMISS_TRANSLATION_INTERPOLATION_OFFSET = 0.75f;
- private static final float INITIAL_SPRING_DISMISS_TRANSLATION_INTERPOLATION_OFFSET = 0.133f;
- private static final float ADDITIONAL_SPRING_DISMISS_TRANSLATION_INTERPOLATION_OFFSET = 0.033f;
-
private static final float SIGNIFICANT_MOVE_SCREEN_WIDTH_PERCENTAGE = 0.15f;
protected final RecentsOrientedState mOrientationState;
@@ -506,10 +483,11 @@
private final InvariantDeviceProfile mIdp;
/**
- * Getting views should be done via {@link #getTaskViewFromPool(boolean)}
+ * Getting views should be done via {@link #getTaskViewFromPool(int)}
*/
private final ViewPool<TaskView> mTaskViewPool;
private final ViewPool<GroupedTaskView> mGroupedTaskViewPool;
+ private final ViewPool<DesktopTaskView> mDesktopTaskViewPool;
private final TaskOverlayFactory mTaskOverlayFactory;
@@ -526,7 +504,7 @@
protected float mTaskViewsSecondarySplitTranslation = 0;
// Progress from 0 to 1 where 0 is a carousel and 1 is a 2 row grid.
private float mGridProgress = 0;
- private float mOverviewProgress = 0;
+ private float mTaskThumbnailSplashAlpha = 0;
private boolean mShowAsGridLastOnLayout = false;
private final IntSet mTopRowIdSet = new IntSet();
@@ -684,11 +662,9 @@
@Nullable
private TaskView mSplitHiddenTaskView;
@Nullable
- private View mSecondSplitHiddenView;
+ private TaskView mSecondSplitHiddenView;
@Nullable
private SplitBounds mSplitBoundsConfig;
- private final Toast mSplitToast = Toast.makeText(getContext(),
- R.string.toast_split_select_app, Toast.LENGTH_SHORT);
private final Toast mSplitUnsupportedToast = Toast.makeText(getContext(),
R.string.toast_split_app_unsupported, Toast.LENGTH_SHORT);
@@ -766,6 +742,8 @@
10 /* initial size */);
mGroupedTaskViewPool = new ViewPool<>(context, this,
R.layout.task_grouped, 20 /* max size */, 10 /* initial size */);
+ mDesktopTaskViewPool = new ViewPool<>(context, this, R.layout.task_desktop,
+ 5 /* max size */, 1 /* initial size */);
mIsRtl = mOrientationHandler.getRecentsRtlSetting(getResources());
setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
@@ -845,8 +823,7 @@
}
super.dispatchDraw(canvas);
}
- if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mEnableDrawingLiveTile
- && mRemoteTargetHandles != null) {
+ if (mEnableDrawingLiveTile && mRemoteTargetHandles != null) {
redrawLiveTile();
}
}
@@ -941,7 +918,7 @@
mSplitSelectStateController = splitController;
}
- public SplitSelectStateController getSplitPlaceholder() {
+ public SplitSelectStateController getSplitSelectController() {
return mSplitSelectStateController;
}
@@ -969,10 +946,9 @@
.setSyncTransactionApplier(mSyncTransactionApplier));
RecentsModel.INSTANCE.get(getContext()).addThumbnailChangeListener(this);
mIPipAnimationListener.setActivityAndRecentsView(mActivity, this);
- SystemUiProxy.INSTANCE.get(getContext()).setPinnedStackAnimationListener(
+ SystemUiProxy.INSTANCE.get(getContext()).setPipAnimationListener(
mIPipAnimationListener);
mOrientationState.initListeners();
- SplitScreenBounds.INSTANCE.addOnChangeListener(this);
mTaskOverlayFactory.initListeners();
}
@@ -988,8 +964,7 @@
.setSyncTransactionApplier(null));
executeSideTaskLaunchCallback();
RecentsModel.INSTANCE.get(getContext()).removeThumbnailChangeListener(this);
- SystemUiProxy.INSTANCE.get(getContext()).setPinnedStackAnimationListener(null);
- SplitScreenBounds.INSTANCE.removeOnChangeListener(this);
+ SystemUiProxy.INSTANCE.get(getContext()).setPipAnimationListener(null);
mIPipAnimationListener.setActivityAndRecentsView(null, null);
mOrientationState.destroyListeners();
mTaskOverlayFactory.removeListeners();
@@ -1011,6 +986,8 @@
}
if (child instanceof GroupedTaskView) {
mGroupedTaskViewPool.recycle((GroupedTaskView) taskView);
+ } else if (child instanceof DesktopTaskView) {
+ mDesktopTaskViewPool.recycle((DesktopTaskView) taskView);
} else {
mTaskViewPool.recycle(taskView);
}
@@ -1085,8 +1062,8 @@
}
}
- public void launchSideTaskInLiveTileMode(int taskId, RemoteAnimationTargetCompat[] apps,
- RemoteAnimationTargetCompat[] wallpaper, RemoteAnimationTargetCompat[] nonApps) {
+ public void launchSideTaskInLiveTileMode(int taskId, RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpaper, RemoteAnimationTarget[] nonApps) {
AnimatorSet anim = new AnimatorSet();
TaskView taskView = getTaskViewByTaskId(taskId);
if (taskView == null || !isTaskViewVisible(taskView)) {
@@ -1098,14 +1075,15 @@
appAnimator.setInterpolator(ACCEL_DEACCEL);
appAnimator.addUpdateListener(valueAnimator -> {
float percent = valueAnimator.getAnimatedFraction();
- SurfaceParams.Builder builder = new SurfaceParams.Builder(
- apps[apps.length - 1].leash);
+ SurfaceTransaction transaction = new SurfaceTransaction();
Matrix matrix = new Matrix();
matrix.postScale(percent, percent);
matrix.postTranslate(mActivity.getDeviceProfile().widthPx * (1 - percent) / 2,
mActivity.getDeviceProfile().heightPx * (1 - percent) / 2);
- builder.withAlpha(percent).withMatrix(matrix);
- surfaceApplier.scheduleApply(builder.build());
+ transaction.forSurface(apps[apps.length - 1].leash)
+ .setAlpha(percent)
+ .setMatrix(matrix);
+ surfaceApplier.scheduleApply(transaction);
});
anim.play(appAnimator);
anim.addListener(new AnimatorListenerAdapter() {
@@ -1229,8 +1207,22 @@
for (int i = 0; i < getTaskViewCount(); i++) {
TaskView taskView = requireTaskViewAt(i);
- int[] taskIds = taskView.getTaskIds();
- if (taskIds[0] == taskId || taskIds[1] == taskId) {
+ if (taskView.containsTaskId(taskId)) {
+ return taskView;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns a {@link TaskView} that has ComponentName matching {@code componentName} or null if
+ * no match.
+ */
+ @Nullable
+ public TaskView getTaskViewByComponentName(ComponentName componentName) {
+ for (int i = 0; i < getTaskViewCount(); i++) {
+ TaskView taskView = requireTaskViewAt(i);
+ if (taskView.getTask().key.sourceComponent.equals(componentName)) {
return taskView;
}
}
@@ -1267,17 +1259,22 @@
if (!mActivity.getDeviceProfile().isTablet) {
mActionsView.updateDisabledFlags(OverviewActionsView.DISABLED_SCROLLING, true);
}
+ InteractionJankMonitorWrapper.begin(/* view= */ this,
+ InteractionJankMonitorWrapper.CUJ_RECENTS_SCROLLING);
}
@Override
protected void onPageEndTransition() {
super.onPageEndTransition();
+ ActiveGestureLog.INSTANCE.addLog(
+ "onPageEndTransition: current page index updated", getNextPage());
if (isClearAllHidden() && !mActivity.getDeviceProfile().isTablet) {
mActionsView.updateDisabledFlags(OverviewActionsView.DISABLED_SCROLLING, false);
}
if (getNextPage() > 0) {
setSwipeDownShouldLaunchApp(true);
}
+ InteractionJankMonitorWrapper.end(InteractionJankMonitorWrapper.CUJ_RECENTS_SCROLLING);
}
@Override
@@ -1462,6 +1459,12 @@
updateGridProperties();
}
+ @Override
+ protected void onScrollerAnimationAborted() {
+ ActiveGestureLog.INSTANCE.addLog("scroller animation aborted",
+ ActiveGestureErrorDetector.GestureEvent.SCROLLER_ANIMATION_ABORTED);
+ }
+
protected void applyLoadPlan(ArrayList<GroupTask> taskGroups) {
if (mPendingAnimation != null) {
mPendingAnimation.addEndListener(success -> applyLoadPlan(taskGroups));
@@ -1503,33 +1506,28 @@
// Add views as children based on whether it's grouped or single task
for (int i = taskGroups.size() - 1; i >= 0; i--) {
GroupTask groupTask = taskGroups.get(i);
- boolean hasMultipleTasks = groupTask.hasMultipleTasks();
- TaskView taskView = getTaskViewFromPool(hasMultipleTasks);
+ TaskView taskView = getTaskViewFromPool(groupTask.taskViewType);
addView(taskView);
- if (hasMultipleTasks) {
+ if (taskView instanceof GroupedTaskView) {
boolean firstTaskIsLeftTopTask =
groupTask.mSplitBounds.leftTopTaskId == groupTask.task1.key.id;
Task leftTopTask = firstTaskIsLeftTopTask ? groupTask.task1 : groupTask.task2;
Task rightBottomTask = firstTaskIsLeftTopTask ? groupTask.task2 : groupTask.task1;
((GroupedTaskView) taskView).bind(leftTopTask, rightBottomTask, mOrientationState,
groupTask.mSplitBounds);
+ } else if (taskView instanceof DesktopTaskView) {
+ ((DesktopTaskView) taskView).bind(((DesktopTask) groupTask).tasks,
+ mOrientationState);
} else {
taskView.bind(groupTask.task1, mOrientationState);
}
}
+
if (!taskGroups.isEmpty()) {
addView(mClearAllButton);
}
- boolean settlingOnNewTask = mNextPage != INVALID_PAGE;
- if (settlingOnNewTask) {
- // Restore mCurrentPage but don't call setCurrentPage() as that clobbers the scroll.
- mCurrentPage = previousCurrentPage;
- } else {
- setCurrentPage(previousCurrentPage);
- }
-
// Keep same previous focused task
TaskView newFocusedTaskView = getTaskViewByTaskId(focusedTaskId);
// If the list changed, maybe the focused task doesn't exist anymore
@@ -1554,21 +1552,36 @@
}
int targetPage = -1;
- if (!settlingOnNewTask) {
+ if (mNextPage != INVALID_PAGE) {
+ // Restore mCurrentPage but don't call setCurrentPage() as that clobbers the scroll.
+ mCurrentPage = previousCurrentPage;
+ if (currentTaskId != -1) {
+ currentTaskView = getTaskViewByTaskId(currentTaskId);
+ if (currentTaskView != null) {
+ targetPage = indexOfChild(currentTaskView);
+ }
+ }
+ } else {
// Set the current page to the running task, but not if settling on new task.
if (runningTaskId != -1) {
targetPage = indexOfChild(newRunningTaskView);
} else if (getTaskViewCount() > 0) {
targetPage = indexOfChild(requireTaskViewAt(0));
}
- } else if (currentTaskId != -1) {
- currentTaskView = getTaskViewByTaskId(currentTaskId);
- if (currentTaskView != null) {
- targetPage = indexOfChild(currentTaskView);
- }
}
if (targetPage != -1 && mCurrentPage != targetPage) {
- setCurrentPage(targetPage);
+ int finalTargetPage = targetPage;
+ runOnPageScrollsInitialized(() -> {
+ // TODO(b/246283207): Remove logging once root cause of flake detected.
+ if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+ Log.d("b/246283207", "RecentsView#applyLoadPlan() -> "
+ + "previousCurrentPage: " + previousCurrentPage
+ + ", targetPage: " + finalTargetPage
+ + ", getScrollForPage(targetPage): "
+ + getScrollForPage(finalTargetPage));
+ }
+ setCurrentPage(finalTargetPage);
+ });
}
if (mIgnoreResetTaskId != -1 &&
@@ -1646,24 +1659,23 @@
taskView.setStableAlpha(mContentAlpha);
taskView.setFullscreenProgress(mFullscreenProgress);
taskView.setModalness(mTaskModalness);
+ taskView.setTaskThumbnailSplashAlpha(mTaskThumbnailSplashAlpha);
}
}
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- // resetTaskVisuals is called at the end of dismiss animation which could update
- // primary and secondary translation of the live tile cut out. We will need to do so
- // here accordingly.
- runActionOnRemoteHandles(remoteTargetHandle -> {
- TaskViewSimulator simulator = remoteTargetHandle.getTaskViewSimulator();
- simulator.taskPrimaryTranslation.value = 0;
- simulator.taskSecondaryTranslation.value = 0;
- simulator.fullScreenProgress.value = 0;
- simulator.recentsViewScale.value = 1;
- });
- // Similar to setRunningTaskHidden below, reapply the state before runningTaskView is
- // null.
- if (!mRunningTaskShowScreenshot) {
- setRunningTaskViewShowScreenshot(mRunningTaskShowScreenshot);
- }
+ // resetTaskVisuals is called at the end of dismiss animation which could update
+ // primary and secondary translation of the live tile cut out. We will need to do so
+ // here accordingly.
+ runActionOnRemoteHandles(remoteTargetHandle -> {
+ TaskViewSimulator simulator = remoteTargetHandle.getTaskViewSimulator();
+ simulator.taskPrimaryTranslation.value = 0;
+ simulator.taskSecondaryTranslation.value = 0;
+ simulator.fullScreenProgress.value = 0;
+ simulator.recentsViewScale.value = 1;
+ });
+ // Similar to setRunningTaskHidden below, reapply the state before runningTaskView is
+ // null.
+ if (!mRunningTaskShowScreenshot) {
+ setRunningTaskViewShowScreenshot(mRunningTaskShowScreenshot);
}
if (mRunningTaskTileHidden) {
setRunningTaskHidden(mRunningTaskTileHidden);
@@ -1743,6 +1755,7 @@
// Changed orientations, update controllers so they intercept accordingly.
mActivity.getDragLayer().recreateControllers();
onOrientationChanged();
+ resetTaskVisuals();
}
boolean isInLandscape = mOrientationState.getTouchRotation() != ROTATION_0
@@ -1764,7 +1777,7 @@
private void onOrientationChanged() {
// If overview is in modal state when rotate, reset it to overview state without running
// animation.
- setModalStateEnabled(false);
+ setModalStateEnabled(/* isModalState= */ false, /* animate= */ false);
if (isSplitSelectionActive()) {
onRotateInSplitSelectionState();
}
@@ -1897,9 +1910,11 @@
private void animateActionsViewAlpha(float alphaValue, long duration) {
mActionsViewAlphaAnimator = ObjectAnimator.ofFloat(
- mActionsView.getVisibilityAlpha(), MultiValueAlpha.VALUE, alphaValue);
+ mActionsView.getVisibilityAlpha(), MULTI_PROPERTY_VALUE, alphaValue);
mActionsViewAlphaAnimatorFinalValue = alphaValue;
mActionsViewAlphaAnimator.setDuration(duration);
+ // Set autocancel to prevent race-conditiony setting of alpha from other animations
+ mActionsViewAlphaAnimator.setAutoCancel(true);
mActionsViewAlphaAnimator.start();
}
@@ -1912,6 +1927,9 @@
}
int scroll = mOrientationHandler.getPrimaryScroll(this);
mClearAllButton.onRecentsViewScroll(scroll, mOverviewGridEnabled);
+
+ // Clear all button alpha was set by the previous line.
+ mActionsView.getIndexScrollAlpha().setValue(1 - mClearAllButton.getScrollAlpha());
}
@Override
@@ -2055,7 +2073,7 @@
mFocusedTaskViewId = -1;
if (mRecentsAnimationController != null) {
- if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mEnableDrawingLiveTile) {
+ if (mEnableDrawingLiveTile) {
// We are still drawing the live tile, finish it now to clean up.
finishRecentsAnimation(true /* toRecents */, null);
} else {
@@ -2136,10 +2154,19 @@
* Handle the edge case where Recents could increment task count very high over long
* period of device usage. Probably will never happen, but meh.
*/
- private <T extends TaskView> T getTaskViewFromPool(boolean isGrouped) {
- T taskView = isGrouped ?
- (T) mGroupedTaskViewPool.getView() :
- (T) mTaskViewPool.getView();
+ private TaskView getTaskViewFromPool(@TaskView.Type int type) {
+ TaskView taskView;
+ switch (type) {
+ case TaskView.Type.GROUPED:
+ taskView = mGroupedTaskViewPool.getView();
+ break;
+ case TaskView.Type.DESKTOP:
+ taskView = mDesktopTaskViewPool.getView();
+ break;
+ case TaskView.Type.SINGLE:
+ default:
+ taskView = mTaskViewPool.getView();
+ }
taskView.setTaskViewId(mTaskViewIdCount);
if (mTaskViewIdCount == Integer.MAX_VALUE) {
mTaskViewIdCount = 0;
@@ -2231,8 +2258,14 @@
for (int i = 0; i < getTaskViewCount(); i++) {
requireTaskViewAt(i).setOrientationState(mOrientationState);
}
+ boolean shouldRotateMenuForFakeRotation =
+ !mOrientationState.isRecentsActivityRotationAllowed();
+ if (!shouldRotateMenuForFakeRotation) {
+ return;
+ }
TaskMenuView tv = (TaskMenuView) getTopOpenViewWithType(mActivity, TYPE_TASK_MENU);
if (tv != null) {
+ // Rotation is supported on phone (details at b/254198019#comment4)
tv.onRotationChanged();
}
}
@@ -2249,8 +2282,8 @@
updateGridProperties();
}
- if (mSizeStrategy.stateFromGestureEndTarget(endTarget)
- .displayOverviewTasksAsGrid(mActivity.getDeviceProfile())) {
+ BaseState<?> endState = mSizeStrategy.stateFromGestureEndTarget(endTarget);
+ if (endState.displayOverviewTasksAsGrid(mActivity.getDeviceProfile())) {
TaskView runningTaskView = getRunningTaskView();
float runningTaskPrimaryGridTranslation = 0;
if (runningTaskView != null) {
@@ -2274,11 +2307,12 @@
}
}
}
- int overviewProgress = isOverviewEndTarget ? 1 : 0;
+ int splashAlpha = endState.showTaskThumbnailSplash() ? 1 : 0;
if (animatorSet == null) {
- setOverviewProgress(overviewProgress);
+ setTaskThumbnailSplashAlpha(splashAlpha);
} else {
- animatorSet.play(ObjectAnimator.ofFloat(this, OVERVIEW_PROGRESS, overviewProgress));
+ animatorSet.play(
+ ObjectAnimator.ofFloat(this, TASK_THUMBNAIL_SPLASH_ALPHA, splashAlpha));
}
}
@@ -2293,9 +2327,6 @@
setEnableFreeScroll(true);
setEnableDrawingLiveTile(mCurrentGestureEndTarget == GestureState.GestureEndTarget.RECENTS);
- if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- setRunningTaskViewShowScreenshot(true);
- }
setRunningTaskHidden(false);
animateUpTaskIconScale();
animateActionsViewIn();
@@ -2333,12 +2364,19 @@
}
int runningTaskViewId = -1;
boolean needGroupTaskView = runningTasks.length > 1;
+ boolean needDesktopTask = hasDesktopTask(runningTasks);
if (shouldAddStubTaskView(runningTasks)) {
boolean wasEmpty = getChildCount() == 0;
// Add an empty view for now until the task plan is loaded and applied
final TaskView taskView;
- if (needGroupTaskView) {
- taskView = getTaskViewFromPool(true);
+ if (needDesktopTask) {
+ taskView = getTaskViewFromPool(TaskView.Type.DESKTOP);
+ mTmpRunningTasks = Arrays.copyOf(runningTasks, runningTasks.length);
+ addView(taskView, 0);
+ ((DesktopTaskView) taskView).bind(Arrays.asList(mTmpRunningTasks),
+ mOrientationState);
+ } else if (needGroupTaskView) {
+ taskView = getTaskViewFromPool(TaskView.Type.GROUPED);
mTmpRunningTasks = new Task[]{runningTasks[0], runningTasks[1]};
addView(taskView, 0);
// When we create a placeholder task view mSplitBoundsConfig will be null, but with
@@ -2347,7 +2385,7 @@
((GroupedTaskView)taskView).bind(mTmpRunningTasks[0], mTmpRunningTasks[1],
mOrientationState, mSplitBoundsConfig);
} else {
- taskView = getTaskViewFromPool(false);
+ taskView = getTaskViewFromPool(TaskView.Type.SINGLE);
addView(taskView, 0);
// The temporary running task is only used for the duration between the start of the
// gesture and the task list is loaded and applied
@@ -2382,6 +2420,18 @@
reloadIfNeeded();
}
+ private boolean hasDesktopTask(Task[] runningTasks) {
+ if (!DESKTOP_MODE_SUPPORTED) {
+ return false;
+ }
+ for (Task task : runningTasks) {
+ if (task.key.windowingMode == WindowConfiguration.WINDOWING_MODE_FREEFORM) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* Sets the running task id, cleaning up the old running task if necessary.
*/
@@ -2420,12 +2470,10 @@
}
private void setRunningTaskViewShowScreenshot(boolean showScreenshot) {
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- mRunningTaskShowScreenshot = showScreenshot;
- TaskView runningTaskView = getRunningTaskView();
- if (runningTaskView != null) {
- runningTaskView.setShowScreenshot(mRunningTaskShowScreenshot);
- }
+ mRunningTaskShowScreenshot = showScreenshot;
+ TaskView runningTaskView = getRunningTaskView();
+ if (runningTaskView != null) {
+ runningTaskView.setShowScreenshot(mRunningTaskShowScreenshot);
}
}
@@ -2729,15 +2777,15 @@
mClearAllButton.setGridProgress(gridProgress);
}
- private void setOverviewProgress(float overviewProgress) {
+ private void setTaskThumbnailSplashAlpha(float taskThumbnailSplashAlpha) {
int taskCount = getTaskViewCount();
if (taskCount == 0) {
return;
}
- mOverviewProgress = overviewProgress;
+ mTaskThumbnailSplashAlpha = taskThumbnailSplashAlpha;
for (int i = 0; i < taskCount; i++) {
- requireTaskViewAt(i).setOverviewProgress(overviewProgress);
+ requireTaskViewAt(i).setTaskThumbnailSplashAlpha(taskThumbnailSplashAlpha);
}
}
@@ -2794,7 +2842,7 @@
anim.setFloat(taskView, VIEW_ALPHA, 0,
clampToProgress(isOnGridBottomRow(taskView) ? ACCEL : FINAL_FRAME, 0, 0.5f));
FloatProperty<TaskView> secondaryViewTranslate =
- taskView.getSecondaryDissmissTranslationProperty();
+ taskView.getSecondaryDismissTranslationProperty();
int secondaryTaskDimension = mOrientationHandler.getSecondaryDimension(taskView);
int verticalFactor = mOrientationHandler.getSecondaryTranslationDirectionFactor();
@@ -2806,8 +2854,7 @@
anim.add(ObjectAnimator.ofFloat(taskView, secondaryViewTranslate,
verticalFactor * secondaryTaskDimension * 2).setDuration(duration), LINEAR, sp);
- if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mEnableDrawingLiveTile
- && taskView.isRunningTask()) {
+ if (mEnableDrawingLiveTile && taskView.isRunningTask()) {
anim.addOnFrameCallback(() -> {
runActionOnRemoteHandles(
remoteTargetHandle -> remoteTargetHandle.getTaskViewSimulator()
@@ -2828,62 +2875,110 @@
mOrientationHandler.getInitialSplitPlaceholderBounds(mSplitPlaceholderSize,
mSplitPlaceholderInset, mActivity.getDeviceProfile(),
mSplitSelectStateController.getActiveSplitStagePosition(), mTempRect);
+ SplitAnimationTimings timings =
+ AnimUtils.getDeviceOverviewToSplitTimings(mActivity.getDeviceProfile().isTablet);
RectF startingTaskRect = new RectF();
+ safeRemoveDragLayerView(mFirstFloatingTaskView);
if (mSplitHiddenTaskView != null) {
- // Split staging is initiated, hide the original TaskView except for the icon (which
- // needs to animate out over time)
- // If needed, visibility should be toggled back on in resetFromSplitSelectionState().
- for (int i = 0; i < mSplitHiddenTaskView.getChildCount(); i++) {
- View child = mSplitHiddenTaskView.getChildAt(i);
- if (child != mSplitHiddenTaskView.mIconView) {
- child.setVisibility(INVISIBLE);
- }
- }
-
- // Icon animates out over time
- anim.addFloat(mSplitHiddenTaskView, TaskView.ICON_ALPHA, 1, 0,
- clampToProgress(LINEAR, 0, 0.167f));
-
+ // Create the split select animation from Overview
+ mSplitHiddenTaskView.setThumbnailVisibility(INVISIBLE);
+ anim.setViewAlpha(mSplitHiddenTaskView.getIconView(), 0, clampToProgress(LINEAR,
+ timings.getIconFadeStartOffset(),
+ timings.getIconFadeEndOffset()));
mFirstFloatingTaskView = FloatingTaskView.getFloatingTaskView(mActivity,
mSplitHiddenTaskView.getThumbnail(),
mSplitHiddenTaskView.getThumbnail().getThumbnail(),
mSplitHiddenTaskView.getIconView().getDrawable(), startingTaskRect);
mFirstFloatingTaskView.setAlpha(1);
- mFirstFloatingTaskView.addAnimation(anim, startingTaskRect, mTempRect,
+ mFirstFloatingTaskView.addStagingAnimation(anim, startingTaskRect, mTempRect,
true /* fadeWithThumbnail */, true /* isStagedTask */);
} else {
+ // Create the split select animation from Home
mFirstFloatingTaskView = FloatingTaskView.getFloatingTaskView(mActivity,
mSplitSelectSource.view, null /* thumbnail */,
mSplitSelectSource.drawable, startingTaskRect);
mFirstFloatingTaskView.setAlpha(1);
- mFirstFloatingTaskView.addAnimation(anim, startingTaskRect, mTempRect,
+ mFirstFloatingTaskView.addStagingAnimation(anim, startingTaskRect, mTempRect,
false /* fadeWithThumbnail */, true /* isStagedTask */);
}
+ // TODO (b/257513449): Launch animation not fully complete. OK to remove flag once it is.
+ if (ENABLE_LAUNCH_FROM_STAGED_APP.get()) {
+ mFirstFloatingTaskView.setOnClickListener(this::animateToFullscreen);
+ }
+
// SplitInstructionsView: animate in
+ safeRemoveDragLayerView(mSplitInstructionsView);
mSplitInstructionsView = SplitInstructionsView.getSplitInstructionsView(mActivity);
mSplitInstructionsView.setAlpha(0);
- anim.addFloat(mSplitInstructionsView, SplitInstructionsView.CONTAINER_ALPHA, 0, 1,
- clampToProgress(LINEAR, 0, 0.167f));
- anim.addFloat(mSplitInstructionsView, SplitInstructionsView.TEXT_ALPHA, 0, 1,
- clampToProgress(LINEAR, 0.1f, 0.267f));
+ anim.setViewAlpha(mSplitInstructionsView, 1, clampToProgress(LINEAR,
+ timings.getInstructionsContainerFadeInStartOffset(),
+ timings.getInstructionsContainerFadeInEndOffset()));
+ anim.setViewAlpha(mSplitInstructionsView.getTextView(), 1, clampToProgress(LINEAR,
+ timings.getInstructionsTextFadeInStartOffset(),
+ timings.getInstructionsTextFadeInEndOffset()));
anim.addFloat(mSplitInstructionsView, mSplitInstructionsView.UNFOLD, 0.1f, 1,
- clampToProgress(EMPHASIZED_DECELERATE, 0, 0.667f));
+ clampToProgress(EMPHASIZED_DECELERATE,
+ timings.getInstructionsUnfoldStartOffset(),
+ timings.getInstructionsUnfoldEndOffset()));
InteractionJankMonitorWrapper.begin(this,
InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER, "First tile selected");
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ if (mSplitHiddenTaskView == getRunningTaskView()) {
+ finishRecentsAnimation(true /* toRecents */, false /* shouldPip */,
+ null /* onFinishComplete */);
+ } else {
+ switchToScreenshot(
+ () -> finishRecentsAnimation(true /* toRecents */,
+ false /* shouldPip */, null /* onFinishComplete */));
+ }
+ }
+ });
anim.addEndListener(success -> {
if (success) {
InteractionJankMonitorWrapper.end(
InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER);
} else {
+ // If transition to split select was interrupted, clean up to prevent glitches
+ resetFromSplitSelectionState();
InteractionJankMonitorWrapper.cancel(
InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER);
}
});
}
+ private void animateToFullscreen(View view) {
+ FloatingTaskView stagedTaskView = (FloatingTaskView) view;
+
+ boolean isTablet = mActivity.getDeviceProfile().isTablet;
+ int duration = isTablet
+ ? SplitAnimationTimings.TABLET_CONFIRM_DURATION
+ : SplitAnimationTimings.PHONE_CONFIRM_DURATION;
+
+ PendingAnimation pendingAnimation = new PendingAnimation(duration);
+
+ Rect firstTaskStartingBounds = new Rect();
+ Rect firstTaskEndingBounds = new Rect();
+
+ stagedTaskView.getBoundsOnScreen(firstTaskStartingBounds);
+ mActivity.getDragLayer().getBoundsOnScreen(firstTaskEndingBounds);
+
+ stagedTaskView.addConfirmAnimation(
+ pendingAnimation,
+ new RectF(firstTaskStartingBounds),
+ firstTaskEndingBounds,
+ false /* fadeWithThumbnail */,
+ true /* isStagedTask */);
+
+ pendingAnimation.addEndListener(success -> launchStagedTask());
+
+ pendingAnimation.buildAnim().start();
+ }
+
/**
* Creates a {@link PendingAnimation} for dismissing the specified {@link TaskView}.
* @param dismissedTaskView the {@link TaskView} to be dismissed
@@ -2894,17 +2989,16 @@
* @param dismissingForSplitSelection task dismiss animation is used for entering split
* selection state from app icon
*/
- public PendingAnimation createTaskDismissAnimation(TaskView dismissedTaskView,
+ public void createTaskDismissAnimation(PendingAnimation anim, TaskView dismissedTaskView,
boolean animateTaskView, boolean shouldRemoveTask, long duration,
boolean dismissingForSplitSelection) {
if (mPendingAnimation != null) {
mPendingAnimation.createPlaybackController().dispatchOnCancel().dispatchOnEnd();
}
- PendingAnimation anim = new PendingAnimation(duration);
int count = getPageCount();
if (count == 0) {
- return anim;
+ return;
}
boolean showAsGrid = showAsGrid();
@@ -3045,8 +3139,7 @@
dismissTranslationInterpolationEnd
- halfAdditionalDismissTranslationOffset,
END_DISMISS_TRANSLATION_INTERPOLATION_OFFSET, 1);
- if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mEnableDrawingLiveTile
- && taskView.isRunningTask()) {
+ if (mEnableDrawingLiveTile && taskView.isRunningTask()) {
anim.addOnFrameCallback(() -> {
runActionOnRemoteHandles(
remoteTargetHandle ->
@@ -3072,6 +3165,9 @@
}
}
+ SplitAnimationTimings splitTimings =
+ AnimUtils.getDeviceOverviewToSplitTimings(mActivity.getDeviceProfile().isTablet);
+
int distanceFromDismissedTask = 0;
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
@@ -3114,11 +3210,31 @@
float additionalDismissDuration =
ADDITIONAL_DISMISS_TRANSLATION_INTERPOLATION_OFFSET * Math.abs(
i - dismissedIndex);
- anim.setFloat(child, translationProperty, scrollDiff, clampToProgress(LINEAR,
- Utilities.boundToRange(INITIAL_DISMISS_TRANSLATION_INTERPOLATION_OFFSET
- + additionalDismissDuration, 0f, 1f), 1));
- if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mEnableDrawingLiveTile
- && child instanceof TaskView
+
+ // We are in non-grid layout.
+ // If dismissing for split select, use split timings.
+ // If not, use dismiss timings.
+ float animationStartProgress = isSplitSelectionActive()
+ ? Utilities.boundToRange(splitTimings.getGridSlideStartOffset(), 0f, 1f)
+ : Utilities.boundToRange(
+ INITIAL_DISMISS_TRANSLATION_INTERPOLATION_OFFSET
+ + additionalDismissDuration, 0f, 1f);
+
+ float animationEndProgress = isSplitSelectionActive()
+ ? Utilities.boundToRange(splitTimings.getGridSlideStartOffset()
+ + splitTimings.getGridSlideDurationOffset(), 0f, 1f)
+ : 1f;
+
+ // Slide tiles in horizontally to fill dismissed area
+ anim.setFloat(child, translationProperty, scrollDiff,
+ clampToProgress(
+ splitTimings.getGridSlidePrimaryInterpolator(),
+ animationStartProgress,
+ animationEndProgress
+ )
+ );
+
+ if (mEnableDrawingLiveTile && child instanceof TaskView
&& ((TaskView) child).isRunningTask()) {
anim.addOnFrameCallback(() -> {
runActionOnRemoteHandles(
@@ -3150,20 +3266,33 @@
// dismissed index or next focused index. Offset successive task dismissal
// durations for a staggered effect.
distanceFromDismissedTask++;
- // If user is initiating splitscreen from the focused (large) task, we use a
- // spring-based animation and timings. For other, smaller, repositions, we currently
- // fall back on a less complicated linear animation and timings.
- float animationStartProgress = isFocusedTaskDismissed && nextFocusedTaskView == null
+ boolean isStagingFocusedTask =
+ isFocusedTaskDismissed && nextFocusedTaskView == null;
+ int staggerColumn = isStagingFocusedTask
+ ? (int) Math.ceil(distanceFromDismissedTask / 2f)
+ : distanceFromDismissedTask;
+ // Set timings based on if user is initiating splitscreen on the focused task,
+ // or splitting/dismissing some other task.
+ float animationStartProgress = isStagingFocusedTask
? Utilities.boundToRange(
- INITIAL_SPRING_DISMISS_TRANSLATION_INTERPOLATION_OFFSET
- + ADDITIONAL_SPRING_DISMISS_TRANSLATION_INTERPOLATION_OFFSET
- * (int) Math.ceil(distanceFromDismissedTask / 2f), 0f,
+ splitTimings.getGridSlideStartOffset()
+ + (splitTimings.getGridSlideStaggerOffset()
+ * staggerColumn),
+ 0f,
dismissTranslationInterpolationEnd)
: Utilities.boundToRange(
INITIAL_DISMISS_TRANSLATION_INTERPOLATION_OFFSET
+ ADDITIONAL_DISMISS_TRANSLATION_INTERPOLATION_OFFSET
- * distanceFromDismissedTask, 0f,
- dismissTranslationInterpolationEnd);
+ * staggerColumn, 0f, dismissTranslationInterpolationEnd);
+ float animationEndProgress = isStagingFocusedTask
+ ? Utilities.boundToRange(
+ splitTimings.getGridSlideStartOffset()
+ + (splitTimings.getGridSlideStaggerOffset() * staggerColumn)
+ + splitTimings.getGridSlideDurationOffset(),
+ 0f,
+ dismissTranslationInterpolationEnd)
+ : dismissTranslationInterpolationEnd;
+ Interpolator dismissInterpolator = isStagingFocusedTask ? OVERSHOOT_0_75 : LINEAR;
if (taskView == nextFocusedTaskView) {
// Enlarge the task to be focused next, and translate into focus position.
@@ -3179,7 +3308,7 @@
if (!nextFocusedTaskFromTop) {
secondaryTranslation -= mTopBottomRowHeightDiff;
}
- anim.setFloat(taskView, taskView.getSecondaryDissmissTranslationProperty(),
+ anim.setFloat(taskView, taskView.getSecondaryDismissTranslationProperty(),
secondaryTranslation, clampToProgress(LINEAR, animationStartProgress,
dismissTranslationInterpolationEnd));
anim.setFloat(taskView, TaskView.FOCUS_TRANSITION, 0f,
@@ -3187,7 +3316,7 @@
} else {
float primaryTranslation =
nextFocusedTaskView != null ? nextFocusedTaskWidth : dismissedTaskWidth;
- if (isFocusedTaskDismissed && nextFocusedTaskView == null) {
+ if (isStagingFocusedTask) {
// Moves less if focused task is not in scroll position.
int focusedTaskScroll = getScrollForPage(dismissedIndex);
int primaryScroll = mOrientationHandler.getPrimaryScroll(this);
@@ -3199,36 +3328,12 @@
primaryTranslation +=
mIsRtl ? -mSplitPlaceholderSize : mSplitPlaceholderSize;
}
-
- // Transitioning to split select -- set up staggered spring animation for
- // other TaskViews.
- Animator taskSlideIn = new SpringAnimationBuilder(taskView.mActivity)
- .setDampingRatio(0.85f)
- .setStiffness(SpringForce.STIFFNESS_LOW)
- .setEndValue(mIsRtl ? primaryTranslation : -primaryTranslation)
- .setStartValue(
- taskView.getPrimaryDismissTranslationProperty()
- .get(taskView)
- )
- .build(taskView, taskView.getPrimaryDismissTranslationProperty());
- long taskSlideInDuration = taskSlideIn.getDuration();
- anim.add(taskSlideIn);
- taskSlideIn
- .setDuration(taskSlideInDuration)
- .setStartDelay(
- Math.round(animationStartProgress * anim.getDuration()));
- } else {
- // Task was dismissed individually -- translate other TaskViews to fill the
- // vacant space.
-
- // TODO (b/242075836): This dismiss animation uses a linear transition.
- // When the above bug is fixed, it can use the same (nicer) spring
- // transition as the focused task split case above.
- anim.setFloat(taskView, taskView.getPrimaryDismissTranslationProperty(),
- mIsRtl ? primaryTranslation : -primaryTranslation,
- clampToProgress(LINEAR, animationStartProgress,
- dismissTranslationInterpolationEnd));
}
+
+ anim.setFloat(taskView, taskView.getPrimaryDismissTranslationProperty(),
+ mIsRtl ? primaryTranslation : -primaryTranslation,
+ clampToProgress(dismissInterpolator, animationStartProgress,
+ animationEndProgress));
}
}
}
@@ -3249,12 +3354,10 @@
final boolean finalCloseGapBetweenClearAll = closeGapBetweenClearAll;
final boolean finalSnapToLastTask = snapToLastTask;
final boolean finalIsFocusedTaskDismissed = isFocusedTaskDismissed;
-
- Consumer endConsumer = new Consumer<Boolean>() {
+ mPendingAnimation.addEndListener(new Consumer<Boolean>() {
@Override
public void accept(Boolean success) {
- if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mEnableDrawingLiveTile
- && dismissedTaskView.isRunningTask() && success) {
+ if (mEnableDrawingLiveTile && dismissedTaskView.isRunningTask() && success) {
finishRecentsAnimation(true /* toRecents */, false /* shouldPip */,
() -> onEnd(success));
} else {
@@ -3271,8 +3374,7 @@
if (success) {
if (shouldRemoveTask) {
if (dismissedTaskView.getTask() != null) {
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()
- && dismissedTaskView.isRunningTask()) {
+ if (dismissedTaskView.isRunningTask()) {
finishRecentsAnimation(true /* toRecents */, false /* shouldPip */,
() -> removeTaskInternal(dismissedTaskViewId));
} else {
@@ -3460,10 +3562,7 @@
onDismissAnimationEnds();
mPendingAnimation = null;
}
- };
-
- mPendingAnimation.addListener(AnimatorListeners.forEndCallback(endConsumer));
- return anim;
+ });
}
/**
@@ -3475,11 +3574,11 @@
private void updateCurrentTaskActionsVisibility() {
boolean isCurrentSplit = getCurrentPageTaskView() instanceof GroupedTaskView;
mActionsView.updateHiddenFlags(HIDDEN_SPLIT_SCREEN, isCurrentSplit);
- if (isCurrentSplit) {
- return;
- }
- mActionsView.setSplitButtonVisible(
- mActivity.getDeviceProfile().isTablet && getTaskViewCount() > 1);
+ mActionsView.updateHiddenFlags(HIDDEN_SPLIT_SELECT_ACTIVE, isSplitSelectionActive());
+ mActionsView.updateSplitButtonHiddenFlags(FLAG_IS_NOT_TABLET,
+ !mActivity.getDeviceProfile().isTablet);
+ mActionsView.updateSplitButtonDisabledFlags(FLAG_SINGLE_TASK,
+ !FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get() && getTaskViewCount() <= 1);
}
/**
@@ -3631,8 +3730,10 @@
}
public void dismissTask(TaskView taskView, boolean animateTaskView, boolean removeTask) {
- runDismissAnimation(createTaskDismissAnimation(taskView, animateTaskView, removeTask,
- DISMISS_TASK_DURATION, false /* dismissingForSplitSelection*/));
+ PendingAnimation pa = new PendingAnimation(DISMISS_TASK_DURATION);
+ createTaskDismissAnimation(pa, taskView, animateTaskView, removeTask, DISMISS_TASK_DURATION,
+ false /* dismissingForSplitSelection*/);
+ runDismissAnimation(pa);
}
@SuppressWarnings("unused")
@@ -3932,8 +4033,7 @@
? ((TaskView) child).getPrimaryTaskOffsetTranslationProperty()
: mOrientationHandler.getPrimaryViewTranslate();
translationProperty.set(child, totalTranslation);
- if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mEnableDrawingLiveTile
- && i == getRunningTaskIndex()) {
+ if (mEnableDrawingLiveTile && i == getRunningTaskIndex()) {
runActionOnRemoteHandles(
remoteTargetHandle -> remoteTargetHandle.getTaskViewSimulator()
.taskPrimaryTranslation.value = totalTranslation);
@@ -4141,37 +4241,45 @@
}
}
+ /**
+ * Primarily used by overview actions to initiate split from focused task, logs the source
+ * of split invocation as such.
+ */
public void initiateSplitSelect(TaskView taskView) {
int defaultSplitPosition = mOrientationHandler
.getDefaultSplitPosition(mActivity.getDeviceProfile());
- initiateSplitSelect(taskView, defaultSplitPosition);
+ initiateSplitSelect(taskView, defaultSplitPosition, LAUNCHER_OVERVIEW_ACTIONS_SPLIT);
}
- public void initiateSplitSelect(TaskView taskView, @StagePosition int stagePosition) {
+ public void initiateSplitSelect(TaskView taskView, @StagePosition int stagePosition,
+ StatsLogManager.EventEnum splitEvent) {
mSplitHiddenTaskView = taskView;
- mSplitSelectStateController.setInitialTaskSelect(taskView.getTask().key.id,
- stagePosition);
+ mSplitSelectStateController.setInitialTaskSelect(taskView.getTask(),
+ stagePosition, splitEvent, taskView.getItemInfo());
mSplitHiddenTaskViewIndex = indexOfChild(taskView);
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- finishRecentsAnimation(true /* toRecents */, false /* shouldPip */,
- null /* onFinishComplete */);
- }
}
+ /**
+ * Called when staging a split from Home/AllApps, using the icon long-press menu.
+ */
public void initiateSplitSelect(QuickstepSystemShortcut.SplitSelectSource splitSelectSource) {
mSplitSelectSource = splitSelectSource;
mSplitSelectStateController.setInitialTaskSelect(splitSelectSource.intent,
- splitSelectSource.position.stagePosition);
+ splitSelectSource.position.stagePosition, splitSelectSource.itemInfo,
+ splitSelectSource.splitEvent);
}
- public PendingAnimation createSplitSelectInitAnimation(int duration) {
+ /**
+ * Modifies a PendingAnimation with the animations for entering split staging
+ */
+ public void createSplitSelectInitAnimation(PendingAnimation builder, int duration) {
if (mSplitHiddenTaskView != null) {
- return createTaskDismissAnimation(mSplitHiddenTaskView, true, false, duration,
+ // Splitting from Overview
+ createTaskDismissAnimation(builder, mSplitHiddenTaskView, true, false, duration,
true /* dismissingForSplitSelection*/);
} else {
- PendingAnimation anim = new PendingAnimation(duration);
- createInitialSplitSelectAnimation(anim);
- return anim;
+ // Splitting from Home
+ createInitialSplitSelectAnimation(builder);
}
}
@@ -4179,32 +4287,48 @@
* Confirms the selection of the next split task. The extra data is passed through because the
* user may be selecting a subtask in a group.
*
+ * @param containerTaskView If our second selected app is currently running in Recents, this is
+ * the "container" TaskView from Recents. If we are starting a fresh
+ * instance of the app from an Intent, this will be null.
+ * @param task The Task corresponding to our second selected app. If we are starting a fresh
+ * instance of the app from an Intent, this will be null.
+ * @param drawable The Drawable corresponding to our second selected app's icon.
+ * @param secondView The View representing the current space on the screen where the second app
+ * is (either the ThumbnailView or the tapped icon).
+ * @param intent If we are launching a fresh instance of the app, this is the Intent for it. If
+ * the second app is already running in Recents, this will be null.
* @return true if waiting for confirmation of second app or if split animations are running,
* false otherwise
*/
- public boolean confirmSplitSelect(TaskView containerTaskView, Task task, IconView iconView,
- TaskThumbnailView thumbnailView) {
+ public boolean confirmSplitSelect(TaskView containerTaskView, Task task, Drawable drawable,
+ View secondView, @Nullable Bitmap thumbnail, Intent intent) {
if (canLaunchFullscreenTask()) {
return false;
}
if (mSplitSelectStateController.isBothSplitAppsConfirmed()) {
return true;
}
- mSplitToast.cancel();
- if (!task.isDockable) {
- // Task not split screen supported
- mSplitUnsupportedToast.show();
- return true;
+ // Second task is selected either as an already-running Task or an Intent
+ if (task != null) {
+ if (!task.isDockable) {
+ // Task does not support split screen
+ mSplitUnsupportedToast.show();
+ return true;
+ }
+ mSplitSelectStateController.setSecondTask(task);
+ } else {
+ mSplitSelectStateController.setSecondTask(intent);
}
- mSplitSelectStateController.setSecondTask(task);
+
RectF secondTaskStartingBounds = new RectF();
Rect secondTaskEndingBounds = new Rect();
// TODO(194414938) starting bounds seem slightly off, investigate
Rect firstTaskStartingBounds = new Rect();
Rect firstTaskEndingBounds = mTempRect;
- int duration = mActivity.getStateManager().getState().getTransitionDuration(mActivity,
- false /* isToState */);
- PendingAnimation pendingAnimation = new PendingAnimation(duration);
+
+ boolean isTablet = mActivity.getDeviceProfile().isTablet;
+ SplitAnimationTimings timings = AnimUtils.getDeviceSplitToConfirmTimings(isTablet);
+ PendingAnimation pendingAnimation = new PendingAnimation(timings.getDuration());
int halfDividerSize = getResources()
.getDimensionPixelSize(R.dimen.multi_window_task_divider_size) / 2;
@@ -4214,28 +4338,33 @@
secondTaskEndingBounds);
mFirstFloatingTaskView.getBoundsOnScreen(firstTaskStartingBounds);
- mFirstFloatingTaskView.addAnimation(pendingAnimation,
+ mFirstFloatingTaskView.addConfirmAnimation(pendingAnimation,
new RectF(firstTaskStartingBounds), firstTaskEndingBounds,
false /* fadeWithThumbnail */, true /* isStagedTask */);
- mSecondFloatingTaskView = FloatingTaskView.getFloatingTaskView(mActivity,
- thumbnailView, thumbnailView.getThumbnail(),
- iconView.getDrawable(), secondTaskStartingBounds);
+ safeRemoveDragLayerView(mSecondFloatingTaskView);
+
+ mSecondFloatingTaskView = FloatingTaskView.getFloatingTaskView(mActivity, secondView,
+ thumbnail, drawable, secondTaskStartingBounds);
mSecondFloatingTaskView.setAlpha(1);
- mSecondFloatingTaskView.addAnimation(pendingAnimation, secondTaskStartingBounds,
+ mSecondFloatingTaskView.addConfirmAnimation(pendingAnimation, secondTaskStartingBounds,
secondTaskEndingBounds, true /* fadeWithThumbnail */, false /* isStagedTask */);
+
+ pendingAnimation.setViewAlpha(mSplitInstructionsView, 0, clampToProgress(LINEAR,
+ timings.getInstructionsFadeStartOffset(),
+ timings.getInstructionsFadeEndOffset()));
+
pendingAnimation.addEndListener(aBoolean -> {
mSplitSelectStateController.launchSplitTasks(
aBoolean1 -> RecentsView.this.resetFromSplitSelectionState());
InteractionJankMonitorWrapper.end(InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER);
});
- if (containerTaskView.containsMultipleTasks()) {
- // If we are launching from a child task, then only hide the thumbnail itself
- mSecondSplitHiddenView = thumbnailView;
- } else {
- mSecondSplitHiddenView = containerTaskView;
+
+ mSecondSplitHiddenView = containerTaskView;
+ if (mSecondSplitHiddenView != null) {
+ mSecondSplitHiddenView.setThumbnailVisibility(INVISIBLE);
}
- mSecondSplitHiddenView.setVisibility(INVISIBLE);
+
InteractionJankMonitorWrapper.begin(this,
InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER, "Second tile selected");
@@ -4250,23 +4379,20 @@
@SuppressLint("WrongCall")
protected void resetFromSplitSelectionState() {
if (mSplitSelectSource != null || mSplitHiddenTaskViewIndex != -1) {
- if (mSplitInstructionsView != null) {
- mActivity.getDragLayer().removeView(mSplitInstructionsView);
- mSplitInstructionsView = null;
- }
- if (mFirstFloatingTaskView != null) {
- mActivity.getDragLayer().removeView(mFirstFloatingTaskView);
- mFirstFloatingTaskView = null;
- }
- if (mSecondFloatingTaskView != null) {
- mActivity.getDragLayer().removeView(mSecondFloatingTaskView);
- mSecondFloatingTaskView = null;
- mSecondSplitHiddenView.setVisibility(VISIBLE);
- mSecondSplitHiddenView = null;
- }
+ safeRemoveDragLayerView(mFirstFloatingTaskView);
+ safeRemoveDragLayerView(mSecondFloatingTaskView);
+ safeRemoveDragLayerView(mSplitInstructionsView);
+ mFirstFloatingTaskView = null;
+ mSecondFloatingTaskView = null;
+ mSplitInstructionsView = null;
mSplitSelectSource = null;
}
+ if (mSecondSplitHiddenView != null) {
+ mSecondSplitHiddenView.setThumbnailVisibility(VISIBLE);
+ mSecondSplitHiddenView = null;
+ }
+
if (mSplitHiddenTaskViewIndex == -1) {
return;
}
@@ -4280,19 +4406,32 @@
snapToPageImmediately(pageToSnapTo);
}
onLayout(false /* changed */, getLeft(), getTop(), getRight(), getBottom());
+
+ // We are leaving split selection state, so it is safe to reset thumbnail translations for
+ // the next time split is invoked.
+ setTaskViewsPrimarySplitTranslation(0);
+ setTaskViewsSecondarySplitTranslation(0);
+
resetTaskVisuals();
mSplitHiddenTaskViewIndex = -1;
if (mSplitHiddenTaskView != null) {
+ mSplitHiddenTaskView.setThumbnailVisibility(VISIBLE);
mSplitHiddenTaskView = null;
}
}
+ private void safeRemoveDragLayerView(@Nullable View viewToRemove) {
+ if (viewToRemove != null) {
+ mActivity.getDragLayer().removeView(viewToRemove);
+ }
+ }
+
/**
* Returns how much additional translation there should be for each of the child TaskViews.
* Note that the translation can be its primary or secondary dimension.
*/
public float getSplitSelectTranslation() {
- int splitPosition = getSplitPlaceholder().getActiveSplitStagePosition();
+ int splitPosition = getSplitSelectController().getActiveSplitStagePosition();
if (!shouldShiftThumbnailsForSplitSelect()) {
return 0f;
}
@@ -4396,30 +4535,31 @@
* If launching one of the adjacent tasks, parallax the center task and other adjacent task
* to the right.
*/
+ @SuppressLint("Recycle")
public AnimatorSet createAdjacentPageAnimForTaskLaunch(TaskView tv) {
AnimatorSet anim = new AnimatorSet();
int taskIndex = indexOfChild(tv);
int centerTaskIndex = getCurrentPage();
- boolean launchingCenterTask = taskIndex == centerTaskIndex;
float toScale = getMaxScaleForFullScreen();
- RecentsView recentsView = tv.getRecentsView();
+ boolean showAsGrid = showAsGrid();
+ boolean launchingCenterTask = showAsGrid
+ ? tv.isFocusedTask() && isTaskViewFullyVisible(tv)
+ : taskIndex == centerTaskIndex;
if (launchingCenterTask) {
- anim.play(ObjectAnimator.ofFloat(recentsView, RECENTS_SCALE_PROPERTY, toScale));
- anim.play(ObjectAnimator.ofFloat(recentsView, FULLSCREEN_PROGRESS, 1));
- } else {
+ anim.play(ObjectAnimator.ofFloat(this, RECENTS_SCALE_PROPERTY, toScale));
+ anim.play(ObjectAnimator.ofFloat(this, FULLSCREEN_PROGRESS, 1));
+ } else if (!showAsGrid) {
// We are launching an adjacent task, so parallax the center and other adjacent task.
float displacementX = tv.getWidth() * (toScale - 1f);
float primaryTranslation = mIsRtl ? -displacementX : displacementX;
anim.play(ObjectAnimator.ofFloat(getPageAt(centerTaskIndex),
mOrientationHandler.getPrimaryViewTranslate(), primaryTranslation));
- int runningTaskIndex = recentsView.getRunningTaskIndex();
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()
- && runningTaskIndex != -1
- && runningTaskIndex != taskIndex
- && recentsView.getRemoteTargetHandles() != null) {
- for (RemoteTargetHandle remoteHandle : recentsView.getRemoteTargetHandles()) {
+ int runningTaskIndex = getRunningTaskIndex();
+ if (runningTaskIndex != -1 && runningTaskIndex != taskIndex
+ && getRemoteTargetHandles() != null) {
+ for (RemoteTargetHandle remoteHandle : getRemoteTargetHandles()) {
anim.play(ObjectAnimator.ofFloat(
remoteHandle.getTaskViewSimulator().taskPrimaryTranslation,
AnimatedFloat.VALUE,
@@ -4439,7 +4579,7 @@
properties));
}
}
- anim.play(ObjectAnimator.ofFloat(recentsView, OVERVIEW_PROGRESS, 1, 0));
+ anim.play(ObjectAnimator.ofFloat(this, TASK_THUMBNAIL_SPLASH_ALPHA, 0, 1));
return anim;
}
@@ -4495,23 +4635,21 @@
DepthController depthController = getDepthController();
if (depthController != null) {
- ObjectAnimator depthAnimator = ObjectAnimator.ofFloat(depthController, DEPTH,
- BACKGROUND_APP.getDepth(mActivity));
+ ObjectAnimator depthAnimator = ObjectAnimator.ofFloat(depthController.stateDepth,
+ MULTI_PROPERTY_VALUE, BACKGROUND_APP.getDepth(mActivity));
anim.play(depthAnimator);
}
- anim.play(ObjectAnimator.ofFloat(this, OVERVIEW_PROGRESS, 1f, 0f));
+ anim.play(ObjectAnimator.ofFloat(this, TASK_THUMBNAIL_SPLASH_ALPHA, 0f, 1f));
anim.play(progressAnim);
anim.setInterpolator(interpolator);
mPendingAnimation = new PendingAnimation(duration);
mPendingAnimation.add(anim);
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- runActionOnRemoteHandles(
- remoteTargetHandle -> remoteTargetHandle.getTaskViewSimulator()
- .addOverviewToAppAnim(mPendingAnimation, interpolator));
- mPendingAnimation.addOnFrameCallback(this::redrawLiveTile);
- }
+ runActionOnRemoteHandles(
+ remoteTargetHandle -> remoteTargetHandle.getTaskViewSimulator()
+ .addOverviewToAppAnim(mPendingAnimation, interpolator));
+ mPendingAnimation.addOnFrameCallback(this::redrawLiveTile);
mPendingAnimation.addEndListener(isSuccess -> {
if (isSuccess) {
if (tv.getTaskIds()[1] != -1 && mRemoteTargetHandles != null) {
@@ -4523,7 +4661,7 @@
dividerAnimator.end();
});
}
- if (ENABLE_QUICKSTEP_LIVE_TILE.get() && tv.isRunningTask()) {
+ if (tv.isRunningTask()) {
finishRecentsAnimation(false /* toRecents */, null);
onTaskLaunchAnimationEnd(true /* success */);
} else {
@@ -4542,6 +4680,16 @@
return mPendingAnimation;
}
+ protected void launchStagedTask() {
+ if (mSplitHiddenTaskView != null) {
+ // Split staging was started from an existing running task (in Overview)
+ mSplitHiddenTaskView.launchTask(success -> resetFromSplitSelectionState());
+ } else {
+ // Split staging was started from a new intent (from app menu in Home/AllApps)
+ mActivity.startActivity(mSplitSelectSource.intent);
+ }
+ }
+
protected void onTaskLaunchAnimationEnd(boolean success) {
if (success) {
resetTaskVisuals();
@@ -4696,7 +4844,7 @@
if (sendUserLeaveHint) {
// Notify the SysUI to use fade-in animation when entering PiP from live tile.
final SystemUiProxy systemUiProxy = SystemUiProxy.INSTANCE.get(getContext());
- systemUiProxy.notifySwipeToHomeFinished();
+ systemUiProxy.setPipAnimationTypeToAlpha();
systemUiProxy.setShelfHeight(true, mActivity.getDeviceProfile().hotseatBarSizePx);
// Transaction to hide the task to avoid flicker for entering PiP from split-screen.
// See also {@link AbsSwipeUpHandler#maybeFinishSwipeToHome}.
@@ -5139,17 +5287,8 @@
return null;
}
- @Override
- public void onSecondaryWindowBoundsChanged() {
- // Invalidate the task view size
- setInsets(mInsets);
- }
-
- /**
- * Enables or disables modal state for RecentsView
- * @param isModalState
- */
- public void setModalStateEnabled(boolean isModalState) { }
+ /** Enables or disables modal state for RecentsView */
+ public abstract void setModalStateEnabled(boolean isModalState, boolean animate);
public TaskOverlayFactory getTaskOverlayFactory() {
return mTaskOverlayFactory;
@@ -5393,6 +5532,11 @@
return mFirstFloatingTaskView;
}
+ @Nullable
+ public SplitInstructionsView getSplitInstructionsView() {
+ return mSplitInstructionsView;
+ }
+
/** Update the current activity locus id to show the enabled state of Overview */
public void updateLocusId() {
String locusId = "Overview";
diff --git a/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java b/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
index d0d715f..b0b111d 100644
--- a/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
+++ b/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
@@ -16,7 +16,7 @@
package com.android.quickstep.views;
-import static com.android.launcher3.util.DisplayController.NavigationMode.THREE_BUTTONS;
+import static com.android.launcher3.util.NavigationMode.THREE_BUTTONS;
import android.content.Context;
import android.util.AttributeSet;
@@ -29,6 +29,7 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.util.DisplayController;
@@ -43,20 +44,6 @@
private final StatefulActivity mLauncher;
private AppCompatTextView mTextView;
- public static final FloatProperty<SplitInstructionsView> CONTAINER_ALPHA =
- new FloatProperty<SplitInstructionsView>("SplitInstructionsContainerAlpha") {
- @Override
- public void setValue(SplitInstructionsView splitInstructionsView, float v) {
- splitInstructionsView.setVisibility(v != 0 ? VISIBLE : GONE);
- splitInstructionsView.setAlpha(v);
- }
-
- @Override
- public Float get(SplitInstructionsView splitInstructionsView) {
- return splitInstructionsView.getAlpha();
- }
- };
-
public static final FloatProperty<SplitInstructionsView> UNFOLD =
new FloatProperty<SplitInstructionsView>("SplitInstructionsUnfold") {
@Override
@@ -70,19 +57,6 @@
}
};
- public static final FloatProperty<SplitInstructionsView> TEXT_ALPHA =
- new FloatProperty<SplitInstructionsView>("SplitInstructionsTextAlpha") {
- @Override
- public void setValue(SplitInstructionsView splitInstructionsView, float v) {
- splitInstructionsView.mTextView.setAlpha(v);
- }
-
- @Override
- public Float get(SplitInstructionsView splitInstructionsView) {
- return splitInstructionsView.mTextView.getAlpha();
- }
- };
-
public SplitInstructionsView(Context context) {
this(context, null);
}
@@ -108,6 +82,11 @@
splitInstructionsView.mTextView = splitInstructionsView.findViewById(
R.id.split_instructions_text);
+ // Since textview overlays base view, and we sometimes manipulate the alpha of each
+ // simultaneously, force overlapping rendering to false prevents redrawing of pixels,
+ // improving performance at the cost of some accuracy.
+ splitInstructionsView.forceHasOverlappingRendering(false);
+
dragLayer.addView(splitInstructionsView);
return splitInstructionsView;
}
@@ -136,11 +115,13 @@
int getThreeButtonNavShift() {
DeviceProfile dp = mLauncher.getDeviceProfile();
if ((DisplayController.getNavigationMode(getContext()) == THREE_BUTTONS)
- && ((dp.isTwoPanels) || (dp.isTablet && !dp.isLandscape))) {
+ && ((dp.isTwoPanels) || (dp.isTablet && !dp.isLandscape))
+ // If taskbar is in overview, overview action has dedicated space above nav buttons
+ && !FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get()) {
int navButtonWidth = getResources().getDimensionPixelSize(
R.dimen.taskbar_nav_buttons_size);
int extraMargin = getResources().getDimensionPixelSize(
- R.dimen.taskbar_contextual_button_margin);
+ R.dimen.taskbar_split_instructions_margin);
// Explanation: The 3-button nav for non-phones sits on one side of the screen, taking
// up 3 buttons + a side margin worth of space. Our splitInstructionsView starts in the
// center of the screen and we want to center it in the remaining space, therefore we
@@ -151,4 +132,8 @@
return 0;
}
}
+
+ public AppCompatTextView getTextView() {
+ return mTextView;
+ }
}
diff --git a/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java b/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java
index ae6aae1..08004dc 100644
--- a/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java
+++ b/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java
@@ -22,7 +22,6 @@
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
-import android.util.FloatProperty;
import android.util.TypedValue;
import android.widget.FrameLayout;
@@ -33,33 +32,6 @@
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final Rect mTempRect = new Rect();
- public static final FloatProperty<SplitPlaceholderView> ALPHA_FLOAT =
- new FloatProperty<SplitPlaceholderView>("SplitViewAlpha") {
- @Override
- public void setValue(SplitPlaceholderView splitPlaceholderView, float v) {
- splitPlaceholderView.setVisibility(v != 0 ? VISIBLE : GONE);
- splitPlaceholderView.setAlpha(v);
- }
-
- @Override
- public Float get(SplitPlaceholderView splitPlaceholderView) {
- return splitPlaceholderView.getAlpha();
- }
- };
-
- public static final FloatProperty<SplitPlaceholderView> ICON_ALPHA =
- new FloatProperty<SplitPlaceholderView>("SplitViewIconAlpha") {
- @Override
- public void setValue(SplitPlaceholderView splitPlaceholderView, float v) {
- splitPlaceholderView.mIconView.setAlpha(v);
- }
-
- @Override
- public Float get(SplitPlaceholderView splitPlaceholderView) {
- return splitPlaceholderView.mIconView.getAlpha();
- }
- };
-
@Nullable
private IconView mIconView;
diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuView.java b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
index c1711d1..2c9afb4 100644
--- a/quickstep/src/com/android/quickstep/views/TaskMenuView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
@@ -16,8 +16,6 @@
package com.android.quickstep.views;
-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.quickstep.views.TaskThumbnailView.DIM_ALPHA;
import android.animation.Animator;
@@ -25,7 +23,6 @@
import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.Outline;
-import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.RectShape;
@@ -34,7 +31,6 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewOutlineProvider;
-import android.view.ViewTreeObserver.OnScrollChangedListener;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -58,13 +54,12 @@
/**
* Contains options for a recent task when long-pressing its icon.
*/
-public class TaskMenuView extends AbstractFloatingView implements OnScrollChangedListener {
+public class TaskMenuView extends AbstractFloatingView {
private static final Rect sTempRect = new Rect();
private static final int REVEAL_OPEN_DURATION = 150;
private static final int REVEAL_CLOSE_DURATION = 100;
- private final float mTaskInsetMargin;
private BaseDraggingActivity mActivity;
private TextView mTaskName;
@@ -83,7 +78,6 @@
mActivity = BaseDraggingActivity.fromContext(context);
setClipToOutline(true);
- mTaskInsetMargin = getResources().getDimension(R.dimen.task_card_margin);
}
@Override
@@ -131,50 +125,6 @@
};
}
- private void setPosition(float x, float y, int overscrollShift) {
- PagedOrientationHandler pagedOrientationHandler = mTaskView.getPagedOrientationHandler();
- // Inset due to margin
- PointF additionalInset = pagedOrientationHandler
- .getAdditionalInsetForTaskMenu(mTaskInsetMargin);
- DeviceProfile deviceProfile = mActivity.getDeviceProfile();
- int taskTopMargin = deviceProfile.overviewTaskThumbnailTopMarginPx;
-
- float adjustedY = y + taskTopMargin - additionalInset.y;
- float adjustedX = x - additionalInset.x;
- // Changing pivot to make computations easier
- // NOTE: Changing the pivots means the rotated view gets rotated about the new pivots set,
- // which would render the X and Y position set here incorrect
- setPivotX(0);
- if (deviceProfile.isTablet) {
- // In tablet, set pivotY to original position without mThumbnailTopMargin adjustment.
- setPivotY(-taskTopMargin);
- } else {
- setPivotY(0);
- }
- setRotation(pagedOrientationHandler.getDegreesRotated());
- setX(pagedOrientationHandler.getTaskMenuX(adjustedX,
- mTaskContainer.getThumbnailView(), overscrollShift, deviceProfile));
- setY(pagedOrientationHandler.getTaskMenuY(
- adjustedY, mTaskContainer.getThumbnailView(), overscrollShift));
-
- // TODO(b/193432925) temporary menu placement for split screen task menus
- TaskIdAttributeContainer[] taskIdAttributeContainers =
- mTaskView.getTaskIdAttributeContainers();
- if (taskIdAttributeContainers[0].getStagePosition() != STAGE_POSITION_UNDEFINED) {
- if (mTaskContainer.getStagePosition() != STAGE_POSITION_BOTTOM_OR_RIGHT) {
- return;
- }
- Rect r = new Rect();
- mTaskContainer.getThumbnailView().getBoundsOnScreen(r);
- if (deviceProfile.isLandscape) {
- setX(r.left);
- } else {
- setY(r.top);
-
- }
- }
- }
-
public void onRotationChanged() {
if (mOpenCloseAnimator != null && mOpenCloseAnimator.isRunning()) {
mOpenCloseAnimator.end();
@@ -206,17 +156,9 @@
return false;
}
post(this::animateOpen);
- ((RecentsView) mActivity.getOverviewPanel()).addOnScrollChangedListener(this);
return true;
}
- @Override
- public void onScrollChanged() {
- RecentsView rv = mActivity.getOverviewPanel();
- setPosition(mTaskView.getX() - rv.getScrollX(), mTaskView.getY() - rv.getScrollY(),
- rv.getOverScrollShift());
- }
-
/** @return true if successfully able to populate task view menu, false otherwise */
private boolean populateAndLayoutMenu() {
if (mTaskContainer.getTask().icon == null) {
@@ -253,18 +195,18 @@
RecentsView recentsView = mActivity.getOverviewPanel();
PagedOrientationHandler orientationHandler = recentsView.getPagedOrientationHandler();
measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
- orientationHandler.setTaskMenuAroundTaskView(this, mTaskInsetMargin);
// Get Position
DeviceProfile deviceProfile = mActivity.getDeviceProfile();
- mActivity.getDragLayer().getDescendantRectRelativeToSelf(mTaskView, sTempRect);
+ mActivity.getDragLayer().getDescendantRectRelativeToSelf(taskContainer.getThumbnailView(),
+ sTempRect);
Rect insets = mActivity.getDragLayer().getInsets();
BaseDragLayer.LayoutParams params = (BaseDragLayer.LayoutParams) getLayoutParams();
int padding = getResources()
.getDimensionPixelSize(R.dimen.task_menu_vertical_padding);
params.width = orientationHandler
.getTaskMenuWidth(taskContainer.getThumbnailView(),
- deviceProfile) - (2 * padding);
+ deviceProfile, taskContainer.getStagePosition()) - (2 * padding);
// Gravity set to Left instead of Start as sTempRect.left measures Left distance not Start
params.gravity = Gravity.LEFT;
setLayoutParams(params);
@@ -279,7 +221,22 @@
orientationHandler.setTaskOptionsMenuLayoutOrientation(
deviceProfile, mOptionLayout, dividerSpacing, divider);
- setPosition(sTempRect.left - insets.left, sTempRect.top - insets.top, 0);
+ float thumbnailAlignedX = sTempRect.left - insets.left;
+ float thumbnailAlignedY = sTempRect.top - insets.top;
+ // Changing pivot to make computations easier
+ // NOTE: Changing the pivots means the rotated view gets rotated about the new pivots set,
+ // which would render the X and Y position set here incorrect
+ setPivotX(0);
+ setPivotY(0);
+ setRotation(orientationHandler.getDegreesRotated());
+
+ // Margin that insets the menuView inside the taskView
+ float taskInsetMargin = getResources().getDimension(R.dimen.task_card_margin);
+ setTranslationX(orientationHandler.getTaskMenuX(thumbnailAlignedX,
+ mTaskContainer.getThumbnailView(), deviceProfile, taskInsetMargin));
+ setTranslationY(orientationHandler.getTaskMenuY(
+ thumbnailAlignedY, mTaskContainer.getThumbnailView(),
+ mTaskContainer.getStagePosition(), this, taskInsetMargin));
}
private void animateOpen() {
@@ -325,7 +282,6 @@
private void closeComplete() {
mIsOpen = false;
mActivity.getDragLayer().removeView(this);
- ((RecentsView) mActivity.getOverviewPanel()).removeOnScrollChangedListener(this);
}
private RoundedRectRevealOutlineProvider createOpenCloseOutlineProvider() {
diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt b/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt
index b586ac3..bdc0585 100644
--- a/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt
@@ -37,7 +37,6 @@
import com.android.launcher3.popup.RoundedArrowDrawable
import com.android.launcher3.popup.SystemShortcut
import com.android.launcher3.util.Themes
-import com.android.quickstep.KtR
import com.android.quickstep.TaskOverlayFactory
import com.android.quickstep.views.TaskView.TaskIdAttributeContainer
@@ -53,9 +52,9 @@
.fromContext<BaseDraggingActivity>(taskContainer.taskView.context)
val taskMenuViewWithArrow = activity.layoutInflater
.inflate(
- KtR.layout.task_menu_with_arrow,
- activity.dragLayer,
- false
+ R.layout.task_menu_with_arrow,
+ activity.dragLayer,
+ false
) as TaskMenuViewWithArrow<*>
return taskMenuViewWithArrow.populateAndShowForTask(taskContainer, alignSecondRow)
@@ -93,7 +92,7 @@
private var optionMeasuredHeight = 0
private val arrowHorizontalPadding: Int
get() = if (taskView.isFocusedTask)
- resources.getDimensionPixelSize(KtR.dimen.task_menu_horizontal_padding)
+ resources.getDimensionPixelSize(R.dimen.task_menu_horizontal_padding)
else
0
@@ -119,7 +118,7 @@
override fun onFinishInflate() {
super.onFinishInflate()
- optionLayout = findViewById(KtR.id.menu_option_layout)
+ optionLayout = findViewById(R.id.menu_option_layout)
}
private fun populateAndShowForTask(
@@ -170,7 +169,7 @@
// Add the spaces between items
val divider = ShapeDrawable(RectShape())
divider.paint.color = resources.getColor(android.R.color.transparent)
- val dividerSpacing = resources.getDimension(KtR.dimen.task_menu_spacing).toInt()
+ val dividerSpacing = resources.getDimension(R.dimen.task_menu_spacing).toInt()
optionLayout.showDividers = SHOW_DIVIDER_MIDDLE
// Set the orientation, which makes the menu show
@@ -187,7 +186,7 @@
private fun addMenuOption(menuOption: SystemShortcut<*>) {
val menuOptionView = mActivityContext.layoutInflater.inflate(
- KtR.layout.task_view_menu_option, this, false
+ R.layout.task_view_menu_option, this, false
) as LinearLayout
menuOption.setIconAndLabelFor(
menuOptionView.findViewById(R.id.icon),
diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
index f4b3d98..904c944 100644
--- a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -19,8 +19,8 @@
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
-import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_FULLSCREEN;
+import static com.android.systemui.shared.recents.utilities.PreviewPositionHelper.MAX_PCT_BEFORE_ASPECT_RATIOS_CONSIDERED_DIFFERENT;
+import static com.android.systemui.shared.recents.utilities.Utilities.isRelativePercentDifferenceGreaterThan;
import android.content.Context;
import android.graphics.Bitmap;
@@ -41,7 +41,6 @@
import android.util.AttributeSet;
import android.util.FloatProperty;
import android.util.Property;
-import android.view.Surface;
import android.view.View;
import android.widget.ImageView;
@@ -60,6 +59,7 @@
import com.android.quickstep.views.TaskView.FullscreenDrawParams;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.systemui.shared.recents.utilities.PreviewPositionHelper;
/**
* A task in the Recents view.
@@ -67,7 +67,6 @@
public class TaskThumbnailView extends View {
private static final MainThreadInitializedObject<FullscreenDrawParams> TEMP_PARAMS =
new MainThreadInitializedObject<>(FullscreenDrawParams::new);
- private static final float MAX_PCT_BEFORE_ASPECT_RATIOS_CONSIDERED_DIFFERENT = 0.1f;
public static final Property<TaskThumbnailView, Float> DIM_ALPHA =
new FloatProperty<TaskThumbnailView>("dimAlpha") {
@@ -320,13 +319,11 @@
public void drawOnCanvas(Canvas canvas, float x, float y, float width, float height,
float cornerRadius) {
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- if (mTask != null && getTaskView().isRunningTask() && !getTaskView().showScreenshot()) {
- canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius, mClearPaint);
- canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius,
- mDimmingPaintAfterClearing);
- return;
- }
+ if (mTask != null && getTaskView().isRunningTask() && !getTaskView().showScreenshot()) {
+ canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius, mClearPaint);
+ canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius,
+ mDimmingPaintAfterClearing);
+ return;
}
// Always draw the background since the snapshots might be translucent or partially empty
@@ -373,7 +370,7 @@
* <p>We want to show the splash if the aspect ratio or rotation of the thumbnail would be
* different from the task.
*/
- boolean shouldShowSplashView() {
+ public boolean shouldShowSplashView() {
return isThumbnailAspectRatioDifferentFromThumbnailData()
|| isThumbnailRotationDifferentFromTask();
}
@@ -421,7 +418,7 @@
float thumbnailDataAspect =
mThumbnailData.thumbnail.getWidth() / (float) mThumbnailData.thumbnail.getHeight();
- return Utilities.isRelativePercentDifferenceGreaterThan(thumbnailViewAspect,
+ return isRelativePercentDifferenceGreaterThan(thumbnailViewAspect,
thumbnailDataAspect, MAX_PCT_BEFORE_ASPECT_RATIOS_CONSIDERED_DIFFERENT);
}
@@ -445,8 +442,8 @@
*/
private void refreshOverlay() {
if (mOverlayEnabled) {
- getTaskOverlay().initOverlay(mTask, mThumbnailData, mPreviewPositionHelper.mMatrix,
- mPreviewPositionHelper.mIsOrientationChanged);
+ getTaskOverlay().initOverlay(mTask, mThumbnailData, mPreviewPositionHelper.getMatrix(),
+ mPreviewPositionHelper.isOrientationChanged());
} else {
getTaskOverlay().reset();
}
@@ -467,18 +464,19 @@
}
private void updateThumbnailMatrix() {
- mPreviewPositionHelper.mIsOrientationChanged = false;
+ mPreviewPositionHelper.setOrientationChanged(false);
if (mBitmapShader != null && mThumbnailData != null) {
mPreviewRect.set(0, 0, mThumbnailData.thumbnail.getWidth(),
mThumbnailData.thumbnail.getHeight());
int currentRotation = getTaskView().getRecentsView().getPagedViewOrientedState()
.getRecentsActivityRotation();
boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
+ DeviceProfile dp = mActivity.getDeviceProfile();
mPreviewPositionHelper.updateThumbnailMatrix(mPreviewRect, mThumbnailData,
- getMeasuredWidth(), getMeasuredHeight(), mActivity.getDeviceProfile(),
- currentRotation, isRtl);
+ getMeasuredWidth(), getMeasuredHeight(), dp.widthPx, dp.heightPx,
+ dp.taskbarSize, dp.isTablet, currentRotation, isRtl);
- mBitmapShader.setLocalMatrix(mPreviewPositionHelper.mMatrix);
+ mBitmapShader.setLocalMatrix(mPreviewPositionHelper.getMatrix());
mPaint.setShader(mBitmapShader);
}
getTaskView().updateCurrentFullscreenParams(mPreviewPositionHelper);
@@ -518,199 +516,4 @@
}
return mThumbnailData.isRealSnapshot && !mTask.isLocked;
}
-
- /**
- * Utility class to position the thumbnail in the TaskView
- */
- public static class PreviewPositionHelper {
-
- private static final RectF EMPTY_RECT_F = new RectF();
-
- // Contains the portion of the thumbnail that is unclipped when fullscreen progress = 1.
- private final RectF mClippedInsets = new RectF();
- private final Matrix mMatrix = new Matrix();
- private boolean mIsOrientationChanged;
-
- public Matrix getMatrix() {
- return mMatrix;
- }
-
- /**
- * Updates the matrix based on the provided parameters
- */
- public void updateThumbnailMatrix(Rect thumbnailBounds, ThumbnailData thumbnailData,
- int canvasWidth, int canvasHeight, DeviceProfile dp, int currentRotation,
- boolean isRtl) {
- boolean isRotated = false;
- boolean isOrientationDifferent;
-
- int thumbnailRotation = thumbnailData.rotation;
- int deltaRotate = getRotationDelta(currentRotation, thumbnailRotation);
- RectF thumbnailClipHint = new RectF();
- float canvasScreenRatio = canvasWidth / (float) dp.widthPx;
- float scaledTaskbarSize = dp.taskbarSize * canvasScreenRatio;
- thumbnailClipHint.bottom = dp.isTablet ? scaledTaskbarSize : 0;
-
- float scale = thumbnailData.scale;
- final float thumbnailScale;
-
- // Landscape vs portrait change.
- // Note: Disable rotation in grid layout.
- boolean windowingModeSupportsRotation =
- thumbnailData.windowingMode == WINDOWING_MODE_FULLSCREEN && !dp.isTablet;
- isOrientationDifferent = isOrientationChange(deltaRotate)
- && windowingModeSupportsRotation;
- if (canvasWidth == 0 || canvasHeight == 0 || scale == 0) {
- // If we haven't measured , skip the thumbnail drawing and only draw the background
- // color
- thumbnailScale = 0f;
- } else {
- // Rotate the screenshot if not in multi-window mode
- isRotated = deltaRotate > 0 && windowingModeSupportsRotation;
-
- float surfaceWidth = thumbnailBounds.width() / scale;
- float surfaceHeight = thumbnailBounds.height() / scale;
- float availableWidth = surfaceWidth
- - (thumbnailClipHint.left + thumbnailClipHint.right);
- float availableHeight = surfaceHeight
- - (thumbnailClipHint.top + thumbnailClipHint.bottom);
-
- float canvasAspect = canvasWidth / (float) canvasHeight;
- float availableAspect = isRotated
- ? availableHeight / availableWidth
- : availableWidth / availableHeight;
- boolean isAspectLargelyDifferent =
- Utilities.isRelativePercentDifferenceGreaterThan(canvasAspect,
- availableAspect, MAX_PCT_BEFORE_ASPECT_RATIOS_CONSIDERED_DIFFERENT);
- if (isRotated && isAspectLargelyDifferent) {
- // Do not rotate thumbnail if it would not improve fit
- isRotated = false;
- isOrientationDifferent = false;
- }
-
- if (isAspectLargelyDifferent) {
- // Crop letterbox insets if insets isn't already clipped
- thumbnailClipHint.left = thumbnailData.letterboxInsets.left;
- thumbnailClipHint.right = thumbnailData.letterboxInsets.right;
- thumbnailClipHint.top = thumbnailData.letterboxInsets.top;
- thumbnailClipHint.bottom = thumbnailData.letterboxInsets.bottom;
- availableWidth = surfaceWidth
- - (thumbnailClipHint.left + thumbnailClipHint.right);
- availableHeight = surfaceHeight
- - (thumbnailClipHint.top + thumbnailClipHint.bottom);
- }
-
- final float targetW, targetH;
- if (isOrientationDifferent) {
- targetW = canvasHeight;
- targetH = canvasWidth;
- } else {
- targetW = canvasWidth;
- targetH = canvasHeight;
- }
- float targetAspect = targetW / targetH;
-
- // Update the clipHint such that
- // > the final clipped position has same aspect ratio as requested by canvas
- // > first fit the width and crop the extra height
- // > if that will leave empty space, fit the height and crop the width instead
- float croppedWidth = availableWidth;
- float croppedHeight = croppedWidth / targetAspect;
- if (croppedHeight > availableHeight) {
- croppedHeight = availableHeight;
- if (croppedHeight < targetH) {
- croppedHeight = Math.min(targetH, surfaceHeight);
- }
- croppedWidth = croppedHeight * targetAspect;
-
- // One last check in case the task aspect radio messed up something
- if (croppedWidth > surfaceWidth) {
- croppedWidth = surfaceWidth;
- croppedHeight = croppedWidth / targetAspect;
- }
- }
-
- // Update the clip hints. Align to 0,0, crop the remaining.
- if (isRtl) {
- thumbnailClipHint.left += availableWidth - croppedWidth;
- if (thumbnailClipHint.right < 0) {
- thumbnailClipHint.left += thumbnailClipHint.right;
- thumbnailClipHint.right = 0;
- }
- } else {
- thumbnailClipHint.right += availableWidth - croppedWidth;
- if (thumbnailClipHint.left < 0) {
- thumbnailClipHint.right += thumbnailClipHint.left;
- thumbnailClipHint.left = 0;
- }
- }
- thumbnailClipHint.bottom += availableHeight - croppedHeight;
- if (thumbnailClipHint.top < 0) {
- thumbnailClipHint.bottom += thumbnailClipHint.top;
- thumbnailClipHint.top = 0;
- } else if (thumbnailClipHint.bottom < 0) {
- thumbnailClipHint.top += thumbnailClipHint.bottom;
- thumbnailClipHint.bottom = 0;
- }
-
- thumbnailScale = targetW / (croppedWidth * scale);
- }
-
- if (!isRotated) {
- mMatrix.setTranslate(
- -thumbnailClipHint.left * scale,
- -thumbnailClipHint.top * scale);
- } else {
- setThumbnailRotation(deltaRotate, thumbnailBounds);
- }
-
- mClippedInsets.set(0, 0, 0, scaledTaskbarSize);
-
- mMatrix.postScale(thumbnailScale, thumbnailScale);
- mIsOrientationChanged = isOrientationDifferent;
- }
-
- private int getRotationDelta(int oldRotation, int newRotation) {
- int delta = newRotation - oldRotation;
- if (delta < 0) delta += 4;
- return delta;
- }
-
- /**
- * @param deltaRotation the number of 90 degree turns from the current orientation
- * @return {@code true} if the change in rotation results in a shift from landscape to
- * portrait or vice versa, {@code false} otherwise
- */
- private boolean isOrientationChange(int deltaRotation) {
- return deltaRotation == Surface.ROTATION_90 || deltaRotation == Surface.ROTATION_270;
- }
-
- private void setThumbnailRotation(int deltaRotate, Rect thumbnailPosition) {
- float translateX = 0;
- float translateY = 0;
-
- mMatrix.setRotate(90 * deltaRotate);
- switch (deltaRotate) { /* Counter-clockwise */
- case Surface.ROTATION_90:
- translateX = thumbnailPosition.height();
- break;
- case Surface.ROTATION_270:
- translateY = thumbnailPosition.width();
- break;
- case Surface.ROTATION_180:
- translateX = thumbnailPosition.width();
- translateY = thumbnailPosition.height();
- break;
- }
- mMatrix.postTranslate(translateX, translateY);
- }
-
- /**
- * Insets to used for clipping the thumbnail (in case it is drawing outside its own space)
- */
- public RectF getInsetsToDrawInFullscreen(DeviceProfile dp) {
- return dp.isTaskbarPresent && !dp.isTaskbarPresentInApps
- ? mClippedInsets : EMPTY_RECT_F;
- }
- }
}
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index d2c2988..4de69cd 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -18,20 +18,19 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.widget.Toast.LENGTH_SHORT;
-import static android.window.SplashScreen.SPLASH_SCREEN_STYLE_SOLID_COLOR;
-import static com.android.launcher3.Utilities.comp;
+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.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
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;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
+import static com.android.launcher3.util.SplitConfigurationOptions.getLogEventForPosition;
import static java.lang.annotation.RetentionPolicy.SOURCE;
@@ -48,11 +47,13 @@
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
+import android.os.Handler;
import android.util.AttributeSet;
import android.util.FloatProperty;
import android.util.Log;
import android.view.Display;
import android.view.MotionEvent;
+import android.view.RemoteAnimationTarget;
import android.view.TouchDelegate;
import android.view.View;
import android.view.ViewGroup;
@@ -78,6 +79,7 @@
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.util.ActivityOptionsWrapper;
import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.RunnableList;
import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
@@ -93,15 +95,14 @@
import com.android.quickstep.TaskViewUtils;
import com.android.quickstep.util.CancellableTask;
import com.android.quickstep.util.RecentsOrientedState;
+import com.android.quickstep.util.SplitSelectStateController;
import com.android.quickstep.util.TaskCornerRadius;
import com.android.quickstep.util.TransformParams;
-import com.android.quickstep.views.TaskThumbnailView.PreviewPositionHelper;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.systemui.shared.recents.utilities.PreviewPositionHelper;
import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.ActivityOptionsCompat;
import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.lang.annotation.Retention;
import java.util.Arrays;
@@ -119,6 +120,8 @@
private static final String TAG = TaskView.class.getSimpleName();
private static final boolean DEBUG = false;
+ private static final RectF EMPTY_RECT_F = new RectF();
+
public static final int FLAG_UPDATE_ICON = 1;
public static final int FLAG_UPDATE_THUMBNAIL = FLAG_UPDATE_ICON << 1;
@@ -132,6 +135,17 @@
@IntDef({FLAG_UPDATE_ALL, FLAG_UPDATE_ICON, FLAG_UPDATE_THUMBNAIL})
public @interface TaskDataChanges {}
+ /**
+ * Type of task view
+ */
+ @Retention(SOURCE)
+ @IntDef({Type.SINGLE, Type.GROUPED, Type.DESKTOP})
+ public @interface Type {
+ int SINGLE = 1;
+ int GROUPED = 2;
+ int DESKTOP = 3;
+ }
+
/** The maximum amount that a task view can be scrimmed, dimmed or tinted. */
public static final float MAX_PAGE_SCRIM_ALPHA = 0.4f;
@@ -158,7 +172,7 @@
new FloatProperty<TaskView>("focusTransition") {
@Override
public void setValue(TaskView taskView, float v) {
- taskView.setIconAndDimTransitionProgress(v, false /* invert */);
+ taskView.setIconsAndBannersTransitionProgress(v, false /* invert */);
}
@Override
@@ -323,27 +337,14 @@
}
};
- public static final FloatProperty<TaskView> ICON_ALPHA =
- new FloatProperty<TaskView>("iconAlpha") {
- @Override
- public void setValue(TaskView taskView, float v) {
- taskView.mIconView.setAlpha(v);
- }
-
- @Override
- public Float get(TaskView taskView) {
- return taskView.mIconView.getAlpha();
- }
- };
-
@Nullable
protected Task mTask;
protected TaskThumbnailView mSnapshotView;
protected IconView mIconView;
protected final DigitalWellBeingToast mDigitalWellBeingToast;
- private float mFullscreenProgress;
+ protected float mFullscreenProgress;
private float mGridProgress;
- protected float mOverviewProgress;
+ protected float mTaskThumbnailSplashAlpha;
private float mNonGridScale = 1;
private float mDismissScale = 1;
protected final FullscreenDrawParams mCurrentFullscreenParams;
@@ -383,8 +384,8 @@
/**
* Index 0 will contain taskID of left/top task, index 1 will contain taskId of bottom/right
*/
- protected final int[] mTaskIdContainer = new int[]{-1, -1};
- protected final TaskIdAttributeContainer[] mTaskIdAttributeContainer =
+ protected int[] mTaskIdContainer = new int[]{-1, -1};
+ protected TaskIdAttributeContainer[] mTaskIdAttributeContainer =
new TaskIdAttributeContainer[2];
private boolean mShowScreenshot;
@@ -399,7 +400,7 @@
private final float[] mIconCenterCoords = new float[2];
- private final PointF mLastTouchDownPosition = new PointF();
+ protected final PointF mLastTouchDownPosition = new PointF();
private boolean mIsClickableAsLiveTile = true;
@@ -499,7 +500,7 @@
return;
}
mModalness = modalness;
- mIconView.setAlpha(comp(modalness));
+ mIconView.setAlpha(1 - modalness);
mDigitalWellBeingToast.updateBannerOffset(modalness,
mCurrentFullscreenParams.mCurrentDrawnInsets.top
+ mCurrentFullscreenParams.mCurrentDrawnInsets.bottom);
@@ -535,6 +536,13 @@
}
/**
+ * Check if given {@code taskId} is tracked in this view
+ */
+ public boolean containsTaskId(int taskId) {
+ return mTask != null && mTask.key.id == taskId;
+ }
+
+ /**
* @return integer array of two elements to be size consistent with max number of tasks possible
* index 0 will contain the taskId, index 1 will be -1 indicating a null taskID value
*/
@@ -573,6 +581,18 @@
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
+ RecentsView recentsView = getRecentsView();
+ if (recentsView == null || getTask() == null) {
+ return false;
+ }
+ SplitSelectStateController splitSelectStateController =
+ recentsView.getSplitSelectController();
+ if (splitSelectStateController.isSplitSelectActive() &&
+ splitSelectStateController.getInitialTaskId() == getTask().key.id) {
+ // Prevent taps on the this taskview if it's being animated into split select state
+ return false;
+ }
+
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
mLastTouchDownPosition.set(ev.getX(), ev.getY());
}
@@ -595,17 +615,21 @@
* @return {@code true} if user is already in split select mode and this tap was to choose the
* second app. {@code false} otherwise
*/
- private boolean confirmSecondSplitSelectApp() {
- int index = getChildTaskIndexAtPosition(mLastTouchDownPosition);
+ protected boolean confirmSecondSplitSelectApp() {
+ int index = getLastSelectedChildTaskIndex();
TaskIdAttributeContainer container = mTaskIdAttributeContainer[index];
- return getRecentsView().confirmSplitSelect(this, container.getTask(),
- container.getIconView(), container.getThumbnailView());
+ if (container != null) {
+ return getRecentsView().confirmSplitSelect(this, container.getTask(),
+ container.getIconView().getDrawable(), container.getThumbnailView(),
+ container.getThumbnailView().getThumbnail(), /* intent */ null);
+ }
+ return false;
}
/**
- * Returns the task under the given position in the local coordinates of this task view.
+ * Returns the task index of the last selected child task (0 or 1).
*/
- protected int getChildTaskIndexAtPosition(PointF position) {
+ protected int getLastSelectedChildTaskIndex() {
return 0;
}
@@ -624,7 +648,7 @@
if (ActivityManagerWrapper.getInstance()
.startActivityFromRecents(mTask.key, opts.options)) {
RecentsView recentsView = getRecentsView();
- if (ENABLE_QUICKSTEP_LIVE_TILE.get() && recentsView.getRunningTaskViewId() != -1) {
+ if (recentsView.getRunningTaskViewId() != -1) {
recentsView.onTaskLaunchedInLiveTileMode();
// Return a fresh callback in the live tile case, so that it's not accidentally
@@ -659,16 +683,14 @@
TestProtocol.SEQUENCE_MAIN, "startActivityFromRecentsAsync", mTask);
// Indicate success once the system has indicated that the transition has started
- ActivityOptions opts = ActivityOptionsCompat.makeCustomAnimation(
- getContext(), 0, 0, () -> callback.accept(true), MAIN_EXECUTOR.getHandler());
+ ActivityOptions opts = makeCustomAnimation(getContext(), 0, 0,
+ () -> callback.accept(true), MAIN_EXECUTOR.getHandler());
opts.setLaunchDisplayId(
getDisplay() == null ? DEFAULT_DISPLAY : getDisplay().getDisplayId());
if (freezeTaskList) {
- ActivityOptionsCompat.setFreezeRecentTasksList(opts);
+ opts.setFreezeRecentTasksReordering();
}
- // TODO(b/202826469): Replace setSplashScreenStyle with setDisableStartingWindow.
- opts.setSplashScreenStyle(mSnapshotView.shouldShowSplashView()
- ? SPLASH_SCREEN_STYLE_SOLID_COLOR : opts.getSplashScreenStyle());
+ opts.setDisableStartingWindow(mSnapshotView.shouldShowSplashView());
Task.TaskKey key = mTask.key;
UI_HELPER_EXECUTOR.execute(() -> {
if (!ActivityManagerWrapper.getInstance().startActivityFromRecents(key, opts)) {
@@ -687,14 +709,30 @@
}
/**
+ * Returns ActivityOptions for overriding task transition animation.
+ */
+ private ActivityOptions makeCustomAnimation(Context context, int enterResId,
+ int exitResId, final Runnable callback, final Handler callbackHandler) {
+ return ActivityOptions.makeCustomTaskAnimation(context, enterResId, exitResId,
+ callbackHandler,
+ elapsedRealTime -> {
+ if (callback != null) {
+ callbackHandler.post(callback);
+ }
+ }, null /* finishedListener */);
+ }
+
+ /**
* Launch of the current task (both live and inactive tasks) with an animation.
*/
- public void launchTasks() {
+ @Nullable
+ public RunnableList launchTasks() {
RecentsView recentsView = getRecentsView();
RemoteTargetHandle[] remoteTargetHandles = recentsView.mRemoteTargetHandles;
- if (ENABLE_QUICKSTEP_LIVE_TILE.get() && isRunningTask() && remoteTargetHandles != null) {
+ if (isRunningTask() && remoteTargetHandles != null) {
if (!mIsClickableAsLiveTile) {
- return;
+ Log.e(TAG, "TaskView is not clickable as a live tile; returning to home.");
+ return null;
}
mIsClickableAsLiveTile = false;
@@ -704,14 +742,14 @@
} else {
TransformParams topLeftParams = remoteTargetHandles[0].getTransformParams();
TransformParams rightBottomParams = remoteTargetHandles[1].getTransformParams();
- RemoteAnimationTargetCompat[] apps = Stream.concat(
+ RemoteAnimationTarget[] apps = Stream.concat(
Arrays.stream(topLeftParams.getTargetSet().apps),
Arrays.stream(rightBottomParams.getTargetSet().apps))
- .toArray(RemoteAnimationTargetCompat[]::new);
- RemoteAnimationTargetCompat[] wallpapers = Stream.concat(
+ .toArray(RemoteAnimationTarget[]::new);
+ RemoteAnimationTarget[] wallpapers = Stream.concat(
Arrays.stream(topLeftParams.getTargetSet().wallpapers),
Arrays.stream(rightBottomParams.getTargetSet().wallpapers))
- .toArray(RemoteAnimationTargetCompat[]::new);
+ .toArray(RemoteAnimationTarget[]::new);
targets = new RemoteAnimationTargets(apps, wallpapers,
topLeftParams.getTargetSet().nonApps,
topLeftParams.getTargetSet().targetMode);
@@ -719,11 +757,16 @@
if (targets == null) {
// If the recents animation is cancelled somehow between the parent if block and
// here, try to launch the task as a non live tile task.
- launchTaskAnimated();
+ RunnableList runnableList = launchTaskAnimated();
+ if (runnableList == null) {
+ Log.e(TAG, "Recents animation cancelled and cannot launch task as non-live tile"
+ + "; returning to home");
+ }
mIsClickableAsLiveTile = true;
- return;
+ return runnableList;
}
+ RunnableList runnableList = new RunnableList();
AnimatorSet anim = new AnimatorSet();
TaskViewUtils.composeRecentsLaunchAnimator(
anim, this, targets.apps,
@@ -746,12 +789,23 @@
launchTaskAnimated();
}
mIsClickableAsLiveTile = true;
+ runEndCallback();
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ runEndCallback();
+ }
+
+ private void runEndCallback() {
+ runnableList.executeAllAndDestroy();
}
});
anim.start();
recentsView.onTaskLaunchedInLiveTileMode();
+ return runnableList;
} else {
- launchTaskAnimated();
+ return launchTaskAnimated();
}
}
@@ -907,7 +961,11 @@
return deviceProfile.isTablet && !isFocusedTask();
}
- protected void setIconAndDimTransitionProgress(float progress, boolean invert) {
+ /**
+ * Called to animate a smooth transition when going directly from an app into Overview (and
+ * vice versa). Icons fade in, and DWB banners slide in with a "shift up" animation.
+ */
+ protected void setIconsAndBannersTransitionProgress(float progress, boolean invert) {
if (invert) {
progress = 1 - progress;
}
@@ -951,7 +1009,7 @@
if (mIconAndDimAnimator != null) {
mIconAndDimAnimator.cancel();
}
- setIconAndDimTransitionProgress(iconScale, invert);
+ setIconsAndBannersTransitionProgress(iconScale, invert);
}
protected void resetPersistentViewTransforms() {
@@ -1069,18 +1127,15 @@
}
/**
- * Updates progress of task view for entering/exiting overview on swipe up/down.
- *
- * <p>Updates the alpha of any splash screen over the thumbnail if it exists.
+ * Updates alpha of task thumbnail splash on swipe up/down.
*/
- public void setOverviewProgress(float overviewProgress) {
- mOverviewProgress = overviewProgress;
+ public void setTaskThumbnailSplashAlpha(float taskThumbnailSplashAlpha) {
+ mTaskThumbnailSplashAlpha = taskThumbnailSplashAlpha;
applyThumbnailSplashAlpha();
}
protected void applyThumbnailSplashAlpha() {
- mSnapshotView.setSplashAlpha(
- Utilities.mapToRange(mOverviewProgress, 0f, 1f, 1f, 0f, LINEAR));
+ mSnapshotView.setSplashAlpha(mTaskThumbnailSplashAlpha);
}
private void setSplitSelectTranslationX(float x) {
@@ -1231,7 +1286,7 @@
DISMISS_TRANSLATION_X, DISMISS_TRANSLATION_Y);
}
- public FloatProperty<TaskView> getSecondaryDissmissTranslationProperty() {
+ public FloatProperty<TaskView> getSecondaryDismissTranslationProperty() {
return getPagedOrientationHandler().getSecondaryValue(
DISMISS_TRANSLATION_X, DISMISS_TRANSLATION_Y);
}
@@ -1374,6 +1429,12 @@
mIconView.setVisibility(progress < 1 ? VISIBLE : INVISIBLE);
mSnapshotView.getTaskOverlay().setFullscreenProgress(progress);
+ // Animate icons and DWB banners in/out, except in QuickSwitch state, when tiles are
+ // oversized and banner would look disproportionately large.
+ if (mActivity.getStateManager().getState() != BACKGROUND_APP) {
+ setIconsAndBannersTransitionProgress(progress, true);
+ }
+
updateSnapshotRadius();
}
@@ -1486,7 +1547,8 @@
}
public void initiateSplitSelect(SplitPositionOption splitPositionOption) {
- getRecentsView().initiateSplitSelect(this, splitPositionOption.stagePosition);
+ getRecentsView().initiateSplitSelect(this, splitPositionOption.stagePosition,
+ getLogEventForPosition(splitPositionOption.stagePosition));
}
/**
@@ -1505,6 +1567,19 @@
}
/**
+ * Sets visibility for the thumbnail and associated elements (DWB banners and action chips).
+ * IconView is unaffected.
+ */
+ void setThumbnailVisibility(int visibility) {
+ for (int i = 0; i < getChildCount(); i++) {
+ View child = getChildAt(i);
+ if (child != mIconView) {
+ child.setVisibility(visibility);
+ }
+ }
+ }
+
+ /**
* We update and subsequently draw these in {@link #setFullscreenProgress(float)}.
*/
public static class FullscreenDrawParams {
@@ -1517,9 +1592,12 @@
/** The current scale we apply to the thumbnail to adjust for new left/right insets. */
public float mScale = 1;
+ private boolean mIsTaskbarTransient;
+
public FullscreenDrawParams(Context context) {
mCornerRadius = TaskCornerRadius.get(context);
mWindowCornerRadius = QuickStepContract.getWindowCornerRadius(context);
+ mIsTaskbarTransient = DisplayController.isTransientTaskbar(context);
mCurrentDrawnCornerRadius = mCornerRadius;
}
@@ -1529,7 +1607,7 @@
*/
public void setProgress(float fullscreenProgress, float parentScale, float taskViewScale,
int previewWidth, DeviceProfile dp, PreviewPositionHelper pph) {
- RectF insets = pph.getInsetsToDrawInFullscreen(dp);
+ RectF insets = getInsetsToDrawInFullscreen(pph, dp, mIsTaskbarTransient);
float currentInsetsLeft = insets.left * fullscreenProgress;
float currentInsetsTop = insets.top * fullscreenProgress;
@@ -1548,6 +1626,18 @@
mScale = previewWidth / (previewWidth + currentInsetsLeft + currentInsetsRight);
}
}
+
+ /**
+ * Insets to used for clipping the thumbnail (in case it is drawing outside its own space)
+ */
+ private static RectF getInsetsToDrawInFullscreen(PreviewPositionHelper pph,
+ DeviceProfile dp, boolean isTaskbarTransient) {
+ if (isTaskbarTransient) {
+ return pph.getClippedInsets();
+ }
+ return dp.isTaskbarPresent && !dp.isTaskbarPresentInApps
+ ? pph.getClippedInsets() : EMPTY_RECT_F;
+ }
}
public class TaskIdAttributeContainer {
diff --git a/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java b/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
index c1b3beb..83341cb 100644
--- a/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
+++ b/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
@@ -36,13 +36,11 @@
import android.content.ComponentName;
import android.os.UserHandle;
import android.text.TextUtils;
-import android.util.Log;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.ComponentWithLabel;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.model.BgDataModel.FixedContainerItems;
@@ -111,7 +109,6 @@
doReturn(allWidgets).when(manager).getInstalledProvidersForProfile(eq(myUserHandle()));
doAnswer(i -> {
String pkg = i.getArgument(0);
- Log.e("Hello", "Getting v " + pkg);
return TextUtils.isEmpty(pkg) ? allWidgets : allWidgets.stream()
.filter(a -> pkg.equals(a.provider.getPackageName()))
.collect(Collectors.toList());
@@ -138,21 +135,21 @@
public void widgetsRecommendationRan_shouldOnlyReturnNotAddedWidgetsInAppPredictionOrder()
throws Exception {
// WHEN newPredicationTask is executed with app predication of 5 apps.
- AppTarget app1 = new AppTarget(new AppTargetId("app1"), "app1", "className",
+ AppTarget app1 = new AppTarget(new AppTargetId("app1"), "app1", "provider1",
mUserHandle);
- AppTarget app2 = new AppTarget(new AppTargetId("app2"), "app2", "className",
+ 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", "className",
+ AppTarget app4 = new AppTarget(new AppTargetId("app4"), "app4", "provider1",
mUserHandle);
- AppTarget app5 = new AppTarget(new AppTargetId("app5"), "app5", "className",
+ AppTarget app5 = new AppTarget(new AppTargetId("app5"), "app5", "provider1",
mUserHandle);
mModelHelper.executeTaskForTest(
newWidgetsPredicationTask(List.of(app5, app3, app2, app4, app1)))
.forEach(Runnable::run);
- // THEN only 3 widgets are returned because
+ // 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.
@@ -161,45 +158,39 @@
.stream()
.map(itemInfo -> (PendingAddWidgetInfo) itemInfo)
.collect(Collectors.toList());
- assertThat(recommendedWidgets).hasSize(3);
+ assertThat(recommendedWidgets).hasSize(2);
assertWidgetInfo(recommendedWidgets.get(0).info, mApp2Provider1);
- assertWidgetInfo(recommendedWidgets.get(1).info, mApp4Provider2);
- assertWidgetInfo(recommendedWidgets.get(2).info, mApp1Provider1);
+ assertWidgetInfo(recommendedWidgets.get(1).info, mApp1Provider1);
}
@Test
- public void widgetsRecommendationRan_localFilterDisabled_shouldReturnWidgetsInPredicationOrder()
+ public void widgetsRecommendationRan_shouldReturnPackageWidgetsWhenEmpty()
throws Exception {
- if (FeatureFlags.ENABLE_LOCAL_RECOMMENDED_WIDGETS_FILTER.get()) {
- return;
- }
- // WHEN newPredicationTask is executed with 5 predicated widgets.
- AppTarget widget1 = new AppTarget(new AppTargetId("app1"), "app1", "provider1",
- mUserHandle);
- AppTarget widget2 = new AppTarget(new AppTargetId("app1"), "app1", "provider2",
+ // 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);
- // Not installed widget
- AppTarget widget4 = new AppTarget(new AppTargetId("app4"), "app4", "provider3",
+ // 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, widget2, widget4, widget1)))
+ newWidgetsPredicationTask(List.of(widget5, widget3, widget4, widget1)))
.forEach(Runnable::run);
- // THEN only 3 widgets are returned because the launcher only filters out non-exist widgets.
+ // 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(3);
- assertWidgetInfo(recommendedWidgets.get(0).info, mApp5Provider1);
- assertWidgetInfo(recommendedWidgets.get(1).info, mApp1Provider2);
- assertWidgetInfo(recommendedWidgets.get(2).info, mApp1Provider1);
+ assertThat(recommendedWidgets).hasSize(2);
+ // Another widget from the same package
+ assertWidgetInfo(recommendedWidgets.get(0).info, mApp4Provider2);
+ assertWidgetInfo(recommendedWidgets.get(1).info, mApp1Provider1);
}
private void assertWidgetInfo(
diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactoryTest.kt b/quickstep/tests/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactoryTest.kt
new file mode 100644
index 0000000..58f0949
--- /dev/null
+++ b/quickstep/tests/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactoryTest.kt
@@ -0,0 +1,148 @@
+package com.android.launcher3.taskbar.navbutton
+
+import android.content.res.Resources
+import android.view.View
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import android.widget.ImageView
+import android.widget.LinearLayout
+import androidx.test.runner.AndroidJUnit4
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.taskbar.TaskbarManager
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import com.android.launcher3.R
+import org.junit.Assume.assumeTrue
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+import java.lang.IllegalStateException
+
+@RunWith(AndroidJUnit4::class)
+class NavButtonLayoutFactoryTest {
+
+ @Mock
+ lateinit var mockDeviceProfile: DeviceProfile
+ @Mock
+ lateinit var mockParentButtonContainer: FrameLayout
+ @Mock
+ lateinit var mockNavLayout: LinearLayout
+ @Mock
+ lateinit var mockStartContextualLayout: ViewGroup
+ @Mock
+ lateinit var mockEndContextualLayout: ViewGroup
+ @Mock
+ lateinit var mockResources: Resources
+ @Mock
+ lateinit var mockBackButton: ImageView
+ @Mock
+ lateinit var mockRecentsButton: ImageView
+ @Mock
+ lateinit var mockHomeButton: ImageView
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+
+ // Init end nav buttons
+ whenever(mockNavLayout.childCount).thenReturn(3)
+ whenever(mockNavLayout.findViewById<View>(R.id.back)).thenReturn(mockBackButton)
+ whenever(mockNavLayout.findViewById<View>(R.id.home)).thenReturn(mockHomeButton)
+ whenever(mockNavLayout.findViewById<View>(R.id.recent_apps)).thenReturn(mockRecentsButton)
+
+ // Init top level layout
+ whenever(mockParentButtonContainer.findViewById<LinearLayout>(R.id.end_nav_buttons))
+ .thenReturn(mockNavLayout)
+ whenever(mockParentButtonContainer.findViewById<ViewGroup>(R.id.end_contextual_buttons))
+ .thenReturn(mockEndContextualLayout)
+ whenever(mockParentButtonContainer.findViewById<ViewGroup>(R.id.start_contextual_buttons))
+ .thenReturn(mockStartContextualLayout)
+ }
+
+ @Test
+ fun getKidsLayoutter() {
+ assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW)
+ mockDeviceProfile.isTaskbarPresent = true
+ val layoutter: NavButtonLayoutFactory.NavButtonLayoutter =
+ getLayoutter(isKidsMode = true, isInSetup = false, isThreeButtonNav = false,
+ phoneMode = false)
+ assert(layoutter is KidsNavLayoutter)
+ }
+
+ @Test
+ fun getSetupLayoutter() {
+ assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW)
+ mockDeviceProfile.isTaskbarPresent = true
+ val layoutter: NavButtonLayoutFactory.NavButtonLayoutter =
+ getLayoutter(isKidsMode = false, isInSetup = true, isThreeButtonNav = false,
+ phoneMode = false)
+ assert(layoutter is SetupNavLayoutter)
+ }
+
+ @Test
+ fun getTaskbarNavLayoutter() {
+ assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW)
+ mockDeviceProfile.isTaskbarPresent = true
+ val layoutter: NavButtonLayoutFactory.NavButtonLayoutter =
+ getLayoutter(isKidsMode = false, isInSetup = false, isThreeButtonNav = false,
+ phoneMode = false)
+ assert(layoutter is TaskbarNavLayoutter)
+ }
+
+ @Test(expected = IllegalStateException::class)
+ fun noValidLayoutForLargeScreenTaskbarNotPresent() {
+ assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW)
+ mockDeviceProfile.isTaskbarPresent = false
+ getLayoutter(isKidsMode = false, isInSetup = false, isThreeButtonNav = false,
+ phoneMode = false)
+ }
+
+ @Test
+ fun getTaskbarPortraitLayoutter() {
+ assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW)
+ mockDeviceProfile.isTaskbarPresent = false
+ val layoutter: NavButtonLayoutFactory.NavButtonLayoutter =
+ getLayoutter(isKidsMode = false, isInSetup = false, isThreeButtonNav = true,
+ phoneMode = true)
+ assert(layoutter is PhonePortraitNavLayoutter)
+ }
+
+ @Test
+ fun getTaskbarLandscapeLayoutter() {
+ assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW)
+ mockDeviceProfile.isTaskbarPresent = false
+ setDeviceProfileLandscape()
+ val layoutter: NavButtonLayoutFactory.NavButtonLayoutter =
+ getLayoutter(isKidsMode = false, isInSetup = false, isThreeButtonNav = true,
+ phoneMode = true)
+ assert(layoutter is PhoneLandscapeNavLayoutter)
+ }
+
+ @Test(expected = IllegalStateException::class)
+ fun noValidLayoutForPhoneGestureNav() {
+ assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW)
+ mockDeviceProfile.isTaskbarPresent = false
+ getLayoutter(isKidsMode = false, isInSetup = false, isThreeButtonNav = false,
+ phoneMode = true)
+ }
+
+ private fun setDeviceProfileLandscape() {
+ // Use reflection to modify landscape field
+ val landscapeField = mockDeviceProfile.javaClass.getDeclaredField("isLandscape")
+ landscapeField.isAccessible = true
+ landscapeField.set(mockDeviceProfile, true)
+ }
+
+ private fun getLayoutter(isKidsMode: Boolean, isInSetup: Boolean,
+ isThreeButtonNav: Boolean, phoneMode: Boolean):
+ NavButtonLayoutFactory.NavButtonLayoutter {
+ return NavButtonLayoutFactory.getUiLayoutter(
+ deviceProfile = mockDeviceProfile,
+ navButtonsView = mockParentButtonContainer,
+ resources = mockResources,
+ isKidsMode = isKidsMode, isInSetup = isInSetup,
+ isThreeButtonNav = isThreeButtonNav, phoneMode = phoneMode
+ )
+ }
+}
\ No newline at end of file
diff --git a/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java b/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java
index f190e27..2c5825f 100644
--- a/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java
+++ b/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java
@@ -16,8 +16,6 @@
package com.android.quickstep;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
-
import static org.junit.Assert.assertTrue;
import android.os.SystemProperties;
@@ -41,6 +39,7 @@
protected TestRule getRulesInsideActivityMonitor() {
return RuleChain.
outerRule(new NavigationModeSwitchRule(mLauncher)).
+ around(new TaskbarModeSwitchRule(mLauncher)).
around(super.getRulesInsideActivityMonitor());
}
@@ -79,8 +78,7 @@
private boolean isInLiveTileMode(Launcher launcher,
LauncherInstrumentation.ContainerType expectedContainerType) {
- if (!ENABLE_QUICKSTEP_LIVE_TILE.get()
- || expectedContainerType != LauncherInstrumentation.ContainerType.OVERVIEW) {
+ if (expectedContainerType != LauncherInstrumentation.ContainerType.OVERVIEW) {
return false;
}
diff --git a/quickstep/tests/src/com/android/quickstep/DeviceProfileTest.kt b/quickstep/tests/src/com/android/quickstep/DeviceProfileTest.kt
deleted file mode 100644
index 45a342a..0000000
--- a/quickstep/tests/src/com/android/quickstep/DeviceProfileTest.kt
+++ /dev/null
@@ -1,1467 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep
-
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.launcher3.DeviceProfileBaseTest
-import com.google.common.truth.Truth.assertThat
-import org.junit.Test
-import org.junit.runner.RunWith
-
-/**
- * Tests for DeviceProfile.
- */
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class DeviceProfileTest : DeviceProfileBaseTest() {
-
- @Test
- fun phonePortrait3Button() {
- initializeVarsForPhone(isGestureMode = false)
- val dp = newDP()
-
- assertThat(dump(dp)).isEqualTo("DeviceProfile:\n" +
- "\t1 dp = 2.625 px\n" +
- "\tisTablet:false\n" +
- "\tisPhone:true\n" +
- "\ttransposeLayoutWithOrientation:true\n" +
- "\tisGestureMode:false\n" +
- "\tisLandscape:false\n" +
- "\tisMultiWindowMode:false\n" +
- "\tisTwoPanels:false\n" +
- "\twindowX: 0.0px (0.0dp)\n" +
- "\twindowY: 0.0px (0.0dp)\n" +
- "\twidthPx: 1080.0px (411.42856dp)\n" +
- "\theightPx: 2400.0px (914.2857dp)\n" +
- "\tavailableWidthPx: 1080.0px (411.42856dp)\n" +
- "\tavailableHeightPx: 2156.0px (821.3333dp)\n" +
- "\tmInsets.left: 0.0px (0.0dp)\n" +
- "\tmInsets.top: 118.0px (44.95238dp)\n" +
- "\tmInsets.right: 0.0px (0.0dp)\n" +
- "\tmInsets.bottom: 126.0px (48.0dp)\n" +
- "\taspectRatio:2.2222223\n" +
- "\tisScalableGrid:true\n" +
- "\tinv.numRows: 5\n" +
- "\tinv.numColumns: 4\n" +
- "\tinv.numSearchContainerColumns: 4\n" +
- "\tminCellSize: PointF(80.0, 104.0)dp\n" +
- "\tcellWidthPx: 210.0px (80.0dp)\n" +
- "\tcellHeightPx: 272.0px (103.61905dp)\n" +
- "\tgetCellSize().x: 210.0px (80.0dp)\n" +
- "\tgetCellSize().y: 272.0px (103.61905dp)\n" +
- "\tcellLayoutBorderSpacePx Horizontal: 42.0px (16.0dp)\n" +
- "\tcellLayoutBorderSpacePx Vertical: 42.0px (16.0dp)\n" +
- "\tcellLayoutPaddingPx.left: 28.0px (10.666667dp)\n" +
- "\tcellLayoutPaddingPx.top: 28.0px (10.666667dp)\n" +
- "\tcellLayoutPaddingPx.right: 28.0px (10.666667dp)\n" +
- "\tcellLayoutPaddingPx.bottom: 28.0px (10.666667dp)\n" +
- "\ticonSizePx: 157.0px (59.809525dp)\n" +
- "\ticonTextSizePx: 36.0px (13.714286dp)\n" +
- "\ticonDrawablePaddingPx: 17.0px (6.4761906dp)\n" +
- "\tfolderCellWidthPx: 210.0px (80.0dp)\n" +
- "\tfolderCellHeightPx: 272.0px (103.61905dp)\n" +
- "\tfolderChildIconSizePx: 158.0px (60.190475dp)\n" +
- "\tfolderChildTextSizePx: 37.0px (14.095238dp)\n" +
- "\tfolderChildDrawablePaddingPx: 21.0px (8.0dp)\n" +
- "\tfolderCellLayoutBorderSpaceOriginalPx: 42.0px (16.0dp)\n" +
- "\tfolderCellLayoutBorderSpacePx Horizontal: 42.0px (16.0dp)\n" +
- "\tfolderCellLayoutBorderSpacePx Vertical: 42.0px (16.0dp)\n" +
- "\tbottomSheetTopPadding: 146.0px (55.61905dp)\n" +
- "\tallAppsShiftRange: 788.0px (300.1905dp)\n" +
- "\tallAppsTopPadding: 0.0px (0.0dp)\n" +
- "\tallAppsIconSizePx: 157.0px (59.809525dp)\n" +
- "\tallAppsIconTextSizePx: 37.0px (14.095238dp)\n" +
- "\tallAppsIconDrawablePaddingPx: 18.0px (6.857143dp)\n" +
- "\tallAppsCellHeightPx: 314.0px (119.61905dp)\n" +
- "\tallAppsCellWidthPx: 210.0px (80.0dp)\n" +
- "\tallAppsBorderSpacePxX: 42.0px (16.0dp)\n" +
- "\tallAppsBorderSpacePxY: 42.0px (16.0dp)\n" +
- "\tnumShownAllAppsColumns: 4\n" +
- "\tallAppsLeftRightPadding: 57.0px (21.714285dp)\n" +
- "\tallAppsLeftRightMargin: 0.0px (0.0dp)\n" +
- "\thotseatBarSizePx: 511.0px (194.66667dp)\n" +
- "\tinv.hotseatColumnSpan: 4\n" +
- "\thotseatCellHeightPx: 177.0px (67.42857dp)\n" +
- "\thotseatBarBottomSpacePx: 147.0px (56.0dp)\n" +
- "\thotseatBarSidePaddingStartPx: 0.0px (0.0dp)\n" +
- "\thotseatBarSidePaddingEndPx: 0.0px (0.0dp)\n" +
- "\thotseatBarEndOffset: 0.0px (0.0dp)\n" +
- "\thotseatQsbSpace: 74.0px (28.190475dp)\n" +
- "\thotseatQsbHeight: 165.0px (62.857143dp)\n" +
- "\tspringLoadedHotseatBarTopMarginPx: 200.0px (76.190475dp)\n" +
- "\tgetHotseatLayoutPadding(context).top: 0.0px (0.0dp)\n" +
- "\tgetHotseatLayoutPadding(context).bottom: 334.0px (127.2381dp)\n" +
- "\tgetHotseatLayoutPadding(context).left: 83.0px (31.619047dp)\n" +
- "\tgetHotseatLayoutPadding(context).right: 83.0px (31.619047dp)\n" +
- "\tnumShownHotseatIcons: 4\n" +
- "\thotseatBorderSpace: 95.0px (36.190475dp)\n" +
- "\tisQsbInline: false\n" +
- "\thotseatQsbWidth: 913.0px (347.8095dp)\n" +
- "\tisTaskbarPresent:false\n" +
- "\tisTaskbarPresentInApps:false\n" +
- "\ttaskbarSize: 0.0px (0.0dp)\n" +
- "\tdesiredWorkspaceHorizontalMarginPx: 57.0px (21.714285dp)\n" +
- "\tworkspacePadding.left: 29.0px (11.047619dp)\n" +
- "\tworkspacePadding.top: 67.0px (25.52381dp)\n" +
- "\tworkspacePadding.right: 29.0px (11.047619dp)\n" +
- "\tworkspacePadding.bottom: 504.0px (192.0dp)\n" +
- "\ticonScale: 0.9981516px (0.38024822dp)\n" +
- "\tcellScaleToFit : 0.9981516px (0.38024822dp)\n" +
- "\textraSpace: 211.0px (80.38095dp)\n" +
- "\tunscaled extraSpace: 211.39073px (80.5298dp)\n" +
- "\tmaxEmptySpace: 315.0px (120.0dp)\n" +
- "\tworkspaceTopPadding: 95.0px (36.190475dp)\n" +
- "\tworkspaceBottomPadding: 116.0px (44.190475dp)\n" +
- "\toverviewTaskMarginPx: 42.0px (16.0dp)\n" +
- "\toverviewTaskIconSizePx: 126.0px (48.0dp)\n" +
- "\toverviewTaskIconDrawableSizePx: 116.0px (44.190475dp)\n" +
- "\toverviewTaskIconDrawableSizeGridPx: 0.0px (0.0dp)\n" +
- "\toverviewTaskThumbnailTopMarginPx: 168.0px (64.0dp)\n" +
- "\toverviewActionsTopMarginPx: 63.0px (24.0dp)\n" +
- "\toverviewActionsHeight: 126.0px (48.0dp)\n" +
- "\toverviewActionsButtonSpacing: 95.0px (36.190475dp)\n" +
- "\toverviewPageSpacing: 42.0px (16.0dp)\n" +
- "\toverviewRowSpacing: 0.0px (0.0dp)\n" +
- "\toverviewGridSideMargin: 0.0px (0.0dp)\n" +
- "\tdropTargetBarTopMarginPx: 84.0px (32.0dp)\n" +
- "\tdropTargetBarSizePx: 147.0px (56.0dp)\n" +
- "\tdropTargetBarBottomMarginPx: 42.0px (16.0dp)\n" +
- "\tgetCellLayoutSpringLoadShrunkTop(): 391.0px (148.95238dp)\n" +
- "\tgetCellLayoutSpringLoadShrunkBottom(): 1689.0px (643.4286dp)\n" +
- "\tworkspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)\n" +
- "\tgetWorkspaceSpringLoadScale(): 0.81892747px (0.31197238dp)\n" +
- "\tgetCellLayoutHeight(): 1585.0px (603.8095dp)\n" +
- "\tgetCellLayoutWidth(): 1022.0px (389.33334dp)\n")
- }
-
- @Test
- fun phonePortrait() {
- initializeVarsForPhone()
- val dp = newDP()
-
- assertThat(dump(dp)).isEqualTo("DeviceProfile:\n" +
- "\t1 dp = 2.625 px\n" +
- "\tisTablet:false\n" +
- "\tisPhone:true\n" +
- "\ttransposeLayoutWithOrientation:true\n" +
- "\tisGestureMode:true\n" +
- "\tisLandscape:false\n" +
- "\tisMultiWindowMode:false\n" +
- "\tisTwoPanels:false\n" +
- "\twindowX: 0.0px (0.0dp)\n" +
- "\twindowY: 0.0px (0.0dp)\n" +
- "\twidthPx: 1080.0px (411.42856dp)\n" +
- "\theightPx: 2400.0px (914.2857dp)\n" +
- "\tavailableWidthPx: 1080.0px (411.42856dp)\n" +
- "\tavailableHeightPx: 2219.0px (845.3333dp)\n" +
- "\tmInsets.left: 0.0px (0.0dp)\n" +
- "\tmInsets.top: 118.0px (44.95238dp)\n" +
- "\tmInsets.right: 0.0px (0.0dp)\n" +
- "\tmInsets.bottom: 63.0px (24.0dp)\n" +
- "\taspectRatio:2.2222223\n" +
- "\tisScalableGrid:true\n" +
- "\tinv.numRows: 5\n" +
- "\tinv.numColumns: 4\n" +
- "\tinv.numSearchContainerColumns: 4\n" +
- "\tminCellSize: PointF(80.0, 104.0)dp\n" +
- "\tcellWidthPx: 210.0px (80.0dp)\n" +
- "\tcellHeightPx: 272.0px (103.61905dp)\n" +
- "\tgetCellSize().x: 210.0px (80.0dp)\n" +
- "\tgetCellSize().y: 272.0px (103.61905dp)\n" +
- "\tcellLayoutBorderSpacePx Horizontal: 42.0px (16.0dp)\n" +
- "\tcellLayoutBorderSpacePx Vertical: 42.0px (16.0dp)\n" +
- "\tcellLayoutPaddingPx.left: 28.0px (10.666667dp)\n" +
- "\tcellLayoutPaddingPx.top: 28.0px (10.666667dp)\n" +
- "\tcellLayoutPaddingPx.right: 28.0px (10.666667dp)\n" +
- "\tcellLayoutPaddingPx.bottom: 28.0px (10.666667dp)\n" +
- "\ticonSizePx: 157.0px (59.809525dp)\n" +
- "\ticonTextSizePx: 36.0px (13.714286dp)\n" +
- "\ticonDrawablePaddingPx: 17.0px (6.4761906dp)\n" +
- "\tfolderCellWidthPx: 210.0px (80.0dp)\n" +
- "\tfolderCellHeightPx: 272.0px (103.61905dp)\n" +
- "\tfolderChildIconSizePx: 158.0px (60.190475dp)\n" +
- "\tfolderChildTextSizePx: 37.0px (14.095238dp)\n" +
- "\tfolderChildDrawablePaddingPx: 21.0px (8.0dp)\n" +
- "\tfolderCellLayoutBorderSpaceOriginalPx: 42.0px (16.0dp)\n" +
- "\tfolderCellLayoutBorderSpacePx Horizontal: 42.0px (16.0dp)\n" +
- "\tfolderCellLayoutBorderSpacePx Vertical: 42.0px (16.0dp)\n" +
- "\tbottomSheetTopPadding: 146.0px (55.61905dp)\n" +
- "\tallAppsShiftRange: 788.0px (300.1905dp)\n" +
- "\tallAppsTopPadding: 0.0px (0.0dp)\n" +
- "\tallAppsIconSizePx: 157.0px (59.809525dp)\n" +
- "\tallAppsIconTextSizePx: 37.0px (14.095238dp)\n" +
- "\tallAppsIconDrawablePaddingPx: 18.0px (6.857143dp)\n" +
- "\tallAppsCellHeightPx: 314.0px (119.61905dp)\n" +
- "\tallAppsCellWidthPx: 210.0px (80.0dp)\n" +
- "\tallAppsBorderSpacePxX: 42.0px (16.0dp)\n" +
- "\tallAppsBorderSpacePxY: 42.0px (16.0dp)\n" +
- "\tnumShownAllAppsColumns: 4\n" +
- "\tallAppsLeftRightPadding: 57.0px (21.714285dp)\n" +
- "\tallAppsLeftRightMargin: 0.0px (0.0dp)\n" +
- "\thotseatBarSizePx: 511.0px (194.66667dp)\n" +
- "\tinv.hotseatColumnSpan: 4\n" +
- "\thotseatCellHeightPx: 177.0px (67.42857dp)\n" +
- "\thotseatBarBottomSpacePx: 126.0px (48.0dp)\n" +
- "\thotseatBarSidePaddingStartPx: 0.0px (0.0dp)\n" +
- "\thotseatBarSidePaddingEndPx: 0.0px (0.0dp)\n" +
- "\thotseatBarEndOffset: 0.0px (0.0dp)\n" +
- "\thotseatQsbSpace: 95.0px (36.190475dp)\n" +
- "\thotseatQsbHeight: 165.0px (62.857143dp)\n" +
- "\tspringLoadedHotseatBarTopMarginPx: 200.0px (76.190475dp)\n" +
- "\tgetHotseatLayoutPadding(context).top: 0.0px (0.0dp)\n" +
- "\tgetHotseatLayoutPadding(context).bottom: 334.0px (127.2381dp)\n" +
- "\tgetHotseatLayoutPadding(context).left: 83.0px (31.619047dp)\n" +
- "\tgetHotseatLayoutPadding(context).right: 83.0px (31.619047dp)\n" +
- "\tnumShownHotseatIcons: 4\n" +
- "\thotseatBorderSpace: 95.0px (36.190475dp)\n" +
- "\tisQsbInline: false\n" +
- "\thotseatQsbWidth: 913.0px (347.8095dp)\n" +
- "\tisTaskbarPresent:false\n" +
- "\tisTaskbarPresentInApps:false\n" +
- "\ttaskbarSize: 0.0px (0.0dp)\n" +
- "\tdesiredWorkspaceHorizontalMarginPx: 57.0px (21.714285dp)\n" +
- "\tworkspacePadding.left: 29.0px (11.047619dp)\n" +
- "\tworkspacePadding.top: 67.0px (25.52381dp)\n" +
- "\tworkspacePadding.right: 29.0px (11.047619dp)\n" +
- "\tworkspacePadding.bottom: 567.0px (216.0dp)\n" +
- "\ticonScale: 0.9981516px (0.38024822dp)\n" +
- "\tcellScaleToFit : 0.9981516px (0.38024822dp)\n" +
- "\textraSpace: 211.0px (80.38095dp)\n" +
- "\tunscaled extraSpace: 211.39073px (80.5298dp)\n" +
- "\tmaxEmptySpace: 315.0px (120.0dp)\n" +
- "\tworkspaceTopPadding: 95.0px (36.190475dp)\n" +
- "\tworkspaceBottomPadding: 116.0px (44.190475dp)\n" +
- "\toverviewTaskMarginPx: 42.0px (16.0dp)\n" +
- "\toverviewTaskIconSizePx: 126.0px (48.0dp)\n" +
- "\toverviewTaskIconDrawableSizePx: 116.0px (44.190475dp)\n" +
- "\toverviewTaskIconDrawableSizeGridPx: 0.0px (0.0dp)\n" +
- "\toverviewTaskThumbnailTopMarginPx: 168.0px (64.0dp)\n" +
- "\toverviewActionsTopMarginPx: 63.0px (24.0dp)\n" +
- "\toverviewActionsHeight: 126.0px (48.0dp)\n" +
- "\toverviewActionsButtonSpacing: 95.0px (36.190475dp)\n" +
- "\toverviewPageSpacing: 42.0px (16.0dp)\n" +
- "\toverviewRowSpacing: 0.0px (0.0dp)\n" +
- "\toverviewGridSideMargin: 0.0px (0.0dp)\n" +
- "\tdropTargetBarTopMarginPx: 84.0px (32.0dp)\n" +
- "\tdropTargetBarSizePx: 147.0px (56.0dp)\n" +
- "\tdropTargetBarBottomMarginPx: 42.0px (16.0dp)\n" +
- "\tgetCellLayoutSpringLoadShrunkTop(): 391.0px (148.95238dp)\n" +
- "\tgetCellLayoutSpringLoadShrunkBottom(): 1689.0px (643.4286dp)\n" +
- "\tworkspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)\n" +
- "\tgetWorkspaceSpringLoadScale(): 0.81892747px (0.31197238dp)\n" +
- "\tgetCellLayoutHeight(): 1585.0px (603.8095dp)\n" +
- "\tgetCellLayoutWidth(): 1022.0px (389.33334dp)\n")
- }
-
- @Test
- fun tabletLandscape3Button() {
- initializeVarsForTablet(isLandscape = true, isGestureMode = false)
- val dp = newDP()
- dp.isTaskbarPresentInApps = true
-
- assertThat(dump(dp)).isEqualTo("DeviceProfile:\n" +
- "\t1 dp = 2.0 px\n" +
- "\tisTablet:true\n" +
- "\tisPhone:false\n" +
- "\ttransposeLayoutWithOrientation:false\n" +
- "\tisGestureMode:false\n" +
- "\tisLandscape:true\n" +
- "\tisMultiWindowMode:false\n" +
- "\tisTwoPanels:false\n" +
- "\twindowX: 0.0px (0.0dp)\n" +
- "\twindowY: 0.0px (0.0dp)\n" +
- "\twidthPx: 2560.0px (1280.0dp)\n" +
- "\theightPx: 1600.0px (800.0dp)\n" +
- "\tavailableWidthPx: 2560.0px (1280.0dp)\n" +
- "\tavailableHeightPx: 1496.0px (748.0dp)\n" +
- "\tmInsets.left: 0.0px (0.0dp)\n" +
- "\tmInsets.top: 104.0px (52.0dp)\n" +
- "\tmInsets.right: 0.0px (0.0dp)\n" +
- "\tmInsets.bottom: 0.0px (0.0dp)\n" +
- "\taspectRatio:1.6\n" +
- "\tisScalableGrid:true\n" +
- "\tinv.numRows: 5\n" +
- "\tinv.numColumns: 6\n" +
- "\tinv.numSearchContainerColumns: 3\n" +
- "\tminCellSize: PointF(120.0, 104.0)dp\n" +
- "\tcellWidthPx: 240.0px (120.0dp)\n" +
- "\tcellHeightPx: 208.0px (104.0dp)\n" +
- "\tgetCellSize().x: 240.0px (120.0dp)\n" +
- "\tgetCellSize().y: 208.0px (104.0dp)\n" +
- "\tcellLayoutBorderSpacePx Horizontal: 128.0px (64.0dp)\n" +
- "\tcellLayoutBorderSpacePx Vertical: 32.0px (16.0dp)\n" +
- "\tcellLayoutPaddingPx.left: 59.0px (29.5dp)\n" +
- "\tcellLayoutPaddingPx.top: 32.0px (16.0dp)\n" +
- "\tcellLayoutPaddingPx.right: 59.0px (29.5dp)\n" +
- "\tcellLayoutPaddingPx.bottom: 59.0px (29.5dp)\n" +
- "\ticonSizePx: 120.0px (60.0dp)\n" +
- "\ticonTextSizePx: 28.0px (14.0dp)\n" +
- "\ticonDrawablePaddingPx: 14.0px (7.0dp)\n" +
- "\tfolderCellWidthPx: 240.0px (120.0dp)\n" +
- "\tfolderCellHeightPx: 208.0px (104.0dp)\n" +
- "\tfolderChildIconSizePx: 120.0px (60.0dp)\n" +
- "\tfolderChildTextSizePx: 28.0px (14.0dp)\n" +
- "\tfolderChildDrawablePaddingPx: 16.0px (8.0dp)\n" +
- "\tfolderCellLayoutBorderSpaceOriginalPx: 0.0px (0.0dp)\n" +
- "\tfolderCellLayoutBorderSpacePx Horizontal: 0.0px (0.0dp)\n" +
- "\tfolderCellLayoutBorderSpacePx Vertical: 0.0px (0.0dp)\n" +
- "\tbottomSheetTopPadding: 104.0px (52.0dp)\n" +
- "\tallAppsShiftRange: 1496.0px (748.0dp)\n" +
- "\tallAppsTopPadding: 104.0px (52.0dp)\n" +
- "\tallAppsIconSizePx: 120.0px (60.0dp)\n" +
- "\tallAppsIconTextSizePx: 28.0px (14.0dp)\n" +
- "\tallAppsIconDrawablePaddingPx: 14.0px (7.0dp)\n" +
- "\tallAppsCellHeightPx: 284.0px (142.0dp)\n" +
- "\tallAppsCellWidthPx: 252.0px (126.0dp)\n" +
- "\tallAppsBorderSpacePxX: 32.0px (16.0dp)\n" +
- "\tallAppsBorderSpacePxY: 32.0px (16.0dp)\n" +
- "\tnumShownAllAppsColumns: 6\n" +
- "\tallAppsLeftRightPadding: 64.0px (32.0dp)\n" +
- "\tallAppsLeftRightMargin: 380.0px (190.0dp)\n" +
- "\thotseatBarSizePx: 200.0px (100.0dp)\n" +
- "\tinv.hotseatColumnSpan: 4\n" +
- "\thotseatCellHeightPx: 135.0px (67.5dp)\n" +
- "\thotseatBarBottomSpacePx: 80.0px (40.0dp)\n" +
- "\thotseatBarSidePaddingStartPx: 0.0px (0.0dp)\n" +
- "\thotseatBarSidePaddingEndPx: 0.0px (0.0dp)\n" +
- "\thotseatBarEndOffset: 705.0px (352.5dp)\n" +
- "\thotseatQsbSpace: 64.0px (32.0dp)\n" +
- "\thotseatQsbHeight: 126.0px (63.0dp)\n" +
- "\tspringLoadedHotseatBarTopMarginPx: 128.0px (64.0dp)\n" +
- "\tgetHotseatLayoutPadding(context).top: -8.0px (-4.0dp)\n" +
- "\tgetHotseatLayoutPadding(context).bottom: 73.0px (36.5dp)\n" +
- "\tgetHotseatLayoutPadding(context).left: 954.0px (477.0dp)\n" +
- "\tgetHotseatLayoutPadding(context).right: 705.0px (352.5dp)\n" +
- "\tnumShownHotseatIcons: 6\n" +
- "\thotseatBorderSpace: 36.0px (18.0dp)\n" +
- "\tisQsbInline: true\n" +
- "\thotseatQsbWidth: 619.0px (309.5dp)\n" +
- "\tisTaskbarPresent:true\n" +
- "\tisTaskbarPresentInApps:true\n" +
- "\ttaskbarSize: 120.0px (60.0dp)\n" +
- "\tdesiredWorkspaceHorizontalMarginPx: 240.0px (120.0dp)\n" +
- "\tworkspacePadding.left: 181.0px (90.5dp)\n" +
- "\tworkspacePadding.top: 0.0px (0.0dp)\n" +
- "\tworkspacePadding.right: 181.0px (90.5dp)\n" +
- "\tworkspacePadding.bottom: 237.0px (118.5dp)\n" +
- "\ticonScale: 1.0px (0.5dp)\n" +
- "\tcellScaleToFit : 1.0px (0.5dp)\n" +
- "\textraSpace: 104.0px (52.0dp)\n" +
- "\tunscaled extraSpace: 104.0px (52.0dp)\n" +
- "\tmaxEmptySpace: 200.0px (100.0dp)\n" +
- "\tworkspaceTopPadding: 32.0px (16.0dp)\n" +
- "\tworkspaceBottomPadding: 72.0px (36.0dp)\n" +
- "\toverviewTaskMarginPx: 32.0px (16.0dp)\n" +
- "\toverviewTaskIconSizePx: 96.0px (48.0dp)\n" +
- "\toverviewTaskIconDrawableSizePx: 88.0px (44.0dp)\n" +
- "\toverviewTaskIconDrawableSizeGridPx: 88.0px (44.0dp)\n" +
- "\toverviewTaskThumbnailTopMarginPx: 128.0px (64.0dp)\n" +
- "\toverviewActionsTopMarginPx: 40.0px (20.0dp)\n" +
- "\toverviewActionsHeight: 96.0px (48.0dp)\n" +
- "\toverviewActionsButtonSpacing: 72.0px (36.0dp)\n" +
- "\toverviewPageSpacing: 88.0px (44.0dp)\n" +
- "\toverviewRowSpacing: 72.0px (36.0dp)\n" +
- "\toverviewGridSideMargin: 128.0px (64.0dp)\n" +
- "\tdropTargetBarTopMarginPx: 0.0px (0.0dp)\n" +
- "\tdropTargetBarSizePx: 144.0px (72.0dp)\n" +
- "\tdropTargetBarBottomMarginPx: 64.0px (32.0dp)\n" +
- "\tgetCellLayoutSpringLoadShrunkTop(): 312.0px (156.0dp)\n" +
- "\tgetCellLayoutSpringLoadShrunkBottom(): 1272.0px (636.0dp)\n" +
- "\tworkspaceSpringLoadedMinNextPageVisiblePx: 48.0px (24.0dp)\n" +
- "\tgetWorkspaceSpringLoadScale(): 0.76250994px (0.38125497dp)\n" +
- "\tgetCellLayoutHeight(): 1259.0px (629.5dp)\n" +
- "\tgetCellLayoutWidth(): 2198.0px (1099.0dp)\n")
- }
-
- @Test
- fun tabletLandscape() {
- initializeVarsForTablet(isLandscape = true)
- val dp = newDP()
- dp.isTaskbarPresentInApps = true
-
- assertThat(dump(dp)).isEqualTo("DeviceProfile:\n" +
- "\t1 dp = 2.0 px\n" +
- "\tisTablet:true\n" +
- "\tisPhone:false\n" +
- "\ttransposeLayoutWithOrientation:false\n" +
- "\tisGestureMode:true\n" +
- "\tisLandscape:true\n" +
- "\tisMultiWindowMode:false\n" +
- "\tisTwoPanels:false\n" +
- "\twindowX: 0.0px (0.0dp)\n" +
- "\twindowY: 0.0px (0.0dp)\n" +
- "\twidthPx: 2560.0px (1280.0dp)\n" +
- "\theightPx: 1600.0px (800.0dp)\n" +
- "\tavailableWidthPx: 2560.0px (1280.0dp)\n" +
- "\tavailableHeightPx: 1496.0px (748.0dp)\n" +
- "\tmInsets.left: 0.0px (0.0dp)\n" +
- "\tmInsets.top: 104.0px (52.0dp)\n" +
- "\tmInsets.right: 0.0px (0.0dp)\n" +
- "\tmInsets.bottom: 0.0px (0.0dp)\n" +
- "\taspectRatio:1.6\n" +
- "\tisScalableGrid:true\n" +
- "\tinv.numRows: 5\n" +
- "\tinv.numColumns: 6\n" +
- "\tinv.numSearchContainerColumns: 3\n" +
- "\tminCellSize: PointF(120.0, 104.0)dp\n" +
- "\tcellWidthPx: 240.0px (120.0dp)\n" +
- "\tcellHeightPx: 208.0px (104.0dp)\n" +
- "\tgetCellSize().x: 240.0px (120.0dp)\n" +
- "\tgetCellSize().y: 208.0px (104.0dp)\n" +
- "\tcellLayoutBorderSpacePx Horizontal: 128.0px (64.0dp)\n" +
- "\tcellLayoutBorderSpacePx Vertical: 32.0px (16.0dp)\n" +
- "\tcellLayoutPaddingPx.left: 59.0px (29.5dp)\n" +
- "\tcellLayoutPaddingPx.top: 32.0px (16.0dp)\n" +
- "\tcellLayoutPaddingPx.right: 59.0px (29.5dp)\n" +
- "\tcellLayoutPaddingPx.bottom: 59.0px (29.5dp)\n" +
- "\ticonSizePx: 120.0px (60.0dp)\n" +
- "\ticonTextSizePx: 28.0px (14.0dp)\n" +
- "\ticonDrawablePaddingPx: 14.0px (7.0dp)\n" +
- "\tfolderCellWidthPx: 240.0px (120.0dp)\n" +
- "\tfolderCellHeightPx: 208.0px (104.0dp)\n" +
- "\tfolderChildIconSizePx: 120.0px (60.0dp)\n" +
- "\tfolderChildTextSizePx: 28.0px (14.0dp)\n" +
- "\tfolderChildDrawablePaddingPx: 16.0px (8.0dp)\n" +
- "\tfolderCellLayoutBorderSpaceOriginalPx: 0.0px (0.0dp)\n" +
- "\tfolderCellLayoutBorderSpacePx Horizontal: 0.0px (0.0dp)\n" +
- "\tfolderCellLayoutBorderSpacePx Vertical: 0.0px (0.0dp)\n" +
- "\tbottomSheetTopPadding: 104.0px (52.0dp)\n" +
- "\tallAppsShiftRange: 1496.0px (748.0dp)\n" +
- "\tallAppsTopPadding: 104.0px (52.0dp)\n" +
- "\tallAppsIconSizePx: 120.0px (60.0dp)\n" +
- "\tallAppsIconTextSizePx: 28.0px (14.0dp)\n" +
- "\tallAppsIconDrawablePaddingPx: 14.0px (7.0dp)\n" +
- "\tallAppsCellHeightPx: 284.0px (142.0dp)\n" +
- "\tallAppsCellWidthPx: 252.0px (126.0dp)\n" +
- "\tallAppsBorderSpacePxX: 32.0px (16.0dp)\n" +
- "\tallAppsBorderSpacePxY: 32.0px (16.0dp)\n" +
- "\tnumShownAllAppsColumns: 6\n" +
- "\tallAppsLeftRightPadding: 64.0px (32.0dp)\n" +
- "\tallAppsLeftRightMargin: 380.0px (190.0dp)\n" +
- "\thotseatBarSizePx: 200.0px (100.0dp)\n" +
- "\tinv.hotseatColumnSpan: 4\n" +
- "\thotseatCellHeightPx: 135.0px (67.5dp)\n" +
- "\thotseatBarBottomSpacePx: 80.0px (40.0dp)\n" +
- "\thotseatBarSidePaddingStartPx: 0.0px (0.0dp)\n" +
- "\thotseatBarSidePaddingEndPx: 0.0px (0.0dp)\n" +
- "\thotseatBarEndOffset: 0.0px (0.0dp)\n" +
- "\thotseatQsbSpace: 64.0px (32.0dp)\n" +
- "\thotseatQsbHeight: 126.0px (63.0dp)\n" +
- "\tspringLoadedHotseatBarTopMarginPx: 128.0px (64.0dp)\n" +
- "\tgetHotseatLayoutPadding(context).top: -8.0px (-4.0dp)\n" +
- "\tgetHotseatLayoutPadding(context).bottom: 73.0px (36.5dp)\n" +
- "\tgetHotseatLayoutPadding(context).left: 1040.0px (520.0dp)\n" +
- "\tgetHotseatLayoutPadding(context).right: 300.0px (150.0dp)\n" +
- "\tnumShownHotseatIcons: 6\n" +
- "\thotseatBorderSpace: 100.0px (50.0dp)\n" +
- "\tisQsbInline: true\n" +
- "\thotseatQsbWidth: 640.0px (320.0dp)\n" +
- "\tisTaskbarPresent:true\n" +
- "\tisTaskbarPresentInApps:true\n" +
- "\ttaskbarSize: 120.0px (60.0dp)\n" +
- "\tdesiredWorkspaceHorizontalMarginPx: 240.0px (120.0dp)\n" +
- "\tworkspacePadding.left: 181.0px (90.5dp)\n" +
- "\tworkspacePadding.top: 0.0px (0.0dp)\n" +
- "\tworkspacePadding.right: 181.0px (90.5dp)\n" +
- "\tworkspacePadding.bottom: 237.0px (118.5dp)\n" +
- "\ticonScale: 1.0px (0.5dp)\n" +
- "\tcellScaleToFit : 1.0px (0.5dp)\n" +
- "\textraSpace: 104.0px (52.0dp)\n" +
- "\tunscaled extraSpace: 104.0px (52.0dp)\n" +
- "\tmaxEmptySpace: 200.0px (100.0dp)\n" +
- "\tworkspaceTopPadding: 32.0px (16.0dp)\n" +
- "\tworkspaceBottomPadding: 72.0px (36.0dp)\n" +
- "\toverviewTaskMarginPx: 32.0px (16.0dp)\n" +
- "\toverviewTaskIconSizePx: 96.0px (48.0dp)\n" +
- "\toverviewTaskIconDrawableSizePx: 88.0px (44.0dp)\n" +
- "\toverviewTaskIconDrawableSizeGridPx: 88.0px (44.0dp)\n" +
- "\toverviewTaskThumbnailTopMarginPx: 128.0px (64.0dp)\n" +
- "\toverviewActionsTopMarginPx: 40.0px (20.0dp)\n" +
- "\toverviewActionsHeight: 96.0px (48.0dp)\n" +
- "\toverviewActionsButtonSpacing: 72.0px (36.0dp)\n" +
- "\toverviewPageSpacing: 88.0px (44.0dp)\n" +
- "\toverviewRowSpacing: 72.0px (36.0dp)\n" +
- "\toverviewGridSideMargin: 128.0px (64.0dp)\n" +
- "\tdropTargetBarTopMarginPx: 0.0px (0.0dp)\n" +
- "\tdropTargetBarSizePx: 144.0px (72.0dp)\n" +
- "\tdropTargetBarBottomMarginPx: 64.0px (32.0dp)\n" +
- "\tgetCellLayoutSpringLoadShrunkTop(): 312.0px (156.0dp)\n" +
- "\tgetCellLayoutSpringLoadShrunkBottom(): 1272.0px (636.0dp)\n" +
- "\tworkspaceSpringLoadedMinNextPageVisiblePx: 48.0px (24.0dp)\n" +
- "\tgetWorkspaceSpringLoadScale(): 0.76250994px (0.38125497dp)\n" +
- "\tgetCellLayoutHeight(): 1259.0px (629.5dp)\n" +
- "\tgetCellLayoutWidth(): 2198.0px (1099.0dp)\n")
- }
-
- @Test
- fun tabletPortrait3Button() {
- initializeVarsForTablet(isGestureMode = false)
- val dp = newDP()
- dp.isTaskbarPresentInApps = true
-
- assertThat(dump(dp)).isEqualTo("DeviceProfile:\n" +
- "\t1 dp = 2.0 px\n" +
- "\tisTablet:true\n" +
- "\tisPhone:false\n" +
- "\ttransposeLayoutWithOrientation:false\n" +
- "\tisGestureMode:false\n" +
- "\tisLandscape:false\n" +
- "\tisMultiWindowMode:false\n" +
- "\tisTwoPanels:false\n" +
- "\twindowX: 0.0px (0.0dp)\n" +
- "\twindowY: 0.0px (0.0dp)\n" +
- "\twidthPx: 1600.0px (800.0dp)\n" +
- "\theightPx: 2560.0px (1280.0dp)\n" +
- "\tavailableWidthPx: 1600.0px (800.0dp)\n" +
- "\tavailableHeightPx: 2456.0px (1228.0dp)\n" +
- "\tmInsets.left: 0.0px (0.0dp)\n" +
- "\tmInsets.top: 104.0px (52.0dp)\n" +
- "\tmInsets.right: 0.0px (0.0dp)\n" +
- "\tmInsets.bottom: 0.0px (0.0dp)\n" +
- "\taspectRatio:1.6\n" +
- "\tisScalableGrid:true\n" +
- "\tinv.numRows: 5\n" +
- "\tinv.numColumns: 6\n" +
- "\tinv.numSearchContainerColumns: 3\n" +
- "\tminCellSize: PointF(102.0, 120.0)dp\n" +
- "\tcellWidthPx: 204.0px (102.0dp)\n" +
- "\tcellHeightPx: 240.0px (120.0dp)\n" +
- "\tgetCellSize().x: 204.0px (102.0dp)\n" +
- "\tgetCellSize().y: 240.0px (120.0dp)\n" +
- "\tcellLayoutBorderSpacePx Horizontal: 32.0px (16.0dp)\n" +
- "\tcellLayoutBorderSpacePx Vertical: 128.0px (64.0dp)\n" +
- "\tcellLayoutPaddingPx.left: 72.0px (36.0dp)\n" +
- "\tcellLayoutPaddingPx.top: 72.0px (36.0dp)\n" +
- "\tcellLayoutPaddingPx.right: 72.0px (36.0dp)\n" +
- "\tcellLayoutPaddingPx.bottom: 72.0px (36.0dp)\n" +
- "\ticonSizePx: 120.0px (60.0dp)\n" +
- "\ticonTextSizePx: 28.0px (14.0dp)\n" +
- "\ticonDrawablePaddingPx: 14.0px (7.0dp)\n" +
- "\tfolderCellWidthPx: 204.0px (102.0dp)\n" +
- "\tfolderCellHeightPx: 240.0px (120.0dp)\n" +
- "\tfolderChildIconSizePx: 120.0px (60.0dp)\n" +
- "\tfolderChildTextSizePx: 28.0px (14.0dp)\n" +
- "\tfolderChildDrawablePaddingPx: 27.0px (13.5dp)\n" +
- "\tfolderCellLayoutBorderSpaceOriginalPx: 0.0px (0.0dp)\n" +
- "\tfolderCellLayoutBorderSpacePx Horizontal: 0.0px (0.0dp)\n" +
- "\tfolderCellLayoutBorderSpacePx Vertical: 0.0px (0.0dp)\n" +
- "\tbottomSheetTopPadding: 704.0px (352.0dp)\n" +
- "\tallAppsShiftRange: 1936.0px (968.0dp)\n" +
- "\tallAppsTopPadding: 624.0px (312.0dp)\n" +
- "\tallAppsIconSizePx: 120.0px (60.0dp)\n" +
- "\tallAppsIconTextSizePx: 28.0px (14.0dp)\n" +
- "\tallAppsIconDrawablePaddingPx: 14.0px (7.0dp)\n" +
- "\tallAppsCellHeightPx: 316.0px (158.0dp)\n" +
- "\tallAppsCellWidthPx: 192.0px (96.0dp)\n" +
- "\tallAppsBorderSpacePxX: 16.0px (8.0dp)\n" +
- "\tallAppsBorderSpacePxY: 32.0px (16.0dp)\n" +
- "\tnumShownAllAppsColumns: 6\n" +
- "\tallAppsLeftRightPadding: 56.0px (28.0dp)\n" +
- "\tallAppsLeftRightMargin: 128.0px (64.0dp)\n" +
- "\thotseatBarSizePx: 358.0px (179.0dp)\n" +
- "\tinv.hotseatColumnSpan: 6\n" +
- "\thotseatCellHeightPx: 135.0px (67.5dp)\n" +
- "\thotseatBarBottomSpacePx: 72.0px (36.0dp)\n" +
- "\thotseatBarSidePaddingStartPx: 0.0px (0.0dp)\n" +
- "\thotseatBarSidePaddingEndPx: 0.0px (0.0dp)\n" +
- "\thotseatBarEndOffset: 558.0px (279.0dp)\n" +
- "\thotseatQsbSpace: 64.0px (32.0dp)\n" +
- "\thotseatQsbHeight: 126.0px (63.0dp)\n" +
- "\tspringLoadedHotseatBarTopMarginPx: 216.0px (108.0dp)\n" +
- "\tgetHotseatLayoutPadding(context).top: 158.0px (79.0dp)\n" +
- "\tgetHotseatLayoutPadding(context).bottom: 65.0px (32.5dp)\n" +
- "\tgetHotseatLayoutPadding(context).left: 150.0px (75.0dp)\n" +
- "\tgetHotseatLayoutPadding(context).right: 558.0px (279.0dp)\n" +
- "\tnumShownHotseatIcons: 5\n" +
- "\thotseatBorderSpace: 73.0px (36.5dp)\n" +
- "\tisQsbInline: false\n" +
- "\thotseatQsbWidth: 1300.0px (650.0dp)\n" +
- "\tisTaskbarPresent:true\n" +
- "\tisTaskbarPresentInApps:true\n" +
- "\ttaskbarSize: 120.0px (60.0dp)\n" +
- "\tdesiredWorkspaceHorizontalMarginPx: 108.0px (54.0dp)\n" +
- "\tworkspacePadding.left: 36.0px (18.0dp)\n" +
- "\tworkspacePadding.top: 87.0px (43.5dp)\n" +
- "\tworkspacePadding.right: 36.0px (18.0dp)\n" +
- "\tworkspacePadding.bottom: 513.0px (256.5dp)\n" +
- "\ticonScale: 1.0px (0.5dp)\n" +
- "\tcellScaleToFit : 1.0px (0.5dp)\n" +
- "\textraSpace: 362.0px (181.0dp)\n" +
- "\tunscaled extraSpace: 362.0px (181.0dp)\n" +
- "\tmaxEmptySpace: 19998.0px (9999.0dp)\n" +
- "\tworkspaceTopPadding: 159.0px (79.5dp)\n" +
- "\tworkspaceBottomPadding: 203.0px (101.5dp)\n" +
- "\toverviewTaskMarginPx: 32.0px (16.0dp)\n" +
- "\toverviewTaskIconSizePx: 96.0px (48.0dp)\n" +
- "\toverviewTaskIconDrawableSizePx: 88.0px (44.0dp)\n" +
- "\toverviewTaskIconDrawableSizeGridPx: 88.0px (44.0dp)\n" +
- "\toverviewTaskThumbnailTopMarginPx: 128.0px (64.0dp)\n" +
- "\toverviewActionsTopMarginPx: 48.0px (24.0dp)\n" +
- "\toverviewActionsHeight: 96.0px (48.0dp)\n" +
- "\toverviewActionsButtonSpacing: 72.0px (36.0dp)\n" +
- "\toverviewPageSpacing: 88.0px (44.0dp)\n" +
- "\toverviewRowSpacing: 72.0px (36.0dp)\n" +
- "\toverviewGridSideMargin: 128.0px (64.0dp)\n" +
- "\tdropTargetBarTopMarginPx: 220.0px (110.0dp)\n" +
- "\tdropTargetBarSizePx: 144.0px (72.0dp)\n" +
- "\tdropTargetBarBottomMarginPx: 96.0px (48.0dp)\n" +
- "\tgetCellLayoutSpringLoadShrunkTop(): 564.0px (282.0dp)\n" +
- "\tgetCellLayoutSpringLoadShrunkBottom(): 1986.0px (993.0dp)\n" +
- "\tworkspaceSpringLoadedMinNextPageVisiblePx: 48.0px (24.0dp)\n" +
- "\tgetWorkspaceSpringLoadScale(): 0.76616377px (0.38308188dp)\n" +
- "\tgetCellLayoutHeight(): 1856.0px (928.0dp)\n" +
- "\tgetCellLayoutWidth(): 1528.0px (764.0dp)\n")
- }
-
- @Test
- fun tabletPortrait() {
- initializeVarsForTablet()
- val dp = newDP()
- dp.isTaskbarPresentInApps = true
-
- assertThat(dump(dp)).isEqualTo("DeviceProfile:\n" +
- "\t1 dp = 2.0 px\n" +
- "\tisTablet:true\n" +
- "\tisPhone:false\n" +
- "\ttransposeLayoutWithOrientation:false\n" +
- "\tisGestureMode:true\n" +
- "\tisLandscape:false\n" +
- "\tisMultiWindowMode:false\n" +
- "\tisTwoPanels:false\n" +
- "\twindowX: 0.0px (0.0dp)\n" +
- "\twindowY: 0.0px (0.0dp)\n" +
- "\twidthPx: 1600.0px (800.0dp)\n" +
- "\theightPx: 2560.0px (1280.0dp)\n" +
- "\tavailableWidthPx: 1600.0px (800.0dp)\n" +
- "\tavailableHeightPx: 2456.0px (1228.0dp)\n" +
- "\tmInsets.left: 0.0px (0.0dp)\n" +
- "\tmInsets.top: 104.0px (52.0dp)\n" +
- "\tmInsets.right: 0.0px (0.0dp)\n" +
- "\tmInsets.bottom: 0.0px (0.0dp)\n" +
- "\taspectRatio:1.6\n" +
- "\tisScalableGrid:true\n" +
- "\tinv.numRows: 5\n" +
- "\tinv.numColumns: 6\n" +
- "\tinv.numSearchContainerColumns: 3\n" +
- "\tminCellSize: PointF(102.0, 120.0)dp\n" +
- "\tcellWidthPx: 204.0px (102.0dp)\n" +
- "\tcellHeightPx: 240.0px (120.0dp)\n" +
- "\tgetCellSize().x: 204.0px (102.0dp)\n" +
- "\tgetCellSize().y: 240.0px (120.0dp)\n" +
- "\tcellLayoutBorderSpacePx Horizontal: 32.0px (16.0dp)\n" +
- "\tcellLayoutBorderSpacePx Vertical: 128.0px (64.0dp)\n" +
- "\tcellLayoutPaddingPx.left: 72.0px (36.0dp)\n" +
- "\tcellLayoutPaddingPx.top: 72.0px (36.0dp)\n" +
- "\tcellLayoutPaddingPx.right: 72.0px (36.0dp)\n" +
- "\tcellLayoutPaddingPx.bottom: 72.0px (36.0dp)\n" +
- "\ticonSizePx: 120.0px (60.0dp)\n" +
- "\ticonTextSizePx: 28.0px (14.0dp)\n" +
- "\ticonDrawablePaddingPx: 14.0px (7.0dp)\n" +
- "\tfolderCellWidthPx: 204.0px (102.0dp)\n" +
- "\tfolderCellHeightPx: 240.0px (120.0dp)\n" +
- "\tfolderChildIconSizePx: 120.0px (60.0dp)\n" +
- "\tfolderChildTextSizePx: 28.0px (14.0dp)\n" +
- "\tfolderChildDrawablePaddingPx: 27.0px (13.5dp)\n" +
- "\tfolderCellLayoutBorderSpaceOriginalPx: 0.0px (0.0dp)\n" +
- "\tfolderCellLayoutBorderSpacePx Horizontal: 0.0px (0.0dp)\n" +
- "\tfolderCellLayoutBorderSpacePx Vertical: 0.0px (0.0dp)\n" +
- "\tbottomSheetTopPadding: 704.0px (352.0dp)\n" +
- "\tallAppsShiftRange: 1936.0px (968.0dp)\n" +
- "\tallAppsTopPadding: 624.0px (312.0dp)\n" +
- "\tallAppsIconSizePx: 120.0px (60.0dp)\n" +
- "\tallAppsIconTextSizePx: 28.0px (14.0dp)\n" +
- "\tallAppsIconDrawablePaddingPx: 14.0px (7.0dp)\n" +
- "\tallAppsCellHeightPx: 316.0px (158.0dp)\n" +
- "\tallAppsCellWidthPx: 192.0px (96.0dp)\n" +
- "\tallAppsBorderSpacePxX: 16.0px (8.0dp)\n" +
- "\tallAppsBorderSpacePxY: 32.0px (16.0dp)\n" +
- "\tnumShownAllAppsColumns: 6\n" +
- "\tallAppsLeftRightPadding: 56.0px (28.0dp)\n" +
- "\tallAppsLeftRightMargin: 128.0px (64.0dp)\n" +
- "\thotseatBarSizePx: 358.0px (179.0dp)\n" +
- "\tinv.hotseatColumnSpan: 6\n" +
- "\thotseatCellHeightPx: 135.0px (67.5dp)\n" +
- "\thotseatBarBottomSpacePx: 72.0px (36.0dp)\n" +
- "\thotseatBarSidePaddingStartPx: 0.0px (0.0dp)\n" +
- "\thotseatBarSidePaddingEndPx: 0.0px (0.0dp)\n" +
- "\thotseatBarEndOffset: 0.0px (0.0dp)\n" +
- "\thotseatQsbSpace: 64.0px (32.0dp)\n" +
- "\thotseatQsbHeight: 126.0px (63.0dp)\n" +
- "\tspringLoadedHotseatBarTopMarginPx: 216.0px (108.0dp)\n" +
- "\tgetHotseatLayoutPadding(context).top: 158.0px (79.0dp)\n" +
- "\tgetHotseatLayoutPadding(context).bottom: 65.0px (32.5dp)\n" +
- "\tgetHotseatLayoutPadding(context).left: 150.0px (75.0dp)\n" +
- "\tgetHotseatLayoutPadding(context).right: 150.0px (75.0dp)\n" +
- "\tnumShownHotseatIcons: 6\n" +
- "\thotseatBorderSpace: 116.0px (58.0dp)\n" +
- "\tisQsbInline: false\n" +
- "\thotseatQsbWidth: 1300.0px (650.0dp)\n" +
- "\tisTaskbarPresent:true\n" +
- "\tisTaskbarPresentInApps:true\n" +
- "\ttaskbarSize: 120.0px (60.0dp)\n" +
- "\tdesiredWorkspaceHorizontalMarginPx: 108.0px (54.0dp)\n" +
- "\tworkspacePadding.left: 36.0px (18.0dp)\n" +
- "\tworkspacePadding.top: 87.0px (43.5dp)\n" +
- "\tworkspacePadding.right: 36.0px (18.0dp)\n" +
- "\tworkspacePadding.bottom: 513.0px (256.5dp)\n" +
- "\ticonScale: 1.0px (0.5dp)\n" +
- "\tcellScaleToFit : 1.0px (0.5dp)\n" +
- "\textraSpace: 362.0px (181.0dp)\n" +
- "\tunscaled extraSpace: 362.0px (181.0dp)\n" +
- "\tmaxEmptySpace: 19998.0px (9999.0dp)\n" +
- "\tworkspaceTopPadding: 159.0px (79.5dp)\n" +
- "\tworkspaceBottomPadding: 203.0px (101.5dp)\n" +
- "\toverviewTaskMarginPx: 32.0px (16.0dp)\n" +
- "\toverviewTaskIconSizePx: 96.0px (48.0dp)\n" +
- "\toverviewTaskIconDrawableSizePx: 88.0px (44.0dp)\n" +
- "\toverviewTaskIconDrawableSizeGridPx: 88.0px (44.0dp)\n" +
- "\toverviewTaskThumbnailTopMarginPx: 128.0px (64.0dp)\n" +
- "\toverviewActionsTopMarginPx: 48.0px (24.0dp)\n" +
- "\toverviewActionsHeight: 96.0px (48.0dp)\n" +
- "\toverviewActionsButtonSpacing: 72.0px (36.0dp)\n" +
- "\toverviewPageSpacing: 88.0px (44.0dp)\n" +
- "\toverviewRowSpacing: 72.0px (36.0dp)\n" +
- "\toverviewGridSideMargin: 128.0px (64.0dp)\n" +
- "\tdropTargetBarTopMarginPx: 220.0px (110.0dp)\n" +
- "\tdropTargetBarSizePx: 144.0px (72.0dp)\n" +
- "\tdropTargetBarBottomMarginPx: 96.0px (48.0dp)\n" +
- "\tgetCellLayoutSpringLoadShrunkTop(): 564.0px (282.0dp)\n" +
- "\tgetCellLayoutSpringLoadShrunkBottom(): 1986.0px (993.0dp)\n" +
- "\tworkspaceSpringLoadedMinNextPageVisiblePx: 48.0px (24.0dp)\n" +
- "\tgetWorkspaceSpringLoadScale(): 0.76616377px (0.38308188dp)\n" +
- "\tgetCellLayoutHeight(): 1856.0px (928.0dp)\n" +
- "\tgetCellLayoutWidth(): 1528.0px (764.0dp)\n")
- }
-
- @Test
- fun twoPanelLandscape3Button() {
- initializeVarsForTwoPanel(isLandscape = true, isGestureMode = false)
- val dp = newDP()
- dp.isTaskbarPresentInApps = true
-
- assertThat(dump(dp)).isEqualTo("DeviceProfile:\n" +
- "\t1 dp = 2.625 px\n" +
- "\tisTablet:true\n" +
- "\tisPhone:false\n" +
- "\ttransposeLayoutWithOrientation:false\n" +
- "\tisGestureMode:false\n" +
- "\tisLandscape:true\n" +
- "\tisMultiWindowMode:false\n" +
- "\tisTwoPanels:true\n" +
- "\twindowX: 0.0px (0.0dp)\n" +
- "\twindowY: 0.0px (0.0dp)\n" +
- "\twidthPx: 2208.0px (841.1429dp)\n" +
- "\theightPx: 1840.0px (700.9524dp)\n" +
- "\tavailableWidthPx: 2208.0px (841.1429dp)\n" +
- "\tavailableHeightPx: 1730.0px (659.0476dp)\n" +
- "\tmInsets.left: 0.0px (0.0dp)\n" +
- "\tmInsets.top: 110.0px (41.904762dp)\n" +
- "\tmInsets.right: 0.0px (0.0dp)\n" +
- "\tmInsets.bottom: 0.0px (0.0dp)\n" +
- "\taspectRatio:1.2\n" +
- "\tisScalableGrid:true\n" +
- "\tinv.numRows: 4\n" +
- "\tinv.numColumns: 4\n" +
- "\tinv.numSearchContainerColumns: 4\n" +
- "\tminCellSize: PointF(80.0, 102.0)dp\n" +
- "\tcellWidthPx: 210.0px (80.0dp)\n" +
- "\tcellHeightPx: 267.0px (101.71429dp)\n" +
- "\tgetCellSize().x: 210.0px (80.0dp)\n" +
- "\tgetCellSize().y: 267.0px (101.71429dp)\n" +
- "\tcellLayoutBorderSpacePx Horizontal: 52.0px (19.809525dp)\n" +
- "\tcellLayoutBorderSpacePx Vertical: 52.0px (19.809525dp)\n" +
- "\tcellLayoutPaddingPx.left: 26.0px (9.904762dp)\n" +
- "\tcellLayoutPaddingPx.top: 18.0px (6.857143dp)\n" +
- "\tcellLayoutPaddingPx.right: 26.0px (9.904762dp)\n" +
- "\tcellLayoutPaddingPx.bottom: 26.0px (9.904762dp)\n" +
- "\ticonSizePx: 157.0px (59.809525dp)\n" +
- "\ticonTextSizePx: 36.0px (13.714286dp)\n" +
- "\ticonDrawablePaddingPx: 17.0px (6.4761906dp)\n" +
- "\tfolderCellWidthPx: 210.0px (80.0dp)\n" +
- "\tfolderCellHeightPx: 267.0px (101.71429dp)\n" +
- "\tfolderChildIconSizePx: 158.0px (60.190475dp)\n" +
- "\tfolderChildTextSizePx: 37.0px (14.095238dp)\n" +
- "\tfolderChildDrawablePaddingPx: 19.0px (7.2380953dp)\n" +
- "\tfolderCellLayoutBorderSpaceOriginalPx: 42.0px (16.0dp)\n" +
- "\tfolderCellLayoutBorderSpacePx Horizontal: 42.0px (16.0dp)\n" +
- "\tfolderCellLayoutBorderSpacePx Vertical: 42.0px (16.0dp)\n" +
- "\tbottomSheetTopPadding: 110.0px (41.904762dp)\n" +
- "\tallAppsShiftRange: 1730.0px (659.0476dp)\n" +
- "\tallAppsTopPadding: 110.0px (41.904762dp)\n" +
- "\tallAppsIconSizePx: 157.0px (59.809525dp)\n" +
- "\tallAppsIconTextSizePx: 37.0px (14.095238dp)\n" +
- "\tallAppsIconDrawablePaddingPx: 18.0px (6.857143dp)\n" +
- "\tallAppsCellHeightPx: 315.0px (120.0dp)\n" +
- "\tallAppsCellWidthPx: 210.0px (80.0dp)\n" +
- "\tallAppsBorderSpacePxX: 52.0px (19.809525dp)\n" +
- "\tallAppsBorderSpacePxY: 42.0px (16.0dp)\n" +
- "\tnumShownAllAppsColumns: 6\n" +
- "\tallAppsLeftRightPadding: 137.0px (52.190475dp)\n" +
- "\tallAppsLeftRightMargin: 207.0px (78.85714dp)\n" +
- "\thotseatBarSizePx: 417.0px (158.85715dp)\n" +
- "\tinv.hotseatColumnSpan: 6\n" +
- "\thotseatCellHeightPx: 177.0px (67.42857dp)\n" +
- "\thotseatBarBottomSpacePx: 53.0px (20.190475dp)\n" +
- "\thotseatBarSidePaddingStartPx: 0.0px (0.0dp)\n" +
- "\thotseatBarSidePaddingEndPx: 0.0px (0.0dp)\n" +
- "\thotseatBarEndOffset: 744.0px (283.42856dp)\n" +
- "\thotseatQsbSpace: 74.0px (28.190475dp)\n" +
- "\thotseatQsbHeight: 165.0px (62.857143dp)\n" +
- "\tspringLoadedHotseatBarTopMarginPx: 116.0px (44.190475dp)\n" +
- "\tgetHotseatLayoutPadding(context).top: 197.0px (75.04762dp)\n" +
- "\tgetHotseatLayoutPadding(context).bottom: 43.0px (16.380953dp)\n" +
- "\tgetHotseatLayoutPadding(context).left: 106.0px (40.38095dp)\n" +
- "\tgetHotseatLayoutPadding(context).right: 744.0px (283.42856dp)\n" +
- "\tnumShownHotseatIcons: 6\n" +
- "\thotseatBorderSpace: 83.0px (31.619047dp)\n" +
- "\tisQsbInline: false\n" +
- "\thotseatQsbWidth: 1467.0px (558.8571dp)\n" +
- "\tisTaskbarPresent:true\n" +
- "\tisTaskbarPresentInApps:true\n" +
- "\ttaskbarSize: 158.0px (60.190475dp)\n" +
- "\tdesiredWorkspaceHorizontalMarginPx: 79.0px (30.095238dp)\n" +
- "\tworkspacePadding.left: 53.0px (20.190475dp)\n" +
- "\tworkspacePadding.top: 0.0px (0.0dp)\n" +
- "\tworkspacePadding.right: 53.0px (20.190475dp)\n" +
- "\tworkspacePadding.bottom: 461.0px (175.61905dp)\n" +
- "\ticonScale: 0.99864316px (0.3804355dp)\n" +
- "\tcellScaleToFit : 0.99864316px (0.3804355dp)\n" +
- "\textraSpace: 57.0px (21.714285dp)\n" +
- "\tunscaled extraSpace: 57.077446px (21.74379dp)\n" +
- "\tmaxEmptySpace: 131.0px (49.904762dp)\n" +
- "\tworkspaceTopPadding: 18.0px (6.857143dp)\n" +
- "\tworkspaceBottomPadding: 39.0px (14.857142dp)\n" +
- "\toverviewTaskMarginPx: 32.0px (12.190476dp)\n" +
- "\toverviewTaskIconSizePx: 126.0px (48.0dp)\n" +
- "\toverviewTaskIconDrawableSizePx: 116.0px (44.190475dp)\n" +
- "\toverviewTaskIconDrawableSizeGridPx: 116.0px (44.190475dp)\n" +
- "\toverviewTaskThumbnailTopMarginPx: 158.0px (60.190475dp)\n" +
- "\toverviewActionsTopMarginPx: 32.0px (12.190476dp)\n" +
- "\toverviewActionsHeight: 126.0px (48.0dp)\n" +
- "\toverviewActionsButtonSpacing: 95.0px (36.190475dp)\n" +
- "\toverviewPageSpacing: 95.0px (36.190475dp)\n" +
- "\toverviewRowSpacing: 74.0px (28.190475dp)\n" +
- "\toverviewGridSideMargin: 168.0px (64.0dp)\n" +
- "\tdropTargetBarTopMarginPx: 0.0px (0.0dp)\n" +
- "\tdropTargetBarSizePx: 147.0px (56.0dp)\n" +
- "\tdropTargetBarBottomMarginPx: 42.0px (16.0dp)\n" +
- "\tgetCellLayoutSpringLoadShrunkTop(): 299.0px (113.90476dp)\n" +
- "\tgetCellLayoutSpringLoadShrunkBottom(): 1307.0px (497.90475dp)\n" +
- "\tworkspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)\n" +
- "\tgetWorkspaceSpringLoadScale(): 0.79432625px (0.30260047dp)\n" +
- "\tgetCellLayoutHeight(): 1269.0px (483.42856dp)\n" +
- "\tgetCellLayoutWidth(): 1051.0px (400.38095dp)\n")
- }
-
- @Test
- fun twoPanelLandscape() {
- initializeVarsForTwoPanel(isLandscape = true)
- val dp = newDP()
- dp.isTaskbarPresentInApps = true
-
- assertThat(dump(dp)).isEqualTo("DeviceProfile:\n" +
- "\t1 dp = 2.625 px\n" +
- "\tisTablet:true\n" +
- "\tisPhone:false\n" +
- "\ttransposeLayoutWithOrientation:false\n" +
- "\tisGestureMode:true\n" +
- "\tisLandscape:true\n" +
- "\tisMultiWindowMode:false\n" +
- "\tisTwoPanels:true\n" +
- "\twindowX: 0.0px (0.0dp)\n" +
- "\twindowY: 0.0px (0.0dp)\n" +
- "\twidthPx: 2208.0px (841.1429dp)\n" +
- "\theightPx: 1840.0px (700.9524dp)\n" +
- "\tavailableWidthPx: 2208.0px (841.1429dp)\n" +
- "\tavailableHeightPx: 1730.0px (659.0476dp)\n" +
- "\tmInsets.left: 0.0px (0.0dp)\n" +
- "\tmInsets.top: 110.0px (41.904762dp)\n" +
- "\tmInsets.right: 0.0px (0.0dp)\n" +
- "\tmInsets.bottom: 0.0px (0.0dp)\n" +
- "\taspectRatio:1.2\n" +
- "\tisScalableGrid:true\n" +
- "\tinv.numRows: 4\n" +
- "\tinv.numColumns: 4\n" +
- "\tinv.numSearchContainerColumns: 4\n" +
- "\tminCellSize: PointF(80.0, 102.0)dp\n" +
- "\tcellWidthPx: 210.0px (80.0dp)\n" +
- "\tcellHeightPx: 267.0px (101.71429dp)\n" +
- "\tgetCellSize().x: 210.0px (80.0dp)\n" +
- "\tgetCellSize().y: 267.0px (101.71429dp)\n" +
- "\tcellLayoutBorderSpacePx Horizontal: 52.0px (19.809525dp)\n" +
- "\tcellLayoutBorderSpacePx Vertical: 52.0px (19.809525dp)\n" +
- "\tcellLayoutPaddingPx.left: 26.0px (9.904762dp)\n" +
- "\tcellLayoutPaddingPx.top: 18.0px (6.857143dp)\n" +
- "\tcellLayoutPaddingPx.right: 26.0px (9.904762dp)\n" +
- "\tcellLayoutPaddingPx.bottom: 26.0px (9.904762dp)\n" +
- "\ticonSizePx: 157.0px (59.809525dp)\n" +
- "\ticonTextSizePx: 36.0px (13.714286dp)\n" +
- "\ticonDrawablePaddingPx: 17.0px (6.4761906dp)\n" +
- "\tfolderCellWidthPx: 210.0px (80.0dp)\n" +
- "\tfolderCellHeightPx: 267.0px (101.71429dp)\n" +
- "\tfolderChildIconSizePx: 158.0px (60.190475dp)\n" +
- "\tfolderChildTextSizePx: 37.0px (14.095238dp)\n" +
- "\tfolderChildDrawablePaddingPx: 19.0px (7.2380953dp)\n" +
- "\tfolderCellLayoutBorderSpaceOriginalPx: 42.0px (16.0dp)\n" +
- "\tfolderCellLayoutBorderSpacePx Horizontal: 42.0px (16.0dp)\n" +
- "\tfolderCellLayoutBorderSpacePx Vertical: 42.0px (16.0dp)\n" +
- "\tbottomSheetTopPadding: 110.0px (41.904762dp)\n" +
- "\tallAppsShiftRange: 1730.0px (659.0476dp)\n" +
- "\tallAppsTopPadding: 110.0px (41.904762dp)\n" +
- "\tallAppsIconSizePx: 157.0px (59.809525dp)\n" +
- "\tallAppsIconTextSizePx: 37.0px (14.095238dp)\n" +
- "\tallAppsIconDrawablePaddingPx: 18.0px (6.857143dp)\n" +
- "\tallAppsCellHeightPx: 315.0px (120.0dp)\n" +
- "\tallAppsCellWidthPx: 210.0px (80.0dp)\n" +
- "\tallAppsBorderSpacePxX: 52.0px (19.809525dp)\n" +
- "\tallAppsBorderSpacePxY: 42.0px (16.0dp)\n" +
- "\tnumShownAllAppsColumns: 6\n" +
- "\tallAppsLeftRightPadding: 137.0px (52.190475dp)\n" +
- "\tallAppsLeftRightMargin: 207.0px (78.85714dp)\n" +
- "\thotseatBarSizePx: 417.0px (158.85715dp)\n" +
- "\tinv.hotseatColumnSpan: 6\n" +
- "\thotseatCellHeightPx: 177.0px (67.42857dp)\n" +
- "\thotseatBarBottomSpacePx: 53.0px (20.190475dp)\n" +
- "\thotseatBarSidePaddingStartPx: 0.0px (0.0dp)\n" +
- "\thotseatBarSidePaddingEndPx: 0.0px (0.0dp)\n" +
- "\thotseatBarEndOffset: 0.0px (0.0dp)\n" +
- "\thotseatQsbSpace: 74.0px (28.190475dp)\n" +
- "\thotseatQsbHeight: 165.0px (62.857143dp)\n" +
- "\tspringLoadedHotseatBarTopMarginPx: 116.0px (44.190475dp)\n" +
- "\tgetHotseatLayoutPadding(context).top: 197.0px (75.04762dp)\n" +
- "\tgetHotseatLayoutPadding(context).bottom: 43.0px (16.380953dp)\n" +
- "\tgetHotseatLayoutPadding(context).left: 370.0px (140.95238dp)\n" +
- "\tgetHotseatLayoutPadding(context).right: 370.0px (140.95238dp)\n" +
- "\tnumShownHotseatIcons: 6\n" +
- "\thotseatBorderSpace: 105.0px (40.0dp)\n" +
- "\tisQsbInline: false\n" +
- "\thotseatQsbWidth: 1467.0px (558.8571dp)\n" +
- "\tisTaskbarPresent:true\n" +
- "\tisTaskbarPresentInApps:true\n" +
- "\ttaskbarSize: 158.0px (60.190475dp)\n" +
- "\tdesiredWorkspaceHorizontalMarginPx: 79.0px (30.095238dp)\n" +
- "\tworkspacePadding.left: 53.0px (20.190475dp)\n" +
- "\tworkspacePadding.top: 0.0px (0.0dp)\n" +
- "\tworkspacePadding.right: 53.0px (20.190475dp)\n" +
- "\tworkspacePadding.bottom: 461.0px (175.61905dp)\n" +
- "\ticonScale: 0.99864316px (0.3804355dp)\n" +
- "\tcellScaleToFit : 0.99864316px (0.3804355dp)\n" +
- "\textraSpace: 57.0px (21.714285dp)\n" +
- "\tunscaled extraSpace: 57.077446px (21.74379dp)\n" +
- "\tmaxEmptySpace: 131.0px (49.904762dp)\n" +
- "\tworkspaceTopPadding: 18.0px (6.857143dp)\n" +
- "\tworkspaceBottomPadding: 39.0px (14.857142dp)\n" +
- "\toverviewTaskMarginPx: 32.0px (12.190476dp)\n" +
- "\toverviewTaskIconSizePx: 126.0px (48.0dp)\n" +
- "\toverviewTaskIconDrawableSizePx: 116.0px (44.190475dp)\n" +
- "\toverviewTaskIconDrawableSizeGridPx: 116.0px (44.190475dp)\n" +
- "\toverviewTaskThumbnailTopMarginPx: 158.0px (60.190475dp)\n" +
- "\toverviewActionsTopMarginPx: 32.0px (12.190476dp)\n" +
- "\toverviewActionsHeight: 126.0px (48.0dp)\n" +
- "\toverviewActionsButtonSpacing: 95.0px (36.190475dp)\n" +
- "\toverviewPageSpacing: 95.0px (36.190475dp)\n" +
- "\toverviewRowSpacing: 74.0px (28.190475dp)\n" +
- "\toverviewGridSideMargin: 168.0px (64.0dp)\n" +
- "\tdropTargetBarTopMarginPx: 0.0px (0.0dp)\n" +
- "\tdropTargetBarSizePx: 147.0px (56.0dp)\n" +
- "\tdropTargetBarBottomMarginPx: 42.0px (16.0dp)\n" +
- "\tgetCellLayoutSpringLoadShrunkTop(): 299.0px (113.90476dp)\n" +
- "\tgetCellLayoutSpringLoadShrunkBottom(): 1307.0px (497.90475dp)\n" +
- "\tworkspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)\n" +
- "\tgetWorkspaceSpringLoadScale(): 0.79432625px (0.30260047dp)\n" +
- "\tgetCellLayoutHeight(): 1269.0px (483.42856dp)\n" +
- "\tgetCellLayoutWidth(): 1051.0px (400.38095dp)\n")
- }
-
- @Test
- fun twoPanelPortrait3Button() {
- initializeVarsForTwoPanel(isGestureMode = false)
- val dp = newDP()
- dp.isTaskbarPresentInApps = true
-
- assertThat(dump(dp)).isEqualTo("DeviceProfile:\n" +
- "\t1 dp = 2.625 px\n" +
- "\tisTablet:true\n" +
- "\tisPhone:false\n" +
- "\ttransposeLayoutWithOrientation:false\n" +
- "\tisGestureMode:false\n" +
- "\tisLandscape:false\n" +
- "\tisMultiWindowMode:false\n" +
- "\tisTwoPanels:true\n" +
- "\twindowX: 0.0px (0.0dp)\n" +
- "\twindowY: 0.0px (0.0dp)\n" +
- "\twidthPx: 1840.0px (700.9524dp)\n" +
- "\theightPx: 2208.0px (841.1429dp)\n" +
- "\tavailableWidthPx: 1840.0px (700.9524dp)\n" +
- "\tavailableHeightPx: 2098.0px (799.2381dp)\n" +
- "\tmInsets.left: 0.0px (0.0dp)\n" +
- "\tmInsets.top: 110.0px (41.904762dp)\n" +
- "\tmInsets.right: 0.0px (0.0dp)\n" +
- "\tmInsets.bottom: 0.0px (0.0dp)\n" +
- "\taspectRatio:1.2\n" +
- "\tisScalableGrid:true\n" +
- "\tinv.numRows: 4\n" +
- "\tinv.numColumns: 4\n" +
- "\tinv.numSearchContainerColumns: 4\n" +
- "\tminCellSize: PointF(68.0, 116.0)dp\n" +
- "\tcellWidthPx: 178.0px (67.809525dp)\n" +
- "\tcellHeightPx: 304.0px (115.809525dp)\n" +
- "\tgetCellSize().x: 178.0px (67.809525dp)\n" +
- "\tgetCellSize().y: 304.0px (115.809525dp)\n" +
- "\tcellLayoutBorderSpacePx Horizontal: 42.0px (16.0dp)\n" +
- "\tcellLayoutBorderSpacePx Vertical: 52.0px (19.809525dp)\n" +
- "\tcellLayoutPaddingPx.left: 21.0px (8.0dp)\n" +
- "\tcellLayoutPaddingPx.top: 21.0px (8.0dp)\n" +
- "\tcellLayoutPaddingPx.right: 21.0px (8.0dp)\n" +
- "\tcellLayoutPaddingPx.bottom: 21.0px (8.0dp)\n" +
- "\ticonSizePx: 136.0px (51.809525dp)\n" +
- "\ticonTextSizePx: 31.0px (11.809524dp)\n" +
- "\ticonDrawablePaddingPx: 17.0px (6.4761906dp)\n" +
- "\tfolderCellWidthPx: 192.0px (73.14286dp)\n" +
- "\tfolderCellHeightPx: 304.0px (115.809525dp)\n" +
- "\tfolderChildIconSizePx: 158.0px (60.190475dp)\n" +
- "\tfolderChildTextSizePx: 37.0px (14.095238dp)\n" +
- "\tfolderChildDrawablePaddingPx: 32.0px (12.190476dp)\n" +
- "\tfolderCellLayoutBorderSpaceOriginalPx: 42.0px (16.0dp)\n" +
- "\tfolderCellLayoutBorderSpacePx Horizontal: 42.0px (16.0dp)\n" +
- "\tfolderCellLayoutBorderSpacePx Vertical: 42.0px (16.0dp)\n" +
- "\tbottomSheetTopPadding: 110.0px (41.904762dp)\n" +
- "\tallAppsShiftRange: 2098.0px (799.2381dp)\n" +
- "\tallAppsTopPadding: 110.0px (41.904762dp)\n" +
- "\tallAppsIconSizePx: 136.0px (51.809525dp)\n" +
- "\tallAppsIconTextSizePx: 31.0px (11.809524dp)\n" +
- "\tallAppsIconDrawablePaddingPx: 18.0px (6.857143dp)\n" +
- "\tallAppsCellHeightPx: 345.0px (131.42857dp)\n" +
- "\tallAppsCellWidthPx: 178.0px (67.809525dp)\n" +
- "\tallAppsBorderSpacePxX: 42.0px (16.0dp)\n" +
- "\tallAppsBorderSpacePxY: 73.0px (27.809525dp)\n" +
- "\tnumShownAllAppsColumns: 6\n" +
- "\tallAppsLeftRightPadding: 126.0px (48.0dp)\n" +
- "\tallAppsLeftRightMargin: 155.0px (59.04762dp)\n" +
- "\thotseatBarSizePx: 459.0px (174.85715dp)\n" +
- "\tinv.hotseatColumnSpan: 6\n" +
- "\thotseatCellHeightPx: 153.0px (58.285713dp)\n" +
- "\thotseatBarBottomSpacePx: 95.0px (36.190475dp)\n" +
- "\thotseatBarSidePaddingStartPx: 0.0px (0.0dp)\n" +
- "\thotseatBarSidePaddingEndPx: 0.0px (0.0dp)\n" +
- "\thotseatBarEndOffset: 660.0px (251.42857dp)\n" +
- "\thotseatQsbSpace: 95.0px (36.190475dp)\n" +
- "\thotseatQsbHeight: 165.0px (62.857143dp)\n" +
- "\tspringLoadedHotseatBarTopMarginPx: 171.0px (65.14286dp)\n" +
- "\tgetHotseatLayoutPadding(context).top: 219.0px (83.42857dp)\n" +
- "\tgetHotseatLayoutPadding(context).bottom: 87.0px (33.142857dp)\n" +
- "\tgetHotseatLayoutPadding(context).left: 78.0px (29.714285dp)\n" +
- "\tgetHotseatLayoutPadding(context).right: 660.0px (251.42857dp)\n" +
- "\tnumShownHotseatIcons: 6\n" +
- "\thotseatBorderSpace: 57.0px (21.714285dp)\n" +
- "\tisQsbInline: false\n" +
- "\thotseatQsbWidth: 1236.0px (470.85715dp)\n" +
- "\tisTaskbarPresent:true\n" +
- "\tisTaskbarPresentInApps:true\n" +
- "\ttaskbarSize: 158.0px (60.190475dp)\n" +
- "\tdesiredWorkspaceHorizontalMarginPx: 58.0px (22.095238dp)\n" +
- "\tworkspacePadding.left: 37.0px (14.095238dp)\n" +
- "\tworkspacePadding.top: 68.0px (25.904762dp)\n" +
- "\tworkspacePadding.right: 37.0px (14.095238dp)\n" +
- "\tworkspacePadding.bottom: 615.0px (234.28572dp)\n" +
- "\ticonScale: 0.9978308px (0.38012603dp)\n" +
- "\tcellScaleToFit : 0.9978308px (0.38012603dp)\n" +
- "\textraSpace: 235.0px (89.52381dp)\n" +
- "\tunscaled extraSpace: 235.51086px (89.71842dp)\n" +
- "\tmaxEmptySpace: 236.0px (89.90476dp)\n" +
- "\tworkspaceTopPadding: 89.0px (33.904762dp)\n" +
- "\tworkspaceBottomPadding: 146.0px (55.61905dp)\n" +
- "\toverviewTaskMarginPx: 32.0px (12.190476dp)\n" +
- "\toverviewTaskIconSizePx: 126.0px (48.0dp)\n" +
- "\toverviewTaskIconDrawableSizePx: 116.0px (44.190475dp)\n" +
- "\toverviewTaskIconDrawableSizeGridPx: 116.0px (44.190475dp)\n" +
- "\toverviewTaskThumbnailTopMarginPx: 158.0px (60.190475dp)\n" +
- "\toverviewActionsTopMarginPx: 63.0px (24.0dp)\n" +
- "\toverviewActionsHeight: 126.0px (48.0dp)\n" +
- "\toverviewActionsButtonSpacing: 95.0px (36.190475dp)\n" +
- "\toverviewPageSpacing: 95.0px (36.190475dp)\n" +
- "\toverviewRowSpacing: 74.0px (28.190475dp)\n" +
- "\toverviewGridSideMargin: 168.0px (64.0dp)\n" +
- "\tdropTargetBarTopMarginPx: 168.0px (64.0dp)\n" +
- "\tdropTargetBarSizePx: 147.0px (56.0dp)\n" +
- "\tdropTargetBarBottomMarginPx: 42.0px (16.0dp)\n" +
- "\tgetCellLayoutSpringLoadShrunkTop(): 467.0px (177.90475dp)\n" +
- "\tgetCellLayoutSpringLoadShrunkBottom(): 1578.0px (601.1429dp)\n" +
- "\tworkspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)\n" +
- "\tgetWorkspaceSpringLoadScale(): 0.785159px (0.29910818dp)\n" +
- "\tgetCellLayoutHeight(): 1415.0px (539.0476dp)\n" +
- "\tgetCellLayoutWidth(): 883.0px (336.38095dp)\n")
- }
-
- @Test
- fun twoPanelPortrait() {
- initializeVarsForTwoPanel()
- val dp = newDP()
- dp.isTaskbarPresentInApps = true
-
- assertThat(dump(dp)).isEqualTo("DeviceProfile:\n" +
- "\t1 dp = 2.625 px\n" +
- "\tisTablet:true\n" +
- "\tisPhone:false\n" +
- "\ttransposeLayoutWithOrientation:false\n" +
- "\tisGestureMode:true\n" +
- "\tisLandscape:false\n" +
- "\tisMultiWindowMode:false\n" +
- "\tisTwoPanels:true\n" +
- "\twindowX: 0.0px (0.0dp)\n" +
- "\twindowY: 0.0px (0.0dp)\n" +
- "\twidthPx: 1840.0px (700.9524dp)\n" +
- "\theightPx: 2208.0px (841.1429dp)\n" +
- "\tavailableWidthPx: 1840.0px (700.9524dp)\n" +
- "\tavailableHeightPx: 2098.0px (799.2381dp)\n" +
- "\tmInsets.left: 0.0px (0.0dp)\n" +
- "\tmInsets.top: 110.0px (41.904762dp)\n" +
- "\tmInsets.right: 0.0px (0.0dp)\n" +
- "\tmInsets.bottom: 0.0px (0.0dp)\n" +
- "\taspectRatio:1.2\n" +
- "\tisScalableGrid:true\n" +
- "\tinv.numRows: 4\n" +
- "\tinv.numColumns: 4\n" +
- "\tinv.numSearchContainerColumns: 4\n" +
- "\tminCellSize: PointF(68.0, 116.0)dp\n" +
- "\tcellWidthPx: 178.0px (67.809525dp)\n" +
- "\tcellHeightPx: 304.0px (115.809525dp)\n" +
- "\tgetCellSize().x: 178.0px (67.809525dp)\n" +
- "\tgetCellSize().y: 304.0px (115.809525dp)\n" +
- "\tcellLayoutBorderSpacePx Horizontal: 42.0px (16.0dp)\n" +
- "\tcellLayoutBorderSpacePx Vertical: 52.0px (19.809525dp)\n" +
- "\tcellLayoutPaddingPx.left: 21.0px (8.0dp)\n" +
- "\tcellLayoutPaddingPx.top: 21.0px (8.0dp)\n" +
- "\tcellLayoutPaddingPx.right: 21.0px (8.0dp)\n" +
- "\tcellLayoutPaddingPx.bottom: 21.0px (8.0dp)\n" +
- "\ticonSizePx: 136.0px (51.809525dp)\n" +
- "\ticonTextSizePx: 31.0px (11.809524dp)\n" +
- "\ticonDrawablePaddingPx: 17.0px (6.4761906dp)\n" +
- "\tfolderCellWidthPx: 192.0px (73.14286dp)\n" +
- "\tfolderCellHeightPx: 304.0px (115.809525dp)\n" +
- "\tfolderChildIconSizePx: 158.0px (60.190475dp)\n" +
- "\tfolderChildTextSizePx: 37.0px (14.095238dp)\n" +
- "\tfolderChildDrawablePaddingPx: 32.0px (12.190476dp)\n" +
- "\tfolderCellLayoutBorderSpaceOriginalPx: 42.0px (16.0dp)\n" +
- "\tfolderCellLayoutBorderSpacePx Horizontal: 42.0px (16.0dp)\n" +
- "\tfolderCellLayoutBorderSpacePx Vertical: 42.0px (16.0dp)\n" +
- "\tbottomSheetTopPadding: 110.0px (41.904762dp)\n" +
- "\tallAppsShiftRange: 2098.0px (799.2381dp)\n" +
- "\tallAppsTopPadding: 110.0px (41.904762dp)\n" +
- "\tallAppsIconSizePx: 136.0px (51.809525dp)\n" +
- "\tallAppsIconTextSizePx: 31.0px (11.809524dp)\n" +
- "\tallAppsIconDrawablePaddingPx: 18.0px (6.857143dp)\n" +
- "\tallAppsCellHeightPx: 345.0px (131.42857dp)\n" +
- "\tallAppsCellWidthPx: 178.0px (67.809525dp)\n" +
- "\tallAppsBorderSpacePxX: 42.0px (16.0dp)\n" +
- "\tallAppsBorderSpacePxY: 73.0px (27.809525dp)\n" +
- "\tnumShownAllAppsColumns: 6\n" +
- "\tallAppsLeftRightPadding: 126.0px (48.0dp)\n" +
- "\tallAppsLeftRightMargin: 155.0px (59.04762dp)\n" +
- "\thotseatBarSizePx: 459.0px (174.85715dp)\n" +
- "\tinv.hotseatColumnSpan: 6\n" +
- "\thotseatCellHeightPx: 153.0px (58.285713dp)\n" +
- "\thotseatBarBottomSpacePx: 95.0px (36.190475dp)\n" +
- "\thotseatBarSidePaddingStartPx: 0.0px (0.0dp)\n" +
- "\thotseatBarSidePaddingEndPx: 0.0px (0.0dp)\n" +
- "\thotseatBarEndOffset: 0.0px (0.0dp)\n" +
- "\thotseatQsbSpace: 95.0px (36.190475dp)\n" +
- "\thotseatQsbHeight: 165.0px (62.857143dp)\n" +
- "\tspringLoadedHotseatBarTopMarginPx: 171.0px (65.14286dp)\n" +
- "\tgetHotseatLayoutPadding(context).top: 219.0px (83.42857dp)\n" +
- "\tgetHotseatLayoutPadding(context).bottom: 87.0px (33.142857dp)\n" +
- "\tgetHotseatLayoutPadding(context).left: 302.0px (115.04762dp)\n" +
- "\tgetHotseatLayoutPadding(context).right: 302.0px (115.04762dp)\n" +
- "\tnumShownHotseatIcons: 6\n" +
- "\thotseatBorderSpace: 84.0px (32.0dp)\n" +
- "\tisQsbInline: false\n" +
- "\thotseatQsbWidth: 1236.0px (470.85715dp)\n" +
- "\tisTaskbarPresent:true\n" +
- "\tisTaskbarPresentInApps:true\n" +
- "\ttaskbarSize: 158.0px (60.190475dp)\n" +
- "\tdesiredWorkspaceHorizontalMarginPx: 58.0px (22.095238dp)\n" +
- "\tworkspacePadding.left: 37.0px (14.095238dp)\n" +
- "\tworkspacePadding.top: 68.0px (25.904762dp)\n" +
- "\tworkspacePadding.right: 37.0px (14.095238dp)\n" +
- "\tworkspacePadding.bottom: 615.0px (234.28572dp)\n" +
- "\ticonScale: 0.9978308px (0.38012603dp)\n" +
- "\tcellScaleToFit : 0.9978308px (0.38012603dp)\n" +
- "\textraSpace: 235.0px (89.52381dp)\n" +
- "\tunscaled extraSpace: 235.51086px (89.71842dp)\n" +
- "\tmaxEmptySpace: 236.0px (89.90476dp)\n" +
- "\tworkspaceTopPadding: 89.0px (33.904762dp)\n" +
- "\tworkspaceBottomPadding: 146.0px (55.61905dp)\n" +
- "\toverviewTaskMarginPx: 32.0px (12.190476dp)\n" +
- "\toverviewTaskIconSizePx: 126.0px (48.0dp)\n" +
- "\toverviewTaskIconDrawableSizePx: 116.0px (44.190475dp)\n" +
- "\toverviewTaskIconDrawableSizeGridPx: 116.0px (44.190475dp)\n" +
- "\toverviewTaskThumbnailTopMarginPx: 158.0px (60.190475dp)\n" +
- "\toverviewActionsTopMarginPx: 63.0px (24.0dp)\n" +
- "\toverviewActionsHeight: 126.0px (48.0dp)\n" +
- "\toverviewActionsButtonSpacing: 95.0px (36.190475dp)\n" +
- "\toverviewPageSpacing: 95.0px (36.190475dp)\n" +
- "\toverviewRowSpacing: 74.0px (28.190475dp)\n" +
- "\toverviewGridSideMargin: 168.0px (64.0dp)\n" +
- "\tdropTargetBarTopMarginPx: 168.0px (64.0dp)\n" +
- "\tdropTargetBarSizePx: 147.0px (56.0dp)\n" +
- "\tdropTargetBarBottomMarginPx: 42.0px (16.0dp)\n" +
- "\tgetCellLayoutSpringLoadShrunkTop(): 467.0px (177.90475dp)\n" +
- "\tgetCellLayoutSpringLoadShrunkBottom(): 1578.0px (601.1429dp)\n" +
- "\tworkspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)\n" +
- "\tgetWorkspaceSpringLoadScale(): 0.785159px (0.29910818dp)\n" +
- "\tgetCellLayoutHeight(): 1415.0px (539.0476dp)\n" +
- "\tgetCellLayoutWidth(): 883.0px (336.38095dp)\n")
- }
-
- @Test
- fun phoneVerticalBar3Button() {
- initializeVarsForPhone(isVerticalBar = true, isGestureMode = false)
- val dp = newDP()
-
- assertThat(dump(dp)).isEqualTo("DeviceProfile:\n" +
- "\t1 dp = 2.625 px\n" +
- "\tisTablet:false\n" +
- "\tisPhone:true\n" +
- "\ttransposeLayoutWithOrientation:true\n" +
- "\tisGestureMode:false\n" +
- "\tisLandscape:true\n" +
- "\tisMultiWindowMode:false\n" +
- "\tisTwoPanels:false\n" +
- "\twindowX: 0.0px (0.0dp)\n" +
- "\twindowY: 0.0px (0.0dp)\n" +
- "\twidthPx: 2400.0px (914.2857dp)\n" +
- "\theightPx: 1080.0px (411.42856dp)\n" +
- "\tavailableWidthPx: 2156.0px (821.3333dp)\n" +
- "\tavailableHeightPx: 1006.0px (383.2381dp)\n" +
- "\tmInsets.left: 118.0px (44.95238dp)\n" +
- "\tmInsets.top: 74.0px (28.190475dp)\n" +
- "\tmInsets.right: 126.0px (48.0dp)\n" +
- "\tmInsets.bottom: 0.0px (0.0dp)\n" +
- "\taspectRatio:2.2222223\n" +
- "\tisScalableGrid:false\n" +
- "\tinv.numRows: 5\n" +
- "\tinv.numColumns: 4\n" +
- "\tinv.numSearchContainerColumns: 4\n" +
- "\tminCellSize: PointF(80.0, 104.0)dp\n" +
- "\tcellWidthPx: 153.0px (58.285713dp)\n" +
- "\tcellHeightPx: 160.0px (60.95238dp)\n" +
- "\tgetCellSize().x: 461.0px (175.61905dp)\n" +
- "\tgetCellSize().y: 193.0px (73.52381dp)\n" +
- "\tcellLayoutBorderSpacePx Horizontal: 0.0px (0.0dp)\n" +
- "\tcellLayoutBorderSpacePx Vertical: 0.0px (0.0dp)\n" +
- "\tcellLayoutPaddingPx.left: 53.0px (20.190475dp)\n" +
- "\tcellLayoutPaddingPx.top: 0.0px (0.0dp)\n" +
- "\tcellLayoutPaddingPx.right: 53.0px (20.190475dp)\n" +
- "\tcellLayoutPaddingPx.bottom: 40.0px (15.238095dp)\n" +
- "\ticonSizePx: 142.0px (54.095238dp)\n" +
- "\ticonTextSizePx: 0.0px (0.0dp)\n" +
- "\ticonDrawablePaddingPx: 0.0px (0.0dp)\n" +
- "\tfolderCellWidthPx: 179.0px (68.190475dp)\n" +
- "\tfolderCellHeightPx: 212.0px (80.7619dp)\n" +
- "\tfolderChildIconSizePx: 135.0px (51.42857dp)\n" +
- "\tfolderChildTextSizePx: 35.0px (13.333333dp)\n" +
- "\tfolderChildDrawablePaddingPx: 10.0px (3.8095238dp)\n" +
- "\tfolderCellLayoutBorderSpaceOriginalPx: 42.0px (16.0dp)\n" +
- "\tfolderCellLayoutBorderSpacePx Horizontal: 42.0px (16.0dp)\n" +
- "\tfolderCellLayoutBorderSpacePx Vertical: 42.0px (16.0dp)\n" +
- "\tbottomSheetTopPadding: 114.0px (43.42857dp)\n" +
- "\tallAppsShiftRange: 788.0px (300.1905dp)\n" +
- "\tallAppsTopPadding: 0.0px (0.0dp)\n" +
- "\tallAppsIconSizePx: 158.0px (60.190475dp)\n" +
- "\tallAppsIconTextSizePx: 37.0px (14.095238dp)\n" +
- "\tallAppsIconDrawablePaddingPx: 21.0px (8.0dp)\n" +
- "\tallAppsCellHeightPx: 329.0px (125.333336dp)\n" +
- "\tallAppsCellWidthPx: 200.0px (76.190475dp)\n" +
- "\tallAppsBorderSpacePxX: 42.0px (16.0dp)\n" +
- "\tallAppsBorderSpacePxY: 42.0px (16.0dp)\n" +
- "\tnumShownAllAppsColumns: 4\n" +
- "\tallAppsLeftRightPadding: 0.0px (0.0dp)\n" +
- "\tallAppsLeftRightMargin: 0.0px (0.0dp)\n" +
- "\thotseatBarSizePx: 247.0px (94.09524dp)\n" +
- "\tinv.hotseatColumnSpan: 4\n" +
- "\thotseatCellHeightPx: 160.0px (60.95238dp)\n" +
- "\thotseatBarBottomSpacePx: 126.0px (48.0dp)\n" +
- "\thotseatBarSidePaddingStartPx: 63.0px (24.0dp)\n" +
- "\thotseatBarSidePaddingEndPx: 42.0px (16.0dp)\n" +
- "\thotseatBarEndOffset: 0.0px (0.0dp)\n" +
- "\thotseatQsbSpace: 95.0px (36.190475dp)\n" +
- "\thotseatQsbHeight: 165.0px (62.857143dp)\n" +
- "\tspringLoadedHotseatBarTopMarginPx: 118.0px (44.95238dp)\n" +
- "\tgetHotseatLayoutPadding(context).top: 65.0px (24.761906dp)\n" +
- "\tgetHotseatLayoutPadding(context).bottom: 48.0px (18.285715dp)\n" +
- "\tgetHotseatLayoutPadding(context).left: 42.0px (16.0dp)\n" +
- "\tgetHotseatLayoutPadding(context).right: 189.0px (72.0dp)\n" +
- "\tnumShownHotseatIcons: 4\n" +
- "\thotseatBorderSpace: 0.0px (0.0dp)\n" +
- "\tisQsbInline: false\n" +
- "\thotseatQsbWidth: 1525.0px (580.9524dp)\n" +
- "\tisTaskbarPresent:false\n" +
- "\tisTaskbarPresentInApps:false\n" +
- "\ttaskbarSize: 0.0px (0.0dp)\n" +
- "\tdesiredWorkspaceHorizontalMarginPx: 0.0px (0.0dp)\n" +
- "\tworkspacePadding.left: 10.0px (3.8095238dp)\n" +
- "\tworkspacePadding.top: 0.0px (0.0dp)\n" +
- "\tworkspacePadding.right: 194.0px (73.90476dp)\n" +
- "\tworkspacePadding.bottom: 0.0px (0.0dp)\n" +
- "\ticonScale: 1.0px (0.3809524dp)\n" +
- "\tcellScaleToFit : 1.0px (0.3809524dp)\n" +
- "\textraSpace: 166.0px (63.238094dp)\n" +
- "\tunscaled extraSpace: 166.0px (63.238094dp)\n" +
- "\tmaxEmptySpace: 184.0px (70.09524dp)\n" +
- "\tworkspaceTopPadding: 0.0px (0.0dp)\n" +
- "\tworkspaceBottomPadding: 0.0px (0.0dp)\n" +
- "\toverviewTaskMarginPx: 42.0px (16.0dp)\n" +
- "\toverviewTaskIconSizePx: 126.0px (48.0dp)\n" +
- "\toverviewTaskIconDrawableSizePx: 116.0px (44.190475dp)\n" +
- "\toverviewTaskIconDrawableSizeGridPx: 0.0px (0.0dp)\n" +
- "\toverviewTaskThumbnailTopMarginPx: 168.0px (64.0dp)\n" +
- "\toverviewActionsTopMarginPx: 32.0px (12.190476dp)\n" +
- "\toverviewActionsHeight: 126.0px (48.0dp)\n" +
- "\toverviewActionsButtonSpacing: 95.0px (36.190475dp)\n" +
- "\toverviewPageSpacing: 42.0px (16.0dp)\n" +
- "\toverviewRowSpacing: 0.0px (0.0dp)\n" +
- "\toverviewGridSideMargin: 0.0px (0.0dp)\n" +
- "\tdropTargetBarTopMarginPx: 16.0px (6.095238dp)\n" +
- "\tdropTargetBarSizePx: 95.0px (36.190475dp)\n" +
- "\tdropTargetBarBottomMarginPx: 16.0px (6.095238dp)\n" +
- "\tgetCellLayoutSpringLoadShrunkTop(): 201.0px (76.57143dp)\n" +
- "\tgetCellLayoutSpringLoadShrunkBottom(): 983.0px (374.4762dp)\n" +
- "\tworkspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)\n" +
- "\tgetWorkspaceSpringLoadScale(): 0.777336px (0.296128dp)\n" +
- "\tgetCellLayoutHeight(): 1006.0px (383.2381dp)\n" +
- "\tgetCellLayoutWidth(): 1952.0px (743.619dp)\n")
- }
-
- @Test
- fun phoneVerticalBar() {
- initializeVarsForPhone(isVerticalBar = true)
- val dp = newDP()
-
- assertThat(dump(dp)).isEqualTo("DeviceProfile:\n" +
- "\t1 dp = 2.625 px\n" +
- "\tisTablet:false\n" +
- "\tisPhone:true\n" +
- "\ttransposeLayoutWithOrientation:true\n" +
- "\tisGestureMode:true\n" +
- "\tisLandscape:true\n" +
- "\tisMultiWindowMode:false\n" +
- "\tisTwoPanels:false\n" +
- "\twindowX: 0.0px (0.0dp)\n" +
- "\twindowY: 0.0px (0.0dp)\n" +
- "\twidthPx: 2400.0px (914.2857dp)\n" +
- "\theightPx: 1080.0px (411.42856dp)\n" +
- "\tavailableWidthPx: 2282.0px (869.3333dp)\n" +
- "\tavailableHeightPx: 943.0px (359.2381dp)\n" +
- "\tmInsets.left: 118.0px (44.95238dp)\n" +
- "\tmInsets.top: 74.0px (28.190475dp)\n" +
- "\tmInsets.right: 0.0px (0.0dp)\n" +
- "\tmInsets.bottom: 63.0px (24.0dp)\n" +
- "\taspectRatio:2.2222223\n" +
- "\tisScalableGrid:false\n" +
- "\tinv.numRows: 5\n" +
- "\tinv.numColumns: 4\n" +
- "\tinv.numSearchContainerColumns: 4\n" +
- "\tminCellSize: PointF(80.0, 104.0)dp\n" +
- "\tcellWidthPx: 153.0px (58.285713dp)\n" +
- "\tcellHeightPx: 160.0px (60.95238dp)\n" +
- "\tgetCellSize().x: 493.0px (187.80952dp)\n" +
- "\tgetCellSize().y: 180.0px (68.57143dp)\n" +
- "\tcellLayoutBorderSpacePx Horizontal: 0.0px (0.0dp)\n" +
- "\tcellLayoutBorderSpacePx Vertical: 0.0px (0.0dp)\n" +
- "\tcellLayoutPaddingPx.left: 53.0px (20.190475dp)\n" +
- "\tcellLayoutPaddingPx.top: 0.0px (0.0dp)\n" +
- "\tcellLayoutPaddingPx.right: 53.0px (20.190475dp)\n" +
- "\tcellLayoutPaddingPx.bottom: 40.0px (15.238095dp)\n" +
- "\ticonSizePx: 142.0px (54.095238dp)\n" +
- "\ticonTextSizePx: 0.0px (0.0dp)\n" +
- "\ticonDrawablePaddingPx: 0.0px (0.0dp)\n" +
- "\tfolderCellWidthPx: 163.0px (62.095238dp)\n" +
- "\tfolderCellHeightPx: 192.0px (73.14286dp)\n" +
- "\tfolderChildIconSizePx: 123.0px (46.857143dp)\n" +
- "\tfolderChildTextSizePx: 32.0px (12.190476dp)\n" +
- "\tfolderChildDrawablePaddingPx: 8.0px (3.047619dp)\n" +
- "\tfolderCellLayoutBorderSpaceOriginalPx: 42.0px (16.0dp)\n" +
- "\tfolderCellLayoutBorderSpacePx Horizontal: 42.0px (16.0dp)\n" +
- "\tfolderCellLayoutBorderSpacePx Vertical: 42.0px (16.0dp)\n" +
- "\tbottomSheetTopPadding: 114.0px (43.42857dp)\n" +
- "\tallAppsShiftRange: 788.0px (300.1905dp)\n" +
- "\tallAppsTopPadding: 0.0px (0.0dp)\n" +
- "\tallAppsIconSizePx: 158.0px (60.190475dp)\n" +
- "\tallAppsIconTextSizePx: 37.0px (14.095238dp)\n" +
- "\tallAppsIconDrawablePaddingPx: 21.0px (8.0dp)\n" +
- "\tallAppsCellHeightPx: 329.0px (125.333336dp)\n" +
- "\tallAppsCellWidthPx: 200.0px (76.190475dp)\n" +
- "\tallAppsBorderSpacePxX: 42.0px (16.0dp)\n" +
- "\tallAppsBorderSpacePxY: 42.0px (16.0dp)\n" +
- "\tnumShownAllAppsColumns: 4\n" +
- "\tallAppsLeftRightPadding: 0.0px (0.0dp)\n" +
- "\tallAppsLeftRightMargin: 0.0px (0.0dp)\n" +
- "\thotseatBarSizePx: 247.0px (94.09524dp)\n" +
- "\tinv.hotseatColumnSpan: 4\n" +
- "\thotseatCellHeightPx: 160.0px (60.95238dp)\n" +
- "\thotseatBarBottomSpacePx: 126.0px (48.0dp)\n" +
- "\thotseatBarSidePaddingStartPx: 63.0px (24.0dp)\n" +
- "\thotseatBarSidePaddingEndPx: 42.0px (16.0dp)\n" +
- "\thotseatBarEndOffset: 0.0px (0.0dp)\n" +
- "\thotseatQsbSpace: 95.0px (36.190475dp)\n" +
- "\thotseatQsbHeight: 165.0px (62.857143dp)\n" +
- "\tspringLoadedHotseatBarTopMarginPx: 118.0px (44.95238dp)\n" +
- "\tgetHotseatLayoutPadding(context).top: 65.0px (24.761906dp)\n" +
- "\tgetHotseatLayoutPadding(context).bottom: 111.0px (42.285713dp)\n" +
- "\tgetHotseatLayoutPadding(context).left: 42.0px (16.0dp)\n" +
- "\tgetHotseatLayoutPadding(context).right: 63.0px (24.0dp)\n" +
- "\tnumShownHotseatIcons: 4\n" +
- "\thotseatBorderSpace: 0.0px (0.0dp)\n" +
- "\tisQsbInline: false\n" +
- "\thotseatQsbWidth: 1621.0px (617.5238dp)\n" +
- "\tisTaskbarPresent:false\n" +
- "\tisTaskbarPresentInApps:false\n" +
- "\ttaskbarSize: 0.0px (0.0dp)\n" +
- "\tdesiredWorkspaceHorizontalMarginPx: 0.0px (0.0dp)\n" +
- "\tworkspacePadding.left: 10.0px (3.8095238dp)\n" +
- "\tworkspacePadding.top: 0.0px (0.0dp)\n" +
- "\tworkspacePadding.right: 194.0px (73.90476dp)\n" +
- "\tworkspacePadding.bottom: 0.0px (0.0dp)\n" +
- "\ticonScale: 1.0px (0.3809524dp)\n" +
- "\tcellScaleToFit : 1.0px (0.3809524dp)\n" +
- "\textraSpace: 103.0px (39.238094dp)\n" +
- "\tunscaled extraSpace: 103.0px (39.238094dp)\n" +
- "\tmaxEmptySpace: 131.0px (49.904762dp)\n" +
- "\tworkspaceTopPadding: 0.0px (0.0dp)\n" +
- "\tworkspaceBottomPadding: 0.0px (0.0dp)\n" +
- "\toverviewTaskMarginPx: 42.0px (16.0dp)\n" +
- "\toverviewTaskIconSizePx: 126.0px (48.0dp)\n" +
- "\toverviewTaskIconDrawableSizePx: 116.0px (44.190475dp)\n" +
- "\toverviewTaskIconDrawableSizeGridPx: 0.0px (0.0dp)\n" +
- "\toverviewTaskThumbnailTopMarginPx: 168.0px (64.0dp)\n" +
- "\toverviewActionsTopMarginPx: 32.0px (12.190476dp)\n" +
- "\toverviewActionsHeight: 126.0px (48.0dp)\n" +
- "\toverviewActionsButtonSpacing: 95.0px (36.190475dp)\n" +
- "\toverviewPageSpacing: 42.0px (16.0dp)\n" +
- "\toverviewRowSpacing: 0.0px (0.0dp)\n" +
- "\toverviewGridSideMargin: 0.0px (0.0dp)\n" +
- "\tdropTargetBarTopMarginPx: 16.0px (6.095238dp)\n" +
- "\tdropTargetBarSizePx: 95.0px (36.190475dp)\n" +
- "\tdropTargetBarBottomMarginPx: 16.0px (6.095238dp)\n" +
- "\tgetCellLayoutSpringLoadShrunkTop(): 201.0px (76.57143dp)\n" +
- "\tgetCellLayoutSpringLoadShrunkBottom(): 927.0px (353.14285dp)\n" +
- "\tworkspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)\n" +
- "\tgetWorkspaceSpringLoadScale(): 0.76988333px (0.2932889dp)\n" +
- "\tgetCellLayoutHeight(): 943.0px (359.2381dp)\n" +
- "\tgetCellLayoutWidth(): 2078.0px (791.619dp)\n")
- }
-}
\ No newline at end of file
diff --git a/quickstep/tests/src/com/android/quickstep/FullscreenDrawParamsTest.kt b/quickstep/tests/src/com/android/quickstep/FullscreenDrawParamsTest.kt
new file mode 100644
index 0000000..6d47281
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/FullscreenDrawParamsTest.kt
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep
+
+import android.graphics.Rect
+import android.graphics.RectF
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.DeviceProfileBaseTest
+import com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT
+import com.android.quickstep.views.TaskView.FullscreenDrawParams
+import com.android.systemui.shared.recents.model.ThumbnailData
+import com.android.systemui.shared.recents.utilities.PreviewPositionHelper
+import com.android.wm.shell.util.SplitBounds
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
+import kotlin.math.roundToInt
+
+/**
+ * Test for FullscreenDrawParams class.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class FullscreenDrawParamsTest : DeviceProfileBaseTest() {
+
+ private val TASK_SCALE = 0.7f
+ private var mThumbnailData: ThumbnailData = mock(ThumbnailData::class.java)
+
+ private val mPreviewPositionHelper = PreviewPositionHelper()
+ private lateinit var params: FullscreenDrawParams
+
+ @Before
+ fun setup() {
+ params = FullscreenDrawParams(context)
+ }
+
+ @Test
+ fun setFullProgress_currentDrawnInsets_clipTaskbarSizeFromBottomForTablets() {
+ initializeVarsForTablet()
+ val dp = newDP()
+ val previewRect = Rect(0, 0, 100, 100)
+ val canvasWidth = (dp.widthPx * TASK_SCALE).roundToInt()
+ val canvasHeight = (dp.heightPx * TASK_SCALE).roundToInt()
+ val currentRotation = 0
+ val isRtl = false
+
+ mPreviewPositionHelper.updateThumbnailMatrix(previewRect, mThumbnailData, canvasWidth,
+ canvasHeight, dp.widthPx, dp.heightPx, dp.taskbarSize, dp.isTablet, currentRotation,
+ isRtl)
+ params.setProgress(/* fullscreenProgress= */ 1.0f, /* parentScale= */ 1.0f,
+ /* taskViewScale= */ 1.0f, /* previewWidth= */ 0, dp, mPreviewPositionHelper)
+
+ val expectedClippedInsets = RectF(0f, 0f, 0f, dp.taskbarSize * TASK_SCALE)
+ assertThat(params.mCurrentDrawnInsets)
+ .isEqualTo(expectedClippedInsets)
+ }
+
+ @Test
+ fun setFullProgress_currentDrawnInsets_clipTaskbarSizeFromBottomForTablets_splitPortrait() {
+ initializeVarsForTablet()
+ val dp = newDP()
+ val previewRect = Rect(0, 0, 100, 100)
+ val canvasWidth = (dp.widthPx * TASK_SCALE).roundToInt()
+ val canvasHeight = (dp.heightPx * TASK_SCALE / 2).roundToInt()
+ val currentRotation = 0
+ val isRtl = false
+ // portrait/vertical split apps
+ val dividerSize = 10
+ val splitBounds = SplitBounds(
+ Rect(0, 0, dp.widthPx, (dp.heightPx - dividerSize) / 2),
+ Rect(0, (dp.heightPx + dividerSize) / 2, dp.widthPx, dp.heightPx),
+ 0 /*lefTopTaskId*/, 0 /*rightBottomTaskId*/)
+ mPreviewPositionHelper.setSplitBounds(splitBounds, STAGE_POSITION_BOTTOM_OR_RIGHT)
+
+ mPreviewPositionHelper.updateThumbnailMatrix(previewRect, mThumbnailData, canvasWidth,
+ canvasHeight, dp.widthPx, dp.heightPx, dp.taskbarSize, dp.isTablet, currentRotation,
+ isRtl)
+ params.setProgress(/* fullscreenProgress= */ 1.0f, /* parentScale= */ 1.0f,
+ /* taskViewScale= */ 1.0f, /* previewWidth= */ 0, dp, mPreviewPositionHelper)
+
+ assertThat(params.mCurrentDrawnInsets.bottom)
+ .isWithin(1f).of((dp.taskbarSize * TASK_SCALE))
+ }
+
+ @Test
+ fun setFullProgress_currentDrawnInsets_clipTaskbarSizeFromBottomForTablets_splitLandscape() {
+ initializeVarsForTablet(isLandscape = true)
+ val dp = newDP()
+ val previewRect = Rect(0, 0, 100, 100)
+ val canvasWidth = (dp.widthPx * TASK_SCALE / 2).roundToInt()
+ val canvasHeight = (dp.heightPx * TASK_SCALE).roundToInt()
+ val currentRotation = 0
+ val isRtl = false
+ // portrait/vertical split apps
+ val dividerSize = 10
+ val splitBounds = SplitBounds(
+ Rect(0, 0, (dp.widthPx - dividerSize) / 2, dp.heightPx),
+ Rect((dp.widthPx + dividerSize) / 2, 0, dp.widthPx, dp.heightPx),
+ 0 /*lefTopTaskId*/, 0 /*rightBottomTaskId*/)
+ mPreviewPositionHelper.setSplitBounds(splitBounds, STAGE_POSITION_BOTTOM_OR_RIGHT)
+
+ mPreviewPositionHelper.updateThumbnailMatrix(previewRect, mThumbnailData, canvasWidth,
+ canvasHeight, dp.widthPx, dp.heightPx, dp.taskbarSize, dp.isTablet, currentRotation,
+ isRtl)
+ params.setProgress(/* fullscreenProgress= */ 1.0f, /* parentScale= */ 1.0f,
+ /* taskViewScale= */ 1.0f, /* previewWidth= */ 0, dp, mPreviewPositionHelper)
+
+ assertThat(params.mCurrentDrawnInsets.bottom)
+ .isWithin(1f).of((dp.taskbarSize * TASK_SCALE))
+ }
+
+ @Test
+ fun setFullProgress_currentDrawnInsets_doNotClipTaskbarSizeFromBottomForPhones() {
+ initializeVarsForPhone()
+ val dp = newDP()
+ val previewRect = Rect(0, 0, 100, 100)
+ val canvasWidth = (dp.widthPx * TASK_SCALE).roundToInt()
+ val canvasHeight = (dp.heightPx * TASK_SCALE).roundToInt()
+ val currentRotation = 0
+ val isRtl = false
+
+ mPreviewPositionHelper.updateThumbnailMatrix(previewRect, mThumbnailData, canvasWidth,
+ canvasHeight, dp.widthPx, dp.heightPx, dp.taskbarSize, dp.isTablet, currentRotation,
+ isRtl)
+ params.setProgress(/* fullscreenProgress= */ 1.0f, /* parentScale= */ 1.0f,
+ /* taskViewScale= */ 1.0f, /* previewWidth= */ 0, dp, mPreviewPositionHelper)
+
+ val expectedClippedInsets = RectF(0f, 0f, 0f, 0f)
+ assertThat(params.mCurrentDrawnInsets)
+ .isEqualTo(expectedClippedInsets)
+ }
+}
\ No newline at end of file
diff --git a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
index e5e2cf3..eded1c9 100644
--- a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
+++ b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
@@ -51,7 +51,7 @@
/**
* Test rule that allows executing a test with Quickstep on and then Quickstep off.
- * The test should be annotated with @QuickstepOnOff.
+ * The test should be annotated with @NavigationModeSwitch.
*/
public class NavigationModeSwitchRule implements TestRule {
diff --git a/quickstep/tests/src/com/android/quickstep/OrientationTouchTransformerTest.java b/quickstep/tests/src/com/android/quickstep/OrientationTouchTransformerTest.java
index c822578..9c240f0 100644
--- a/quickstep/tests/src/com/android/quickstep/OrientationTouchTransformerTest.java
+++ b/quickstep/tests/src/com/android/quickstep/OrientationTouchTransformerTest.java
@@ -19,7 +19,7 @@
import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
-import static com.android.launcher3.util.DisplayController.NavigationMode.NO_BUTTON;
+import static com.android.launcher3.util.NavigationMode.NO_BUTTON;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
diff --git a/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java b/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java
index 4e49716..ed5526f 100644
--- a/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java
+++ b/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java
@@ -26,12 +26,12 @@
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
+import android.app.KeyguardManager;
import androidx.test.filters.SmallTest;
import com.android.launcher3.util.LooperExecutor;
import com.android.quickstep.util.GroupTask;
-import com.android.systemui.shared.system.KeyguardManagerCompat;
import com.android.wm.shell.util.GroupedRecentTaskInfo;
import org.junit.Before;
@@ -56,8 +56,8 @@
public void setup() {
MockitoAnnotations.initMocks(this);
LooperExecutor mockMainThreadExecutor = mock(LooperExecutor.class);
- KeyguardManagerCompat mockKeyguardManagerCompat = mock(KeyguardManagerCompat.class);
- mRecentTasksList = new RecentTasksList(mockMainThreadExecutor, mockKeyguardManagerCompat,
+ KeyguardManager mockKeyguardManager = mock(KeyguardManager.class);
+ mRecentTasksList = new RecentTasksList(mockMainThreadExecutor, mockKeyguardManager,
mockSystemUiProxy);
}
@@ -70,7 +70,7 @@
@Test
public void loadTasksInBackground_onlyKeys_noValidTaskDescription() {
- GroupedRecentTaskInfo recentTaskInfos = new GroupedRecentTaskInfo(
+ GroupedRecentTaskInfo recentTaskInfos = GroupedRecentTaskInfo.forSplitTasks(
new ActivityManager.RecentTaskInfo(), new ActivityManager.RecentTaskInfo(), null);
when(mockSystemUiProxy.getRecentTasks(anyInt(), anyInt()))
.thenReturn(new ArrayList<>(Collections.singletonList(recentTaskInfos)));
@@ -90,8 +90,8 @@
task1.taskDescription = new ActivityManager.TaskDescription(taskDescription);
ActivityManager.RecentTaskInfo task2 = new ActivityManager.RecentTaskInfo();
task2.taskDescription = new ActivityManager.TaskDescription();
- GroupedRecentTaskInfo recentTaskInfos = new GroupedRecentTaskInfo(
- task1, task2, null);
+ GroupedRecentTaskInfo recentTaskInfos = GroupedRecentTaskInfo.forSplitTasks(task1, task2,
+ null);
when(mockSystemUiProxy.getRecentTasks(anyInt(), anyInt()))
.thenReturn(new ArrayList<>(Collections.singletonList(recentTaskInfos)));
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index 159b65f..cc561c6 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -229,6 +229,7 @@
@PortraitLandscape
@ScreenRecord // b/238461765
public void testSwitchToOverview() throws Exception {
+ startTestAppsWithCheck();
assertNotNull("Workspace.switchToOverview() returned null",
mLauncher.goHome().switchToOverview());
assertTrue("Launcher internal state didn't switch to Overview",
@@ -308,6 +309,28 @@
launchedAppState.switchToOverview();
}
+ @Test
+ @ScreenRecord // b/242163205
+ public void testQuickSwitchToPreviousAppForTablet() throws Exception {
+ assumeTrue(mLauncher.isTablet());
+ startTestActivity(2);
+ startImeTestActivity();
+
+ // Set ignoreTaskbarVisibility to true to verify the task bar visibility explicitly.
+ mLauncher.setIgnoreTaskbarVisibility(true);
+
+ // Expect task bar invisible when the launched app was the IME activity.
+ LaunchedAppState launchedAppState = getAndAssertLaunchedApp();
+ launchedAppState.assertTaskbarHidden();
+
+ // Quick-switch to the test app with swiping to right.
+ launchedAppState.quickSwitchToPreviousApp();
+
+ // Expect task bar visible when the launched app was the test activity.
+ launchedAppState = getAndAssertLaunchedApp();
+ launchedAppState.assertTaskbarVisible();
+ }
+
private boolean isTestActivityRunning(int activityNumber) {
return mDevice.wait(Until.hasObject(By.pkg(getAppPackageName())
.text("TestActivity" + activityNumber)),
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java b/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java
index 1df9c02..0b8bc10 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java
@@ -17,6 +17,8 @@
import static androidx.test.InstrumentationRegistry.getInstrumentation;
+import static com.android.quickstep.TaskbarModeSwitchRule.Mode.PERSISTENT;
+
import static junit.framework.TestCase.assertEquals;
import android.content.Intent;
@@ -27,6 +29,7 @@
import com.android.launcher3.tapl.Taskbar;
import com.android.launcher3.ui.TaplTestsLauncher3;
import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
+import com.android.quickstep.TaskbarModeSwitchRule.TaskbarModeSwitch;
import org.junit.After;
import org.junit.Assume;
@@ -53,31 +56,45 @@
TaplTestsLauncher3.initialize(this);
startAppFast(CALCULATOR_APP_PACKAGE);
+ mLauncher.enableBlockTimeout(true);
mLauncher.showTaskbarIfHidden();
}
@After
public void tearDown() {
mLauncher.useDefaultWorkspaceLayoutOnReload();
+ mLauncher.enableBlockTimeout(false);
}
@Test
+ @TaskbarModeSwitch(mode = PERSISTENT)
public void testHideShowTaskbar() {
getTaskbar().hide();
mLauncher.getLaunchedAppState().showTaskbar();
}
@Test
+ @TaskbarModeSwitch(mode = PERSISTENT)
+ public void testHideTaskbarPersistsOnRecreate() {
+ getTaskbar().hide();
+ mLauncher.recreateTaskbar();
+ mLauncher.getLaunchedAppState().assertTaskbarHidden();
+ }
+
+ @Test
+ @TaskbarModeSwitch
public void testLaunchApp() throws Exception {
getTaskbar().getAppIcon(TEST_APP_NAME).launch(TEST_APP_PACKAGE);
}
@Test
+ @TaskbarModeSwitch
public void testOpenMenu() throws Exception {
getTaskbar().getAppIcon(TEST_APP_NAME).openMenu();
}
@Test
+ @TaskbarModeSwitch
public void testLaunchShortcut() throws Exception {
getTaskbar().getAppIcon(TEST_APP_NAME)
.openDeepShortcutMenu()
@@ -88,6 +105,7 @@
@Test
@ScreenRecord // b/231615831
@PortraitLandscape
+ @TaskbarModeSwitch
public void testLaunchAppInSplitscreen() throws Exception {
getTaskbar().getAppIcon(TEST_APP_NAME).dragToSplitscreen(
TEST_APP_PACKAGE, CALCULATOR_APP_PACKAGE);
@@ -96,6 +114,7 @@
@Test
@ScreenRecord // b/231615831
@PortraitLandscape
+ @TaskbarModeSwitch
public void testLaunchShortcutInSplitscreen() throws Exception {
getTaskbar().getAppIcon(TEST_APP_NAME)
.openDeepShortcutMenu()
@@ -104,16 +123,19 @@
}
@Test
+ @TaskbarModeSwitch
public void testLaunchApp_FromTaskbarAllApps() throws Exception {
getTaskbar().openAllApps().getAppIcon(TEST_APP_NAME).launch(TEST_APP_PACKAGE);
}
@Test
+ @TaskbarModeSwitch
public void testOpenMenu_FromTaskbarAllApps() throws Exception {
getTaskbar().openAllApps().getAppIcon(TEST_APP_NAME).openMenu();
}
@Test
+ @TaskbarModeSwitch
public void testLaunchShortcut_FromTaskbarAllApps() throws Exception {
getTaskbar().openAllApps()
.getAppIcon(TEST_APP_NAME)
@@ -125,6 +147,7 @@
@Test
@ScreenRecord // b/231615831
@PortraitLandscape
+ @TaskbarModeSwitch
public void testLaunchAppInSplitscreen_FromTaskbarAllApps() throws Exception {
getTaskbar().openAllApps()
.getAppIcon(TEST_APP_NAME)
@@ -134,6 +157,7 @@
@Test
@ScreenRecord // b/231615831
@PortraitLandscape
+ @TaskbarModeSwitch
public void testLaunchShortcutInSplitscreen_FromTaskbarAllApps() throws Exception {
getTaskbar().openAllApps()
.getAppIcon(TEST_APP_NAME)
diff --git a/quickstep/tests/src/com/android/quickstep/TaskThumbnailViewTest.kt b/quickstep/tests/src/com/android/quickstep/TaskThumbnailViewTest.kt
deleted file mode 100644
index cf3c8c9..0000000
--- a/quickstep/tests/src/com/android/quickstep/TaskThumbnailViewTest.kt
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep
-
-import android.graphics.Rect
-import android.graphics.RectF
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.launcher3.DeviceProfileBaseTest
-import com.android.quickstep.views.TaskThumbnailView.PreviewPositionHelper
-import com.android.systemui.shared.recents.model.ThumbnailData
-import com.google.common.truth.Truth.assertThat
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mockito.mock
-
-/**
- * Test for TaskThumbnailView class.
- */
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class TaskThumbnailViewTest : DeviceProfileBaseTest() {
-
- private var mThumbnailData: ThumbnailData = mock(ThumbnailData::class.java)
-
- private val mPreviewPositionHelper = PreviewPositionHelper()
-
- @Test
- fun getInsetsToDrawInFullscreen_clipTaskbarSizeFromBottomForTablets() {
- initializeVarsForTablet()
- val dp = newDP()
- val previewRect = Rect(0, 0, 100, 100)
- val canvasWidth = dp.widthPx / 2
- val canvasHeight = dp.heightPx / 2
- val currentRotation = 0
- val isRtl = false
-
- mPreviewPositionHelper.updateThumbnailMatrix(previewRect, mThumbnailData, canvasWidth,
- canvasHeight, dp, currentRotation, isRtl)
-
- val expectedClippedInsets = RectF(0f, 0f, 0f, dp.taskbarSize / 2f)
- assertThat(mPreviewPositionHelper.getInsetsToDrawInFullscreen(dp))
- .isEqualTo(expectedClippedInsets)
- }
-
- @Test
- fun getInsetsToDrawInFullscreen_doNotClipTaskbarSizeFromBottomForPhones() {
- initializeVarsForPhone()
- val dp = newDP()
- val previewRect = Rect(0, 0, 100, 100)
- val canvasWidth = dp.widthPx / 2
- val canvasHeight = dp.heightPx / 2
- val currentRotation = 0
- val isRtl = false
-
- mPreviewPositionHelper.updateThumbnailMatrix(previewRect, mThumbnailData, canvasWidth,
- canvasHeight, dp, currentRotation, isRtl)
-
- val expectedClippedInsets = RectF(0f, 0f, 0f, 0f)
- assertThat(mPreviewPositionHelper.getInsetsToDrawInFullscreen(dp))
- .isEqualTo(expectedClippedInsets)
- }
-}
\ No newline at end of file
diff --git a/quickstep/tests/src/com/android/quickstep/TaskbarModeSwitchRule.java b/quickstep/tests/src/com/android/quickstep/TaskbarModeSwitchRule.java
new file mode 100644
index 0000000..9e41f74
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/TaskbarModeSwitchRule.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep;
+
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.quickstep.TaskbarModeSwitchRule.Mode.ALL;
+import static com.android.quickstep.TaskbarModeSwitchRule.Mode.PERSISTENT;
+import static com.android.quickstep.TaskbarModeSwitchRule.Mode.TRANSIENT;
+
+import android.content.Context;
+import android.util.Log;
+
+import com.android.launcher3.tapl.LauncherInstrumentation;
+import com.android.launcher3.tapl.TestHelpers;
+import com.android.launcher3.ui.AbstractLauncherUiTest;
+import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.rule.FailureWatcher;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Test rule that allows executing a test multiple times with different conditions
+ * ie. with transient taskbar enabled and disabled.
+ * The test should be annotated with @TaskbarModeSwitch.
+ */
+public class TaskbarModeSwitchRule implements TestRule {
+
+ static final String TAG = "TaskbarModeSwitchRule";
+
+ public static final int WAIT_TIME_MS = 10000;
+
+ public enum Mode {
+ TRANSIENT, PERSISTENT, ALL
+ }
+
+ // Annotation for tests that need to be run with quickstep enabled and disabled.
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.METHOD)
+ public @interface TaskbarModeSwitch {
+ Mode mode() default ALL;
+ }
+
+ private final LauncherInstrumentation mLauncher;
+
+ public TaskbarModeSwitchRule(LauncherInstrumentation launcher) {
+ mLauncher = launcher;
+ }
+
+ @Override
+ public Statement apply(Statement base, Description description) {
+ if (TestHelpers.isInLauncherProcess()
+ && description.getAnnotation(TaskbarModeSwitch.class) != null) {
+ Mode mode = description.getAnnotation(TaskbarModeSwitch.class).mode();
+ return new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ mLauncher.enableDebugTracing();
+ final boolean wasTransientTaskbarMode =
+ isTaskbarTransientMode(getInstrumentation().getTargetContext());
+ try {
+ if (mode == TRANSIENT || mode == ALL) {
+ evaluateWithTransientTaskbar();
+ }
+ if (mode == PERSISTENT || mode == ALL) {
+ evaluateWithPersistentTaskbar();
+ }
+ } catch (Throwable e) {
+ Log.e(TAG, "Error", e);
+ throw e;
+ } finally {
+ Log.d(TAG, "In Finally block");
+ setTaskbarMode(mLauncher, wasTransientTaskbarMode, description);
+ }
+ }
+
+ private void evaluateWithPersistentTaskbar() throws Throwable {
+ setTaskbarMode(mLauncher, false, description);
+ base.evaluate();
+ }
+
+ private void evaluateWithTransientTaskbar() throws Throwable {
+ setTaskbarMode(mLauncher, true, description);
+ base.evaluate();
+ }
+ };
+ } else {
+ return base;
+ }
+ }
+
+ private static boolean isTaskbarTransientMode(Context context) {
+ return DisplayController.isTransientTaskbar(context);
+ }
+
+ public static void setTaskbarMode(LauncherInstrumentation launcher,
+ boolean expectTransientTaskbar, Description description) throws Exception {
+ launcher.enableTransientTaskbar(expectTransientTaskbar);
+ launcher.recreateTaskbar();
+
+ Context context = getInstrumentation().getTargetContext();
+ assertTrue(launcher, "Couldn't set taskbar=" + expectTransientTaskbar,
+ isTaskbarTransientMode(context) == expectTransientTaskbar, description);
+
+ AbstractLauncherUiTest.checkDetectedLeaks(launcher);
+ }
+
+ private static void assertTrue(LauncherInstrumentation launcher, String message,
+ boolean condition, Description description) {
+ launcher.checkForAnomaly(true, true);
+ if (!condition) {
+ final AssertionError assertionError = new AssertionError(message);
+ if (description != null) {
+ FailureWatcher.onError(launcher, description, assertionError);
+ }
+ throw assertionError;
+ }
+ }
+}
diff --git a/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java b/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
index d43aafa..83602be 100644
--- a/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
+++ b/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
@@ -23,8 +23,8 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.ArrayMap;
+import android.view.RemoteAnimationTarget;
import android.view.Surface;
-import android.view.SurfaceControl;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -34,6 +34,7 @@
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.DisplayController.Info;
import com.android.launcher3.util.LauncherModelHelper;
+import com.android.launcher3.util.NavigationMode;
import com.android.launcher3.util.ReflectionHelpers;
import com.android.launcher3.util.RotationUtils;
import com.android.launcher3.util.WindowBounds;
@@ -41,8 +42,7 @@
import com.android.launcher3.util.window.WindowManagerProxy;
import com.android.quickstep.FallbackActivityInterface;
import com.android.quickstep.SystemUiProxy;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
+import com.android.quickstep.util.SurfaceTransaction.MockProperties;
import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;
@@ -164,6 +164,7 @@
WindowManagerProxy wmProxy = mock(WindowManagerProxy.class);
doReturn(cdi).when(wmProxy).getDisplayInfo(any());
doReturn(wm).when(wmProxy).getRealBounds(any(), any());
+ doReturn(NavigationMode.NO_BUTTON).when(wmProxy).getNavigationMode(any());
ArrayMap<CachedDisplayInfo, WindowBounds[]> perDisplayBoundsCache =
new ArrayMap<>();
@@ -205,17 +206,21 @@
}
@Override
- public SurfaceParams[] createSurfaceParams(BuilderProxy proxy) {
- SurfaceParams.Builder builder = new SurfaceParams.Builder((SurfaceControl) null);
- proxy.onBuildTargetParams(builder, mock(RemoteAnimationTargetCompat.class), this);
- return new SurfaceParams[] {builder.build()};
+ public SurfaceTransaction createSurfaceParams(BuilderProxy proxy) {
+ RecordingSurfaceTransaction transaction = new RecordingSurfaceTransaction();
+ proxy.onBuildTargetParams(
+ transaction.mockProperties, mock(RemoteAnimationTarget.class), this);
+ return transaction;
}
@Override
- public void applySurfaceParams(SurfaceParams[] params) {
+ public void applySurfaceParams(SurfaceTransaction params) {
+ Assert.assertTrue(params instanceof RecordingSurfaceTransaction);
+ MockProperties p = ((RecordingSurfaceTransaction) params).mockProperties;
+
// Verify that the task position remains the same
RectF newAppBounds = new RectF(mAppBounds);
- params[0].matrix.mapRect(newAppBounds);
+ p.matrix.mapRect(newAppBounds);
Assert.assertThat(newAppBounds, new AlmostSame(mAppBounds));
System.err.println("Bounds mapped: " + mAppBounds + " => " + newAppBounds);
diff --git a/res/color-night-v31/transient_taskbar_background.xml b/res/color-night-v31/transient_taskbar_background.xml
new file mode 100644
index 0000000..40f6494
--- /dev/null
+++ b/res/color-night-v31/transient_taskbar_background.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@android:color/system_neutral1_500" android:lStar="15" />
+</selector>
+
diff --git a/res/color-v31/transient_taskbar_background.xml b/res/color-v31/transient_taskbar_background.xml
new file mode 100644
index 0000000..bce947d
--- /dev/null
+++ b/res/color-v31/transient_taskbar_background.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@android:color/system_neutral1_500" android:lStar="95" />
+</selector>
+
diff --git a/res/drawable/ic_all_apps_button.xml b/res/drawable/ic_all_apps_button.xml
index 5770d3c..7de390a 100644
--- a/res/drawable/ic_all_apps_button.xml
+++ b/res/drawable/ic_all_apps_button.xml
@@ -18,27 +18,29 @@
android:width="80dp"
android:height="80dp"
android:viewportWidth="80"
- android:viewportHeight="80"
- android:theme="@style/AllAppsTheme">
- <path
- android:pathData="M40,0.5L40,0.5c21.8,0 39.5,17.7 39.5,39.5l0,0c0,21.8 -17.7,39.5 -39.5,39.5l0,0C18.2,79.5 0.5,61.8 0.5,40l0,0C0.5,18.2 18.2,0.5 40,0.5z"
- android:fillColor="?attr/allAppsButtonBgColor"/>
- <path
- android:pathData="M26.8,32.1m-5.3,0a5.3,5.3 0,1 1,10.6 0a5.3,5.3 0,1 1,-10.6 0"
- android:fillColor="?attr/allAppsButtonColor1"/>
- <path
- android:pathData="M26.8,47.9m-5.3,0a5.3,5.3 0,1 1,10.6 0a5.3,5.3 0,1 1,-10.6 0"
- android:fillColor="?attr/allAppsButtonColor2"/>
- <path
- android:pathData="M40,32.1m-5.3,0a5.3,5.3 0,1 1,10.6 0a5.3,5.3 0,1 1,-10.6 0"
- android:fillColor="?attr/allAppsButtonColor3"/>
- <path
- android:pathData="M40,47.9m-5.3,0a5.3,5.3 0,1 1,10.6 0a5.3,5.3 0,1 1,-10.6 0"
- android:fillColor="?attr/allAppsButtonColor2"/>
- <path
- android:pathData="M53.2,32.1m-5.3,0a5.3,5.3 0,1 1,10.6 0a5.3,5.3 0,1 1,-10.6 0"
- android:fillColor="?attr/allAppsButtonColor4"/>
- <path
- android:pathData="M53.2,47.9m-5.3,0a5.3,5.3 0,1 1,10.6 0a5.3,5.3 0,1 1,-10.6 0"
- android:fillColor="?attr/allAppsButtonColor2"/>
+ android:viewportHeight="80">
+ <group
+ android:pivotY="40"
+ android:pivotX="40"
+ android:scaleX=".88"
+ android:scaleY=".88">
+ <path
+ android:pathData="M26.8,32.1m-5.3,0a5.3,5.3 0,1 1,10.6 0a5.3,5.3 0,1 1,-10.6 0"
+ android:fillColor="@color/all_apps_button_color_1"/>
+ <path
+ android:pathData="M26.8,47.9m-5.3,0a5.3,5.3 0,1 1,10.6 0a5.3,5.3 0,1 1,-10.6 0"
+ android:fillColor="@color/all_apps_button_color_2"/>
+ <path
+ android:pathData="M40,32.1m-5.3,0a5.3,5.3 0,1 1,10.6 0a5.3,5.3 0,1 1,-10.6 0"
+ android:fillColor="@color/all_apps_button_color_3"/>
+ <path
+ android:pathData="M40,47.9m-5.3,0a5.3,5.3 0,1 1,10.6 0a5.3,5.3 0,1 1,-10.6 0"
+ android:fillColor="@color/all_apps_button_color_2"/>
+ <path
+ android:pathData="M53.2,32.1m-5.3,0a5.3,5.3 0,1 1,10.6 0a5.3,5.3 0,1 1,-10.6 0"
+ android:fillColor="@color/all_apps_button_color_4"/>
+ <path
+ android:pathData="M53.2,47.9m-5.3,0a5.3,5.3 0,1 1,10.6 0a5.3,5.3 0,1 1,-10.6 0"
+ android:fillColor="@color/all_apps_button_color_2"/>
+ </group>
</vector>
diff --git a/res/drawable/page_indicator.xml b/res/drawable/page_indicator.xml
new file mode 100644
index 0000000..c0ccc49
--- /dev/null
+++ b/res/drawable/page_indicator.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="?attr/folderPaginationColor"/>
+ <size android:width="@dimen/page_indicator_size" android:height="@dimen/page_indicator_size"/>
+</shape>
\ No newline at end of file
diff --git a/res/layout/all_apps_personal_work_tabs.xml b/res/layout/all_apps_personal_work_tabs.xml
index 11143fb..4459c87 100644
--- a/res/layout/all_apps_personal_work_tabs.xml
+++ b/res/layout/all_apps_personal_work_tabs.xml
@@ -20,6 +20,9 @@
android:layout_width="match_parent"
android:layout_height="@dimen/all_apps_header_pill_height"
android:layout_gravity="center_horizontal"
+ android:paddingTop="@dimen/all_apps_tabs_vertical_padding"
+ android:paddingBottom="@dimen/all_apps_tabs_vertical_padding"
+ android:layout_marginTop="@dimen/all_apps_tabs_margin_top"
android:orientation="horizontal"
style="@style/TextHeadline">
@@ -28,7 +31,6 @@
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginEnd="@dimen/all_apps_tabs_button_horizontal_padding"
- android:layout_marginVertical="@dimen/all_apps_tabs_vertical_padding"
android:layout_weight="1"
android:background="@drawable/all_apps_tabs_background"
android:text="@string/all_apps_personal_tab"
@@ -41,7 +43,6 @@
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginStart="@dimen/all_apps_tabs_button_horizontal_padding"
- android:layout_marginVertical="@dimen/all_apps_tabs_vertical_padding"
android:layout_weight="1"
android:background="@drawable/all_apps_tabs_background"
android:text="@string/all_apps_work_tab"
diff --git a/res/layout/hotseat.xml b/res/layout/hotseat.xml
index 82b0b8d..95ebd94 100644
--- a/res/layout/hotseat.xml
+++ b/res/layout/hotseat.xml
@@ -21,4 +21,5 @@
android:layout_height="match_parent"
android:theme="@style/HomeScreenElementTheme"
android:importantForAccessibility="no"
+ android:preferKeepClear="true"
launcher:containerType="hotseat" />
\ No newline at end of file
diff --git a/res/layout/page_indicator_dots.xml b/res/layout/page_indicator_dots.xml
new file mode 100644
index 0000000..d5fe51e
--- /dev/null
+++ b/res/layout/page_indicator_dots.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<com.android.launcher3.pageindicators.PageIndicatorDots xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/page_indicator"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/workspace_page_indicator_height"
+ android:layout_gravity="bottom | center_horizontal"
+ android:theme="@style/HomeScreenElementTheme" />
\ No newline at end of file
diff --git a/res/layout/search_container_all_apps.xml b/res/layout/search_container_all_apps.xml
index e1646ba..b46298c 100644
--- a/res/layout/search_container_all_apps.xml
+++ b/res/layout/search_container_all_apps.xml
@@ -26,6 +26,7 @@
android:gravity="center"
android:hint="@string/all_apps_search_bar_hint"
android:imeOptions="actionSearch|flagNoExtractUi"
+ android:importantForAutofill="no"
android:inputType="text|textNoSuggestions|textCapWords"
android:maxLines="1"
android:padding="8dp"
diff --git a/res/layout/search_results_rv_layout.xml b/res/layout/search_results_rv_layout.xml
index 567cb5f..9127521 100644
--- a/res/layout/search_results_rv_layout.xml
+++ b/res/layout/search_results_rv_layout.xml
@@ -18,7 +18,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/search_results_list_view"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="match_parent"
android:clipToPadding="false"
android:descendantFocusability="afterDescendants"
android:focusable="true" />
diff --git a/res/layout/secondary_launcher.xml b/res/layout/secondary_launcher.xml
index 635db14..4be2e45 100644
--- a/res/layout/secondary_launcher.xml
+++ b/res/layout/secondary_launcher.xml
@@ -18,6 +18,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/drag_layer"
+ android:clipChildren="false"
android:padding="@dimen/dynamic_grid_edge_margin">
<GridView
@@ -52,7 +53,6 @@
android:saveEnabled="false"
android:layout_gravity="bottom|end"
android:background="@drawable/round_rect_primary"
- android:elevation="2dp"
android:visibility="invisible" >
<include
@@ -76,35 +76,8 @@
android:paddingTop="@dimen/all_apps_header_top_padding"
android:orientation="vertical" >
- <com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip
- android:id="@+id/tabs"
- android:layout_width="match_parent"
- android:layout_height="@dimen/all_apps_header_pill_height"
- android:orientation="horizontal"
- style="@style/TextHeadline">
-
- <Button
- android:id="@+id/tab_personal"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:background="?android:attr/selectableItemBackground"
- android:text="@string/all_apps_personal_tab"
- android:textAllCaps="true"
- android:textColor="@color/all_apps_tab_text"
- android:textSize="14sp" />
-
- <Button
- android:id="@+id/tab_work"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:background="?android:attr/selectableItemBackground"
- android:text="@string/all_apps_work_tab"
- android:textAllCaps="true"
- android:textColor="@color/all_apps_tab_text"
- android:textSize="14sp" />
- </com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip>
+ <include layout="@layout/floating_header_content" />
+ <include layout="@layout/all_apps_personal_work_tabs" />
</com.android.launcher3.allapps.FloatingHeaderView>
<com.android.launcher3.allapps.search.AppsSearchContainerLayout
@@ -113,12 +86,14 @@
android:layout_height="@dimen/all_apps_search_bar_field_height"
android:layout_centerHorizontal="true"
android:layout_gravity="top|center_horizontal"
+ android:layout_marginTop="24dp"
android:background="@drawable/bg_all_apps_searchbox"
android:elevation="1dp"
android:focusableInTouchMode="true"
android:gravity="center"
android:hint="@string/all_apps_search_bar_hint"
android:imeOptions="actionSearch|flagNoExtractUi"
+ android:importantForAutofill="no"
android:inputType="text|textNoSuggestions|textCapWords"
android:maxLines="1"
android:padding="8dp"
diff --git a/res/layout/user_folder_icon_normalized.xml b/res/layout/user_folder_icon_normalized.xml
index 11eea60..5518dc8 100644
--- a/res/layout/user_folder_icon_normalized.xml
+++ b/res/layout/user_folder_icon_normalized.xml
@@ -30,11 +30,11 @@
<LinearLayout
android:id="@+id/folder_footer"
android:layout_width="match_parent"
- android:layout_height="48dp"
+ android:layout_height="@dimen/folder_footer_height_default"
android:clipChildren="false"
android:orientation="horizontal"
- android:paddingLeft="12dp"
- android:paddingRight="12dp" >
+ android:paddingLeft="@dimen/folder_footer_horiz_padding"
+ android:paddingRight="@dimen/folder_footer_horiz_padding">
<com.android.launcher3.folder.FolderNameEditText
android:id="@+id/folder_name"
@@ -47,6 +47,7 @@
android:gravity="center_horizontal"
android:hint="@string/folder_hint_text"
android:imeOptions="flagNoExtractUi"
+ android:importantForAutofill="no"
android:singleLine="true"
android:textColor="?attr/folderTextColor"
android:textColorHighlight="?android:attr/colorControlHighlight"
diff --git a/res/layout/widgets_full_sheet_recyclerview.xml b/res/layout/widgets_full_sheet_recyclerview.xml
index 9da3e87..b2a3a0d 100644
--- a/res/layout/widgets_full_sheet_recyclerview.xml
+++ b/res/layout/widgets_full_sheet_recyclerview.xml
@@ -30,6 +30,7 @@
android:layout_height="wrap_content"
android:layout_below="@id/collapse_handle"
android:paddingBottom="16dp"
+ android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
android:orientation="vertical">
<TextView
@@ -40,7 +41,6 @@
android:textSize="24sp"
android:layout_marginTop="24dp"
android:textColor="?android:attr/textColorSecondary"
- android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
android:text="@string/widget_button_text"/>
<FrameLayout
@@ -49,7 +49,6 @@
android:layout_height="wrap_content"
android:elevation="0.1dp"
android:background="?android:attr/colorBackground"
- android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
android:paddingBottom="8dp"
android:clipToPadding="false"
launcher:layout_sticky="true" >
@@ -63,7 +62,6 @@
android:layout_marginTop="8dp"
android:background="@drawable/widgets_recommendation_background"
android:paddingVertical="@dimen/recommended_widgets_table_vertical_padding"
- android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
android:visibility="gone" />
</com.android.launcher3.views.StickyHeaderLayout>
diff --git a/res/layout/widgets_search_bar.xml b/res/layout/widgets_search_bar.xml
index 9178a75..6d44865 100644
--- a/res/layout/widgets_search_bar.xml
+++ b/res/layout/widgets_search_bar.xml
@@ -23,6 +23,7 @@
android:layout_weight="1"
android:inputType="text"
android:imeOptions="actionSearch"
+ android:importantForAutofill="no"
android:textColor="?android:attr/textColorPrimary"
android:textColorHint="?android:attr/textColorSecondary"/>
diff --git a/res/layout/work_mode_fab.xml b/res/layout/work_mode_fab.xml
index d2fa5fa..81b28ba 100644
--- a/res/layout/work_mode_fab.xml
+++ b/res/layout/work_mode_fab.xml
@@ -26,6 +26,7 @@
android:textColor="@color/all_apps_tab_text"
android:textSize="14sp"
android:background="@drawable/work_apps_toggle_background"
+ android:forceHasOverlappingRendering="false"
android:drawablePadding="8dp"
android:drawableStart="@drawable/ic_corp_off"
android:layout_marginBottom="@dimen/work_fab_margin_bottom"
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index a699667..500fce2 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -74,7 +74,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Deïnstalleer"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Programinligting"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Installeer"</string>
- <string name="dismiss_prediction_label" msgid="3357562989568808658">"Moenie program voorstel nie"</string>
+ <string name="dismiss_prediction_label" msgid="3357562989568808658">"Moenie voorstel nie"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Vasspeldvoorspelling"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"installeer kortpaaie"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Laat \'n program toe om kortpaaie by te voeg sonder gebruikerinmenging."</string>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index e075559..c234900 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -101,7 +101,7 @@
<string name="folder_name_format_exact" msgid="8626242716117004803">"المجلد: <xliff:g id="NAME">%1$s</xliff:g>، <xliff:g id="SIZE">%2$d</xliff:g> عنصر"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"المجلد: <xliff:g id="NAME">%1$s</xliff:g>، <xliff:g id="SIZE">%2$d</xliff:g> عنصر أو أكثر"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"الخلفيات"</string>
- <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"الخلفية والنمط"</string>
+ <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"الخلفية والأسلوب"</string>
<string name="settings_button_text" msgid="8873672322605444408">"إعدادات الشاشة الرئيسية"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"أوقف المشرف هذه الميزة"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"السماح بتدوير الشاشة الرئيسية"</string>
diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml
index ba14e97..67998d5 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -21,9 +21,9 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
<string name="work_folder_name" msgid="3753320833950115786">"কৰ্মস্থান"</string>
- <string name="activity_not_found" msgid="8071924732094499514">"এপটো ইনষ্টল কৰা নহ\'ল।"</string>
- <string name="activity_not_available" msgid="7456344436509528827">"এপটো নাই"</string>
- <string name="safemode_shortcut_error" msgid="9160126848219158407">"ডাউনল’ড কৰা এপটোক সুৰক্ষিত ম\'ডত অক্ষম কৰা হ’ল"</string>
+ <string name="activity_not_found" msgid="8071924732094499514">"এপ্টো ইনষ্টল কৰা নহ\'ল।"</string>
+ <string name="activity_not_available" msgid="7456344436509528827">"এপ্টো নাই"</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"ডাউনল’ড কৰা এপ্টোক সুৰক্ষিত ম\'ডত অক্ষম কৰা হ’ল"</string>
<string name="safemode_widget_error" msgid="4863470563535682004">"ৱিজেটবোৰক সুৰক্ষিত ম\'ডত অক্ষম কৰা হ’ল"</string>
<string name="shortcut_not_available" msgid="2536503539825726397">"শ্বৰ্টকাট নাই"</string>
<string name="home_screen" msgid="5629429142036709174">"গৃহ স্ক্ৰীন"</string>
@@ -74,7 +74,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"আনইনষ্টল কৰক"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"এপ্ সম্পৰ্কীয় তথ্য"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"ইনষ্টল কৰক"</string>
- <string name="dismiss_prediction_label" msgid="3357562989568808658">"এপৰ পৰামৰ্শ নিদিব"</string>
+ <string name="dismiss_prediction_label" msgid="3357562989568808658">"পৰামৰ্শ নিদিব"</string>
<string name="pin_prediction" msgid="4196423321649756498">"পূৰ্বানুমান কৰা এপ্টো পিন কৰক"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"শ্বৰ্টকাট ইনষ্টল কৰিব পাৰে"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"ব্য়ৱহাৰকাৰীৰ হস্তক্ষেপ অবিহনেই কোনো এপক শ্বৰ্টকাটবোৰ যোগ কৰাৰ অনুমতি দিয়ে।"</string>
@@ -109,7 +109,7 @@
<string name="notification_dots_title" msgid="9062440428204120317">"জাননী বিন্দু"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"অন আছে"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"অফ আছে"</string>
- <string name="title_missing_notification_access" msgid="7503287056163941064">"জাননী চাবলৈ অনুমতিৰ প্ৰয়োজন"</string>
+ <string name="title_missing_notification_access" msgid="7503287056163941064">"জাননীৰ এক্সেছৰ প্ৰয়োজন"</string>
<string name="msg_missing_notification_access" msgid="281113995110910548">"জাননী সম্পৰ্কীয় বিন্দুবোৰ দেখুৱাবলৈ <xliff:g id="NAME">%1$s</xliff:g>ৰ বাবে এপৰ জাননীসমূহ অন কৰক"</string>
<string name="title_change_settings" msgid="1376365968844349552">"ছেটিং সলনি কৰক"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"জাননী বিন্দু দেখুৱাওক"</string>
@@ -119,8 +119,8 @@
<string name="package_state_unknown" msgid="7592128424511031410">"অজ্ঞাত"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"আঁতৰাওক"</string>
<string name="abandoned_search" msgid="891119232568284442">"সন্ধান কৰক"</string>
- <string name="abandoned_promises_title" msgid="7096178467971716750">"এই এপটো ইনষ্টল কৰা হোৱা নাই"</string>
- <string name="abandoned_promise_explanation" msgid="3990027586878167529">"এই আইকনৰ এপটো ইনষ্টল কৰা হোৱা নাই। আপুনি এইটো আঁতৰাব পাৰে অথবা এপটো বিচাৰি মেনুৱেলভাৱে ইনষ্টল কৰিব পাৰে।"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"এই এপ্টো ইনষ্টল কৰা হোৱা নাই"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"এই আইকনৰ এপ্টো ইনষ্টল কৰা হোৱা নাই। আপুনি এইটো আঁতৰাব পাৰে অথবা এপ্টো বিচাৰি মেনুৱেলভাৱে ইনষ্টল কৰিব পাৰে।"</string>
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> ইনষ্টল কৰি থকা হৈছে, <xliff:g id="PROGRESS">%2$s</xliff:g> সম্পূৰ্ণ হৈছে"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ডাউনল’ড কৰি থকা হৈছে, <xliff:g id="PROGRESS">%2$s</xliff:g> সম্পূৰ্ণ হ’ল"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ইনষ্টল হোৱালৈ অপেক্ষা কৰি থকা হৈছে"</string>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index e4d46e4..600d66f 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -74,7 +74,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Odinstalovat"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"O aplikaci"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Nainstalovat"</string>
- <string name="dismiss_prediction_label" msgid="3357562989568808658">"Aplikaci nenavrhovat"</string>
+ <string name="dismiss_prediction_label" msgid="3357562989568808658">"Nenavrhovat aplikaci"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Připnout předpověď"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instalace zástupce"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Umožňuje aplikaci přidat zástupce bez zásahu uživatele."</string>
diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml
index 4f15eb5..aa29d8d 100644
--- a/res/values-en-rCA/strings.xml
+++ b/res/values-en-rCA/strings.xml
@@ -32,12 +32,12 @@
<string name="split_screen_position_left" msgid="7537793098851830883">"Split left"</string>
<string name="split_screen_position_right" msgid="1569377524925193369">"Split right"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"App info for %1$s"</string>
- <string name="long_press_widget_to_add" msgid="3587712543577675817">"Touch and hold to move a widget."</string>
+ <string name="long_press_widget_to_add" msgid="3587712543577675817">"Touch & hold to move a widget."</string>
<string name="long_accessible_way_to_add" msgid="2733588281439571974">"Double-tap & hold to move a widget or use custom actions."</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d wide by %2$d high"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget"</string>
- <string name="add_item_request_drag_hint" msgid="8730547755622776606">"Touch and hold the widget to move it around the home screen"</string>
+ <string name="add_item_request_drag_hint" msgid="8730547755622776606">"Touch & hold the widget to move it around the home screen"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Add to home screen"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget added to home screen"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widgets}}"</string>
@@ -54,18 +54,18 @@
<string name="widget_education_header" msgid="4874760613775913787">"Useful info at your fingertips"</string>
<string name="widget_education_content" msgid="1731667670753497052">"To get info without opening apps, you can add widgets to your home screen"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Tap to change widget settings"</string>
- <string name="widget_education_close_button" msgid="8676165703104836580">"OK"</string>
+ <string name="widget_education_close_button" msgid="8676165703104836580">"Got it"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Change widget settings"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Search apps"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"Loading apps…"</string>
- <string name="all_apps_no_search_results" msgid="3200346862396363786">"No apps found matching \'<xliff:g id="QUERY">%1$s</xliff:g>\'"</string>
+ <string name="all_apps_no_search_results" msgid="3200346862396363786">"No apps found matching \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="label_application" msgid="8531721983832654978">"App"</string>
<string name="all_apps_label" msgid="5015784846527570951">"All apps"</string>
<string name="notifications_header" msgid="1404149926117359025">"Notifications"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Touch & hold to move a shortcut."</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Double-tap & hold to move a shortcut or use custom actions."</string>
<string name="out_of_space" msgid="6455557115204099579">"No room on this home screen"</string>
- <string name="hotseat_out_of_space" msgid="7448809638125333693">"No more room in the Favourites tray"</string>
+ <string name="hotseat_out_of_space" msgid="7448809638125333693">"No more room in the Favorites tray"</string>
<string name="all_apps_button_label" msgid="8130441508702294465">"Apps list"</string>
<string name="all_apps_search_results" msgid="5889367432531296759">"Search results"</string>
<string name="all_apps_button_personal_label" msgid="1315764287305224468">"Personal apps list"</string>
@@ -75,13 +75,13 @@
<string name="app_info_drop_target_label" msgid="692894985365717661">"App info"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Install"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Don\'t suggest app"</string>
- <string name="pin_prediction" msgid="4196423321649756498">"Pin prediction"</string>
+ <string name="pin_prediction" msgid="4196423321649756498">"Pin Prediction"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"install shortcuts"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Allows an app to add shortcuts without user intervention."</string>
- <string name="permlab_read_settings" msgid="5136500343007704955">"read Home settings and shortcuts"</string>
- <string name="permdesc_read_settings" msgid="4208061150510996676">"Allows the app to read the settings and shortcuts in Home."</string>
- <string name="permlab_write_settings" msgid="4820028712156303762">"write Home settings and shortcuts"</string>
- <string name="permdesc_write_settings" msgid="726859348127868466">"Allows the app to change the settings and shortcuts in Home."</string>
+ <string name="permlab_read_settings" msgid="5136500343007704955">"read home settings and shortcuts"</string>
+ <string name="permdesc_read_settings" msgid="4208061150510996676">"Allows the app to read the settings and shortcuts in home."</string>
+ <string name="permlab_write_settings" msgid="4820028712156303762">"write home settings and shortcuts"</string>
+ <string name="permdesc_write_settings" msgid="726859348127868466">"Allows the app to change the settings and shortcuts in home."</string>
<string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> is not allowed to make phone calls"</string>
<string name="gadget_error_text" msgid="740356548025791839">"Can\'t load widget"</string>
<string name="gadget_setup_text" msgid="8348374825537681407">"Widget settings"</string>
@@ -101,7 +101,7 @@
<string name="folder_name_format_exact" msgid="8626242716117004803">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> items"</string>
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> or more items"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Wallpapers"</string>
- <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Wallpaper and style"</string>
+ <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Wallpaper & style"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Home settings"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Disabled by your admin"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Allow home screen rotation"</string>
@@ -113,7 +113,7 @@
<string name="msg_missing_notification_access" msgid="281113995110910548">"To show Notification Dots, turn on app notifications for <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="title_change_settings" msgid="1376365968844349552">"Change settings"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"Show notification dots"</string>
- <string name="developer_options_title" msgid="700788437593726194">"Developer options"</string>
+ <string name="developer_options_title" msgid="700788437593726194">"Developer Options"</string>
<string name="auto_add_shortcuts_label" msgid="4926805029653694105">"Add app icons to home screen"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"For new apps"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"Unknown"</string>
@@ -125,7 +125,7 @@
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> downloading, <xliff:g id="PROGRESS">%2$s</xliff:g> complete"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> waiting to install"</string>
<string name="dialog_update_title" msgid="114234265740994042">"App update required"</string>
- <string name="dialog_update_message" msgid="4176784553982226114">"The app for this icon isn\'t updated. You can update manually to re-enable this shortcut or remove the icon."</string>
+ <string name="dialog_update_message" msgid="4176784553982226114">"The app for this icon isn\'t updated. You can update manually to re-enable this shortcut, or remove the icon."</string>
<string name="dialog_update" msgid="2178028071796141234">"Update"</string>
<string name="dialog_remove" msgid="6510806469849709407">"Remove"</string>
<string name="widgets_list" msgid="796804551140113767">"Widgets list"</string>
@@ -138,7 +138,7 @@
<string name="action_move" msgid="4339390619886385032">"Move item"</string>
<string name="move_to_empty_cell_description" msgid="5254852678218206889">"Move to row <xliff:g id="NUMBER_0">%1$s</xliff:g> column <xliff:g id="NUMBER_1">%2$s</xliff:g> in <xliff:g id="STRING">%3$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"Move to position <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="move_to_hotseat_position" msgid="6295412897075147808">"Move to favourites position <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
+ <string name="move_to_hotseat_position" msgid="6295412897075147808">"Move to favorites position <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="item_moved" msgid="4606538322571412879">"Item moved"</string>
<string name="add_to_folder" msgid="9040534766770853243">"Add to folder: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="add_to_folder_with_app" msgid="4534929978967147231">"Add to folder with <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -146,13 +146,13 @@
<string name="create_folder_with" msgid="4050141361160214248">"Create folder with: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"Folder created"</string>
<string name="action_move_to_workspace" msgid="39528912300293768">"Move to home screen"</string>
- <string name="action_resize" msgid="1802976324781771067">"Re-size"</string>
+ <string name="action_resize" msgid="1802976324781771067">"Resize"</string>
<string name="action_increase_width" msgid="8773715375078513326">"Increase width"</string>
<string name="action_increase_height" msgid="459390020612501122">"Increase height"</string>
<string name="action_decrease_width" msgid="1374549771083094654">"Decrease width"</string>
<string name="action_decrease_height" msgid="282377193880900022">"Decrease height"</string>
- <string name="widget_resized" msgid="9130327887929620">"Widget re-sized to width <xliff:g id="NUMBER_0">%1$s</xliff:g> height <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
- <string name="action_deep_shortcut" msgid="2864038805849372848">"Short cuts"</string>
+ <string name="widget_resized" msgid="9130327887929620">"Widget resized to width <xliff:g id="NUMBER_0">%1$s</xliff:g> height <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="action_deep_shortcut" msgid="2864038805849372848">"Shortcuts"</string>
<string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"Shortcuts and notifications"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Dismiss"</string>
<string name="accessibility_close" msgid="2277148124685870734">"Close"</string>
@@ -161,12 +161,12 @@
<string name="all_apps_work_tab" msgid="4884822796154055118">"Work"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Work profile"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Work apps are badged and visible to your IT admin"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
+ <string name="work_profile_edu_accept" msgid="6069788082535149071">"Got it"</string>
<string name="work_apps_paused_title" msgid="3040901117349444598">"Work apps are paused"</string>
- <string name="work_apps_paused_body" msgid="261634750995824906">"Your work apps can’t send you notifications, use your battery or access your location"</string>
- <string name="work_apps_paused_content_description" msgid="5149623040804051095">"Work apps are off. Your work apps can’t send you notifications, use your battery or access your location"</string>
+ <string name="work_apps_paused_body" msgid="261634750995824906">"Your work apps can’t send you notifications, use your battery, or access your location"</string>
+ <string name="work_apps_paused_content_description" msgid="5149623040804051095">"Work apps are off. Your work apps can’t send you notifications, use your battery, or access your location"</string>
<string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Work apps are badged and visible to your IT admin"</string>
- <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"OK"</string>
+ <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Got it"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Pause work apps"</string>
<string name="work_apps_enable_btn_text" msgid="1156432622148413741">"Turn on work apps"</string>
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filter"</string>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index 9e2d9a4..8b944fe 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -124,7 +124,7 @@
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> instalatzen, <xliff:g id="PROGRESS">%2$s</xliff:g> osatuta"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> deskargatzen, <xliff:g id="PROGRESS">%2$s</xliff:g> osatuta"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> instalatzeko zain"</string>
- <string name="dialog_update_title" msgid="114234265740994042">"Aplikazioa eguneratu behar da"</string>
+ <string name="dialog_update_title" msgid="114234265740994042">"Aplikazioa eguneratu egin behar da"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"Ikonoaren aplikazioa ez dago eguneratuta. Lasterbidea berriro gaitzeko, eskuz egunera dezakezu aplikazioa. Bestela, kendu ikonoa."</string>
<string name="dialog_update" msgid="2178028071796141234">"Eguneratu"</string>
<string name="dialog_remove" msgid="6510806469849709407">"Kendu"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index a6856fc..b371067 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -157,8 +157,8 @@
<string name="action_dismiss_notification" msgid="5909461085055959187">"Ignorer"</string>
<string name="accessibility_close" msgid="2277148124685870734">"Fermer"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Notification ignorée"</string>
- <string name="all_apps_personal_tab" msgid="4190252696685155002">"Personnelles"</string>
- <string name="all_apps_work_tab" msgid="4884822796154055118">"Professionnelles"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Personnel"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Professionnel"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Profil professionnel"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Les applis professionnelles sont identifiées par un badge et votre administrateur informatique peut les voir"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 5e71f33..e90e63a 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -158,16 +158,16 @@
<string name="accessibility_close" msgid="2277148124685870734">"बंद करें"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"सूचना को खारिज किया गया"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"निजी ऐप"</string>
- <string name="all_apps_work_tab" msgid="4884822796154055118">"काम से जुड़े ऐप"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"वर्क ऐप्लिकेशन"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"वर्क प्रोफ़ाइल"</string>
- <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"ऑफ़िस के काम से जुड़े ऐप्लिकेशन बैज किए गए हैं और आईटी एडमिन को दिख रहे हैं"</string>
+ <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"वर्क ऐप्लिकेशन बैज किए गए हैं और आईटी एडमिन को दिख रहे हैं"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"ठीक है"</string>
- <string name="work_apps_paused_title" msgid="3040901117349444598">"ऑफ़िस के काम से जुड़े ऐप्लिकेशन रोके गए"</string>
- <string name="work_apps_paused_body" msgid="261634750995824906">"ऑफ़िस के काम से जुड़े आपके ऐप्लिकेशन, आपको सूचनाएं नहीं भेज सकते. साथ ही, आपकी बैटरी का इस्तेमाल या आपकी जगह की जानकारी को ऐक्सेस भी नहीं कर सकते"</string>
+ <string name="work_apps_paused_title" msgid="3040901117349444598">"वर्क ऐप्लिकेशन रोके गए"</string>
+ <string name="work_apps_paused_body" msgid="261634750995824906">"आपके वर्क ऐप्लिकेशन, आपको सूचनाएं नहीं भेज सकते. साथ ही, आपकी बैटरी का इस्तेमाल या आपकी जगह की जानकारी को ऐक्सेस भी नहीं कर सकते"</string>
<string name="work_apps_paused_content_description" msgid="5149623040804051095">"ऑफ़िस के काम से जुड़े ऐप्लिकेशन बंद हैं. ये ऐप्लिकेशन, आपको सूचनाएं नहीं भेज सकते. साथ ही, आपकी बैटरी का इस्तेमाल या आपकी जगह की जानकारी को ऐक्सेस भी नहीं कर सकते"</string>
<string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"ऑफ़िस के काम से जुड़े ऐप्लिकेशन बैज किए गए हैं और आईटी एडमिन को दिख रहे हैं"</string>
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"ठीक है"</string>
- <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"ऑफ़िस के काम से जुड़े ऐप्लिकेशन रोकें"</string>
+ <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"वर्क ऐप्लिकेशन रोकें"</string>
<string name="work_apps_enable_btn_text" msgid="1156432622148413741">"ऑफ़िस के काम से जुड़े ऐप्लिकेशन चालू करें"</string>
<string name="developer_options_filter_hint" msgid="5896817443635989056">"फ़िल्टर"</string>
<string name="search_pref_screen_title" msgid="3258959643336315962">"अपने फ़ोन में खोजें"</string>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index 44c3a96..b8f1643 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -74,7 +74,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Ապատեղադրել"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Հավելվածի մասին"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Տեղադրել"</string>
- <string name="dismiss_prediction_label" msgid="3357562989568808658">"Թաքցնել առաջարկը"</string>
+ <string name="dismiss_prediction_label" msgid="3357562989568808658">"Չառաջարկել"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Ամրացնել առաջարկվող հավելվածը"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"Դյուրանցումների տեղադրում"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Հավելվածին թույլ է տալիս ավելացնել դյուրանցումներ՝ առանց օգտագործողի միջամտության:"</string>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 484be1f..1a442a8 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -74,7 +74,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"アンインストール"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"アプリ情報"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"インストール"</string>
- <string name="dismiss_prediction_label" msgid="3357562989568808658">"アプリの候補を表示しない"</string>
+ <string name="dismiss_prediction_label" msgid="3357562989568808658">"アプリを表示しない"</string>
<string name="pin_prediction" msgid="4196423321649756498">"アプリの候補を固定"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"ショートカットのインストール"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"ユーザー操作なしでショートカットを追加することをアプリに許可します。"</string>
@@ -170,7 +170,7 @@
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"仕事用アプリを一時停止"</string>
<string name="work_apps_enable_btn_text" msgid="1156432622148413741">"仕事用アプリを ON にする"</string>
<string name="developer_options_filter_hint" msgid="5896817443635989056">"フィルタ"</string>
- <string name="search_pref_screen_title" msgid="3258959643336315962">"スマートフォンの検索"</string>
+ <string name="search_pref_screen_title" msgid="3258959643336315962">"スマートフォンを検索"</string>
<string name="search_pref_screen_title_tablet" msgid="5220319680451343959">"タブレットを探す"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"失敗: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index 84b7993..3a58b85 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -74,7 +74,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Чыгарып салуу"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Колдонмо тууралуу"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Орнотуу"</string>
- <string name="dismiss_prediction_label" msgid="3357562989568808658">"Колдонмо сунушталбасын"</string>
+ <string name="dismiss_prediction_label" msgid="3357562989568808658">"Cунушталбасын"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Божомолдонгон колдонмону кадап коюу"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"тез чакырмаларды орнотуу"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Колдонмого колдонуучуга кайрылбастан тез чакырма кошууга уруксат берет."</string>
@@ -85,7 +85,7 @@
<string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> телефон чалууларды аткарууга уруксаты жок"</string>
<string name="gadget_error_text" msgid="740356548025791839">"Виджет жүктөлбөй жатат"</string>
<string name="gadget_setup_text" msgid="8348374825537681407">"Виджеттин жөндөөлөрү"</string>
- <string name="gadget_complete_setup_text" msgid="309040266978007925">"Жөндөп бүтүү үчүн таптап коюңуз"</string>
+ <string name="gadget_complete_setup_text" msgid="309040266978007925">"Аягына чейин тууралоо үчүн басып коюңуз"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Бул системдик колдонмо жана аны чечкенге болбойт."</string>
<string name="folder_hint_text" msgid="5174843001373488816">"Аталышын түзөтүү"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> өчүрүлгөн"</string>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index d6535ce..b553c90 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -120,7 +120,7 @@
<string name="abandoned_clean_this" msgid="7610119707847920412">"काढा"</string>
<string name="abandoned_search" msgid="891119232568284442">"शोधा"</string>
<string name="abandoned_promises_title" msgid="7096178467971716750">"हा अॅप इंस्टॉल केलेला नाही"</string>
- <string name="abandoned_promise_explanation" msgid="3990027586878167529">"या चिन्हासाठी अॅप इंस्टॉल केलेला नाही. तुम्ही ते काढू शकता किंवा अॅपचा शोध घेऊ शकता आणि त्यास व्यक्तिचलितपणे इंस्टॉल करू शकता."</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"या आयकनसाठी अॅप इंस्टॉल केलेले नाही. तुम्ही तो काढू शकता किंवा अॅपचा शोध घेऊन ते मॅन्युअली इंस्टॉल करू शकता."</string>
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> इंस्टॉल करत आहे, <xliff:g id="PROGRESS">%2$s</xliff:g> पूर्ण झाले"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> डाउनलोड होत आहे , <xliff:g id="PROGRESS">%2$s</xliff:g> पूर्ण झाले"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> इंस्टॉल करण्याची प्रतिक्षा करत आहे"</string>
@@ -160,14 +160,14 @@
<string name="all_apps_personal_tab" msgid="4190252696685155002">"वैयक्तिक"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"कार्य"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"कार्य प्रोफाइल"</string>
- <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"कामाशी संबंधित ॲप्स ही बॅज केलेली असून तुमच्या आयटी ॲडमिनला दृश्यमान आहेत"</string>
+ <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"कार्य ॲप्स ही बॅज केलेली असून तुमच्या आयटी ॲडमिनला दृश्यमान आहेत"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"समजले"</string>
<string name="work_apps_paused_title" msgid="3040901117349444598">"कार्य ॲप्स थांबवली आहेत"</string>
<string name="work_apps_paused_body" msgid="261634750995824906">"तुमची कार्य ॲप्स तुम्हाला सूचना पाठवू शकत नाहीत, तुमची बॅटरी वापरू शकत नाहीत किंवा तुमचे स्थान अॅक्सेस करू शकत नाहीत"</string>
<string name="work_apps_paused_content_description" msgid="5149623040804051095">"कामाशी संबंधित ॲप्स बंद आहेत. तुमचे कामाशी संबंधित ॲप्स तुम्हाला सूचना पाठवू शकत नाहीत, तुमची बॅटरी वापरू शकत नाहीत किंवा तुमचे स्थान अॅक्सेस करू शकत नाहीत"</string>
- <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Work apps ही बॅज केलेली असून तुमच्या IT ॲडमिनला दृश्यमान आहेत"</string>
+ <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"कार्य ॲप्स ही बॅज केलेली असून तुमच्या आयटी ॲडमिनला दृश्यमान आहेत"</string>
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"समजले"</string>
- <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Work apps थांबवा"</string>
+ <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"कार्य ॲप्स थांबवा"</string>
<string name="work_apps_enable_btn_text" msgid="1156432622148413741">"कार्य ॲप्स सुरू करा"</string>
<string name="developer_options_filter_hint" msgid="5896817443635989056">"फिल्टर"</string>
<string name="search_pref_screen_title" msgid="3258959643336315962">"तुमच्या फोनमध्ये शोधा"</string>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index bf66399..69157bc 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -54,7 +54,7 @@
<string name="widget_education_header" msgid="4874760613775913787">"အသုံးဝင်သော အချက်အလက်များကို အလွယ်တကူ ရယူလိုက်ပါ"</string>
<string name="widget_education_content" msgid="1731667670753497052">"အက်ပ်မဖွင့်ဘဲ အချက်အလက်များရယူရန် ပင်မစာမျက်နှာတွင် ဝိဂျက်များ ထည့်နိုင်သည်"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"ဝိဂျက် ဆက်တင်များကို ပြောင်းရန် တို့ပါ"</string>
- <string name="widget_education_close_button" msgid="8676165703104836580">"ရပြီ"</string>
+ <string name="widget_education_close_button" msgid="8676165703104836580">"နားလည်ပြီ"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"ဝိဂျက် ဆက်တင်များကို ပြောင်းပါ"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"ရှာဖွေမှု အက်ပ်များ"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"အက်ပ်များကို ဖွင့်နေသည်…"</string>
@@ -74,7 +74,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"ဖယ်ရှားရန်"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"အက်ပ်အချက်အလက်"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"ထည့်သွင်းရန်"</string>
- <string name="dismiss_prediction_label" msgid="3357562989568808658">"အက်ပ်ကို အကြံမပြုပါနှင့်"</string>
+ <string name="dismiss_prediction_label" msgid="3357562989568808658">"အက်ပ်အကြံမပြုပါနှင့်"</string>
<string name="pin_prediction" msgid="4196423321649756498">"ခန့်မှန်းချက်ကို ပင်ထိုးရန်"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"ဖြတ်လမ်းလင့်ခ်များ ထည့်သွင်းခြင်း"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"အသုံးပြုသူ လုပ်ဆောင်မှုမရှိပဲ အပ်ပလီကေးရှင်းကို အတိုကောက်မှတ်သားမှုများ ပြုလုပ်ခွင့် ပေးခြင်း"</string>
@@ -114,7 +114,7 @@
<string name="title_change_settings" msgid="1376365968844349552">"ဆက်တင်များ ပြောင်းရန်"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"အကြောင်းကြားချက် အစက်များ ပြရန်"</string>
<string name="developer_options_title" msgid="700788437593726194">"တီထွင်သူ ရွေးစရာများ"</string>
- <string name="auto_add_shortcuts_label" msgid="4926805029653694105">"ပင်မစာမျက်နှာတွင် အက်ပ်သင်္ကေတထည့်ရန်"</string>
+ <string name="auto_add_shortcuts_label" msgid="4926805029653694105">"ပင်မစာမျက်နှာတွင် အက်ပ်သင်္ကေတထည့်ခြင်း"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"အက်ပ်အသစ်များအတွက်"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"မသိ"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"ဖယ်ရှားရန်"</string>
@@ -166,7 +166,7 @@
<string name="work_apps_paused_body" msgid="261634750995824906">"သင်၏ အလုပ်သုံးအက်ပ်များက အကြောင်းကြားချက်များ ပို့ခြင်း၊ သင့်ဘက်ထရီ သုံးခြင်း (သို့) သင့်တည်နေရာ သုံးခြင်းတို့ မပြုလုပ်နိုင်ပါ"</string>
<string name="work_apps_paused_content_description" msgid="5149623040804051095">"အလုပ်သုံးအက်ပ်များ ပိတ်ထားသည်။ သင်၏ အလုပ်သုံးအက်ပ်များက အကြောင်းကြားချက်များ ပို့ခြင်း၊ သင့်ဘက်ထရီ သုံးခြင်း (သို့) သင့်တည်နေရာ သုံးခြင်းတို့ မပြုလုပ်နိုင်ပါ"</string>
<string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"အလုပ်သုံးအက်ပ်များကို တံဆိပ်တပ်ထားပြီး သင်၏ IT စီမံခန့်ခွဲသူက မြင်နိုင်ပါသည်"</string>
- <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"ရပြီ"</string>
+ <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"နားလည်ပြီ"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"အလုပ်သုံးအက်ပ်များကို ခဏရပ်ရန်"</string>
<string name="work_apps_enable_btn_text" msgid="1156432622148413741">"အလုပ်သုံးအက်ပ်များ ဖွင့်ရန်"</string>
<string name="developer_options_filter_hint" msgid="5896817443635989056">"စစ်ထုတ်ရန်"</string>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index 8cef63d..6d48969 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -74,7 +74,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"अनइन्स्टल गर्नुहोस्"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"एपसम्बन्धी जानकारी"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"स्थापना गर्नुहोस्"</string>
- <string name="dismiss_prediction_label" msgid="3357562989568808658">"यो एप सिफारिस नगरियोस्"</string>
+ <string name="dismiss_prediction_label" msgid="3357562989568808658">"एप सिफारिस नगरियोस्"</string>
<string name="pin_prediction" msgid="4196423321649756498">"सिफारिस गरिएको एप पिन गर्नुहोस्"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"सर्टकट स्थापना गर्नेहोस्"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"प्रयोगकर्ताको हस्तक्षेप बिना एउटा एपलाई सर्टकटमा थप्नको लागि अनुमति दिनुहोस्।"</string>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index dd5561d..c64e9b2 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -26,7 +26,7 @@
<string name="safemode_shortcut_error" msgid="9160126848219158407">"ନିରାପଦ ମୋଡରେ ଡାଉନଲୋଡ୍ ହେଇଥିବା ଆପ୍ ଅକ୍ଷମ କରାଗଲା"</string>
<string name="safemode_widget_error" msgid="4863470563535682004">"ନିରାପଦ ମୋଡରେ ୱିଜେଟ୍ ଅକ୍ଷମ କରାଗଲା"</string>
<string name="shortcut_not_available" msgid="2536503539825726397">"ଶର୍ଟକଟ୍ ଉପଲବ୍ଧ ନାହିଁ"</string>
- <string name="home_screen" msgid="5629429142036709174">"ମୂଳପୃଷ୍ଠା"</string>
+ <string name="home_screen" msgid="5629429142036709174">"ହୋମ"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"ସ୍କ୍ରିନକୁ ସ୍ପ୍ଲିଟ୍ କରନ୍ତୁ"</string>
<string name="split_screen_position_top" msgid="1504965011158689649">"ଶୀର୍ଷକୁ ସ୍ପ୍ଲିଟ କରନ୍ତୁ"</string>
<string name="split_screen_position_left" msgid="7537793098851830883">"ବାମପତକୁ ସ୍ପ୍ଲିଟ କରନ୍ତୁ"</string>
@@ -37,9 +37,9 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d ଓସାର ଓ %2$d ଉଚ୍ଚ"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ୱିଜେଟ୍"</string>
- <string name="add_item_request_drag_hint" msgid="8730547755622776606">"ମୂଳସ୍କ୍ରିନର ଆଖପାଖରେ ୱିଜେଟକୁ ମୁଭ କରିବା ପାଇଁ ଏହାକୁ ସ୍ପର୍ଶ କରି ଧରି ରଖନ୍ତୁ"</string>
+ <string name="add_item_request_drag_hint" msgid="8730547755622776606">"ହୋମ ସ୍କ୍ରିନର ଆଖପାଖରେ ୱିଜେଟକୁ ମୁଭ କରିବା ପାଇଁ ଏହାକୁ ସ୍ପର୍ଶ କରି ଧରି ରଖନ୍ତୁ"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"ହୋମ ସ୍କ୍ରିନରେ ଯୋଗ କରନ୍ତୁ"</string>
- <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>ର ୱିଜେଟ୍ ମୂଳସ୍କ୍ରିନରେ ଯୋଡ଼ାଗଲା"</string>
+ <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>ର ୱିଜେଟ ହୋମ ସ୍କ୍ରିନରେ ଯୋଡ଼ାଗଲା"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{#ଟି ୱିଜେଟ୍}other{#ଟି ୱିଜେଟ୍}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{#ଟି ସର୍ଟକଟ୍}other{#ଟି ସର୍ଟକଟ୍}}"</string>
<string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
@@ -52,7 +52,7 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"ୱାର୍କ"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"ବାର୍ତ୍ତାଳାପଗୁଡ଼ିକ"</string>
<string name="widget_education_header" msgid="4874760613775913787">"ଉପଯୋଗୀ ସୂଚନା ଆପଣଙ୍କ ପାଖରେ ସହଜରେ ଉପଲବ୍ଧ"</string>
- <string name="widget_education_content" msgid="1731667670753497052">"ଆପ୍ସକୁ ନଖୋଲି ସୂଚନା ପାଇବା ପାଇଁ, ଆପଣ ଆପଣଙ୍କ ମୂଳସ୍କ୍ରିନରେ ୱିଜେଟଗୁଡ଼ିକୁ ଯୋଗ କରିପାରିବେ"</string>
+ <string name="widget_education_content" msgid="1731667670753497052">"ଆପ୍ସକୁ ନଖୋଲି ସୂଚନା ପାଇବା ପାଇଁ, ଆପଣ ଆପଣଙ୍କ ହୋମ ସ୍କ୍ରିନରେ ୱିଜେଟଗୁଡ଼ିକୁ ଯୋଗ କରିପାରିବେ"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"ୱିଜେଟ ସେଟିଂସ ପରିବର୍ତ୍ତନ କରିବାକୁ ଟାପ କରନ୍ତୁ"</string>
<string name="widget_education_close_button" msgid="8676165703104836580">"ବୁଝିଗଲି"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"ୱିଜେଟ ସେଟିଂସ ପରିବର୍ତ୍ତନ କରନ୍ତୁ"</string>
@@ -64,24 +64,24 @@
<string name="notifications_header" msgid="1404149926117359025">"ବିଜ୍ଞପ୍ତି"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"ଏକ ସର୍ଟକଟକୁ ମୁଭ୍ କରିବା ପାଇଁ ସ୍ପର୍ଶ କରି ଧରି ରଖନ୍ତୁ।"</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"ଏକ ସର୍ଟକଟକୁ ମୁଭ୍ କରିବା ପାଇଁ ଦୁଇଥର-ଟାପ୍ କରି ଧରି ରଖନ୍ତୁ କିମ୍ବା କଷ୍ଟମ୍ କାର୍ଯ୍ୟଗୁଡ଼ିକୁ ବ୍ୟବହାର କରନ୍ତୁ।"</string>
- <string name="out_of_space" msgid="6455557115204099579">"ଏହି ମୂଳସ୍କ୍ରିନରେ ଆଉ ଜାଗା ନାହିଁ"</string>
+ <string name="out_of_space" msgid="6455557115204099579">"ଏହି ହୋମ ସ୍କ୍ରିନରେ ଆଉ ଜାଗା ନାହିଁ"</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"ମନପସନ୍ଦ ଟ୍ରେରେ ଆଉ କୋଠରୀ ନାହିଁ"</string>
<string name="all_apps_button_label" msgid="8130441508702294465">"ଆପ୍ ତାଲିକା"</string>
<string name="all_apps_search_results" msgid="5889367432531296759">"ସନ୍ଧାନ ଫଳାଫଳ"</string>
<string name="all_apps_button_personal_label" msgid="1315764287305224468">"ବ୍ୟକ୍ତିଗତ ଆପ୍ ତାଲିକା"</string>
<string name="all_apps_button_work_label" msgid="7270707118948892488">"କାର୍ଯ୍ୟକାରୀ ଆପ୍ ତାଲିକା"</string>
<string name="remove_drop_target_label" msgid="7812859488053230776">"ବାହାର କରନ୍ତୁ"</string>
- <string name="uninstall_drop_target_label" msgid="4722034217958379417">"ଅନଇନଷ୍ଟଲ୍ କରନ୍ତୁ"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"ଅନଇନଷ୍ଟଲ କରନ୍ତୁ"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"ଆପ୍ ସୂଚନା"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"ଇନଷ୍ଟଲ୍ କରନ୍ତୁ"</string>
- <string name="dismiss_prediction_label" msgid="3357562989568808658">"ଆପ୍ ପରାମର୍ଶ ଦିଅନ୍ତୁ ନାହିଁ"</string>
+ <string name="dismiss_prediction_label" msgid="3357562989568808658">"ଆପ ପରାମର୍ଶ ଦିଅନ୍ତୁ ନାହିଁ"</string>
<string name="pin_prediction" msgid="4196423321649756498">"ପୂର୍ବାନୁମାନକୁ ପିନ୍ କରନ୍ତୁ"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"ସର୍ଟକଟ୍ ଇନଷ୍ଟଲ୍ କରନ୍ତୁ"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"ୟୁଜରଙ୍କ ବିନା ହସ୍ତକ୍ଷେପରେ ଶର୍ଟକଟ୍ ଯୋଡ଼ିବାକୁ ଆପକୁ ଅନୁମତି ଦିଏ।"</string>
- <string name="permlab_read_settings" msgid="5136500343007704955">"ମୂଳପୃଷ୍ଠା ସେଟିଂସ ଏବଂ ସର୍ଟକଟଗୁଡ଼ିକୁ ପଢ଼ନ୍ତୁ"</string>
- <string name="permdesc_read_settings" msgid="4208061150510996676">"ମୂଳପୃଷ୍ଠାରେ ଥିବା ସେଟିଂସ ଏବଂ ସର୍ଟକଟଗୁଡ଼ିକୁ ପଢ଼ିବା ପାଇଁ ଆପକୁ ଅନୁମତି ଦିଏ।"</string>
- <string name="permlab_write_settings" msgid="4820028712156303762">"ମୂଳପୃଷ୍ଠା ସେଟିଂସ ଏବଂ ସର୍ଟକଟଗୁଡ଼ିକୁ ଲେଖନ୍ତୁ"</string>
- <string name="permdesc_write_settings" msgid="726859348127868466">"ମୂଳପୃଷ୍ଠାରେ ଥିବା ସେଟିଂସ ଏବଂ ସର୍ଟକଟଗୁଡ଼ିକୁ ପରିବର୍ତ୍ତନ କରିବା ପାଇଁ ଆପକୁ ଅନୁମତି ଦିଏ।"</string>
+ <string name="permlab_read_settings" msgid="5136500343007704955">"ହୋମ ସେଟିଂସ ଏବଂ ସର୍ଟକଟଗୁଡ଼ିକୁ ପଢ଼ନ୍ତୁ"</string>
+ <string name="permdesc_read_settings" msgid="4208061150510996676">"ହୋମରେ ଥିବା ସେଟିଂସ ଏବଂ ସର୍ଟକଟଗୁଡ଼ିକୁ ପଢ଼ିବା ପାଇଁ ଆପକୁ ଅନୁମତି ଦିଏ।"</string>
+ <string name="permlab_write_settings" msgid="4820028712156303762">"ହୋମ ସେଟିଂସ ଏବଂ ସର୍ଟକଟଗୁଡ଼ିକୁ ଲେଖନ୍ତୁ"</string>
+ <string name="permdesc_write_settings" msgid="726859348127868466">"ହୋମରେ ଥିବା ସେଟିଂସ ଏବଂ ସର୍ଟକଟଗୁଡ଼ିକୁ ପରିବର୍ତ୍ତନ କରିବା ପାଇଁ ଆପକୁ ଅନୁମତି ଦିଏ।"</string>
<string name="msg_no_phone_permission" msgid="9208659281529857371">"ଫୋନ୍ କଲ୍ କରିବାକୁ <xliff:g id="APP_NAME">%1$s</xliff:g>କୁ ଅନୁମତି ଦିଆଯାଇ ନାହିଁ"</string>
<string name="gadget_error_text" msgid="740356548025791839">"ୱିଜେଟ୍ ଲୋଡ୍ କରାଯାଇପାରିବ ନାହିଁ"</string>
<string name="gadget_setup_text" msgid="8348374825537681407">"ୱିଜେଟ ସେଟିଂସ"</string>
@@ -91,8 +91,8 @@
<string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> ଅକ୍ଷମ କରାଗଲା"</string>
<string name="dotted_app_label" msgid="1865617679843363410">"{count,plural, =1{{app_name}ର #ଟି ବିଜ୍ଞପ୍ତି ଅଛି}other{{app_name}ର #ଟି ବିଜ୍ଞପ୍ତି ଅଛି}}"</string>
<string name="default_scroll_format" msgid="7475544710230993317">"ମୋଟ %2$dରୁ %1$d ନମ୍ବର ପୃଷ୍ଠା"</string>
- <string name="workspace_scroll_format" msgid="8458889198184077399">"%2$dରୁ %1$d ହୋମ୍ ସ୍କ୍ରୀନ୍"</string>
- <string name="workspace_new_page" msgid="257366611030256142">"ନୂଆ ହୋମ୍ ସ୍କ୍ରୀନ୍ ପୃଷ୍ଠା"</string>
+ <string name="workspace_scroll_format" msgid="8458889198184077399">"%2$dରୁ %1$d ହୋମ ସ୍କ୍ରିନ"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"ନୂଆ ହୋମ ସ୍କ୍ରିନ ପେଜ"</string>
<string name="folder_opened" msgid="94695026776264709">"<xliff:g id="HEIGHT">%2$d</xliff:g> / <xliff:g id="WIDTH">%1$d</xliff:g>ର ଫୋଲ୍ଡର ଖୋଲାଗଲା"</string>
<string name="folder_tap_to_close" msgid="4625795376335528256">"ଫୋଲ୍ଡର୍ ବନ୍ଦ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ"</string>
<string name="folder_tap_to_rename" msgid="4017685068016979677">"ନାମ ବଦଳାଇବା ସେଭ୍ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ"</string>
@@ -104,7 +104,7 @@
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"ୱାଲପେପର ଏବଂ ଷ୍ଟାଇଲ"</string>
<string name="settings_button_text" msgid="8873672322605444408">"ହୋମ ସେଟିଂସ"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"ଆପଣଙ୍କ ଆଡମିନଙ୍କ ଦ୍ୱାରା ଅକ୍ଷମ କରାଯାଇଛି"</string>
- <string name="allow_rotation_title" msgid="7222049633713050106">"ମୂଳସ୍କ୍ରିନ ରୋଟେସନକୁ ଅନୁମତି ଦିଅନ୍ତୁ"</string>
+ <string name="allow_rotation_title" msgid="7222049633713050106">"ହୋମ ସ୍କ୍ରିନ ରୋଟେସନକୁ ଅନୁମତି ଦିଅନ୍ତୁ"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"ଯେତେବେଳେ ଫୋନକୁ ବୁଲାଯାଇଥାଏ"</string>
<string name="notification_dots_title" msgid="9062440428204120317">"ବିଜ୍ଞପ୍ତି ଡଟ୍ସ"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"ଚାଲୁ"</string>
@@ -114,7 +114,7 @@
<string name="title_change_settings" msgid="1376365968844349552">"ସେଟିଂସ ପରିବର୍ତ୍ତନ କରନ୍ତୁ"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"ବିଜ୍ଞପ୍ତି ଡଟ୍ଗୁଡ଼ିକୁ ଦେଖାନ୍ତୁ"</string>
<string name="developer_options_title" msgid="700788437593726194">"ଡେଭେଲପର ବିକଳ୍ପଗୁଡ଼ିକ"</string>
- <string name="auto_add_shortcuts_label" msgid="4926805029653694105">"ମୂଳସ୍କ୍ରିନରେ ଆପ ଆଇକନଗୁଡ଼ିକୁ ଯୋଗ କରନ୍ତୁ"</string>
+ <string name="auto_add_shortcuts_label" msgid="4926805029653694105">"ହୋମ ସ୍କ୍ରିନରେ ଆପ ଆଇକନଗୁଡ଼ିକୁ ଯୋଗ କରନ୍ତୁ"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"ନୂଆ ଆପ୍ ପାଇଁ"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"ଅଜଣା"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"କାଢ଼ି ଦିଅନ୍ତୁ"</string>
@@ -132,7 +132,7 @@
<string name="widgets_list_closed" msgid="6141506579418771922">"ୱିଜେଟ୍ ତାଲିକା ବନ୍ଦ ହୋଇଛି"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"ହୋମ ସ୍କ୍ରିନରେ ଯୋଗ କରନ୍ତୁ"</string>
<string name="action_move_here" msgid="2170188780612570250">"ଆଇଟମ୍କୁ ଏଠାକୁ ଘୁଞ୍ଚାନ୍ତୁ"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"ହୋମ୍ ସ୍କ୍ରୀନରେ ଆଇଟମ୍ ଯୋଡ଼ାଗଲା"</string>
+ <string name="item_added_to_workspace" msgid="4211073925752213539">"ହୋମ ସ୍କ୍ରିନରେ ଆଇଟମ ଯୋଗ କରାଗଲା"</string>
<string name="item_removed" msgid="851119963877842327">"ଆଇଟମକୁ କାଢ଼ି ଦିଆଯାଇଛି"</string>
<string name="undo" msgid="4151576204245173321">"ପୂର୍ବବତ୍"</string>
<string name="action_move" msgid="4339390619886385032">"ଆଇଟମ୍ ଘୁଞ୍ଚାନ୍ତୁ"</string>
@@ -145,7 +145,7 @@
<string name="added_to_folder" msgid="4793259502305558003">"ଫୋଲ୍ଡରରେ ଆଇଟମ୍ ଯୋଡ଼ାଗଲା"</string>
<string name="create_folder_with" msgid="4050141361160214248">"ଏହି ନାମରେ ଫୋଲ୍ଡର ତିଆରି କରନ୍ତୁ: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"ଫୋଲ୍ଡର ତିଆରି କରାଗଲା"</string>
- <string name="action_move_to_workspace" msgid="39528912300293768">"ମୂଳସ୍କ୍ରିନକୁ ମୁଭ କରନ୍ତୁ"</string>
+ <string name="action_move_to_workspace" msgid="39528912300293768">"ହୋମ ସ୍କ୍ରିନକୁ ମୁଭ କରନ୍ତୁ"</string>
<string name="action_resize" msgid="1802976324781771067">"ଆକାର ବଦଳାନ୍ତୁ"</string>
<string name="action_increase_width" msgid="8773715375078513326">"ଚଉଡ଼ା ବଢ଼ାନ୍ତୁ"</string>
<string name="action_increase_height" msgid="459390020612501122">"ଉଚ୍ଚତା ବଢ଼ାନ୍ତୁ"</string>
@@ -160,14 +160,14 @@
<string name="all_apps_personal_tab" msgid="4190252696685155002">"ବ୍ୟକ୍ତିଗତ"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"ୱାର୍କ"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"ୱର୍କ ପ୍ରୋଫାଇଲ୍"</string>
- <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"ୱାର୍କ ଆପଗୁଡ଼ିକୁ ବ୍ୟାଜ୍ କରାଯାଇଛି ଏବଂ ସେଗୁଡ଼ିକ ଆପଣଙ୍କ IT ଆଡମିନଙ୍କୁ ଦୃଶ୍ୟମାନ ହେଉଛି"</string>
+ <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"ୱାର୍କ ଆପ୍ସ ବ୍ୟାଜ୍ କରାଯାଇଛି ଏବଂ ସେଗୁଡ଼ିକ ଆପଣଙ୍କ IT ଆଡମିନଙ୍କୁ ଦୃଶ୍ୟମାନ ହେଉଛି"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"ବୁଝିଗଲି"</string>
<string name="work_apps_paused_title" msgid="3040901117349444598">"ୱାର୍କ ଆପ୍ସ ବିରତ କରାଯାଇଛି"</string>
- <string name="work_apps_paused_body" msgid="261634750995824906">"ଆପଣଙ୍କ ୱାର୍କ ଆପଗୁଡ଼ିକ ଆପଣଙ୍କୁ ବିଜ୍ଞପ୍ତି ପଠାଇପାରିବ ନାହିଁ, ଆପଣଙ୍କ ବ୍ୟାଟେରୀକୁ ବ୍ୟବହାର କରିପାରିବ ନାହିଁ କିମ୍ବା ଆପଣଙ୍କର ଲୋକେସନକୁ ଆକ୍ସେସ୍ କରିପାରିବ ନାହିଁ"</string>
+ <string name="work_apps_paused_body" msgid="261634750995824906">"ଆପଣଙ୍କ ୱାର୍କ ଆପ୍ସ ଆପଣଙ୍କୁ ବିଜ୍ଞପ୍ତି ପଠାଇପାରିବ ନାହିଁ, ଆପଣଙ୍କ ବ୍ୟାଟେରୀକୁ ବ୍ୟବହାର କରିପାରିବ ନାହିଁ କିମ୍ବା ଆପଣଙ୍କର ଲୋକେସନକୁ ଆକ୍ସେସ କରିପାରିବ ନାହିଁ"</string>
<string name="work_apps_paused_content_description" msgid="5149623040804051095">"ୱାର୍କ ଆପଗୁଡ଼ିକ ବନ୍ଦ ଅଛି। ଆପଣଙ୍କ ୱାର୍କ ଆପଗୁଡ଼ିକ ଆପଣଙ୍କୁ ବିଜ୍ଞପ୍ତି ପଠାଇପାରିବ ନାହିଁ, ଆପଣଙ୍କ ବ୍ୟାଟେରୀକୁ ବ୍ୟବହାର କରିପାରିବ ନାହିଁ କିମ୍ବା ଆପଣଙ୍କର ଲୋକେସନକୁ ଆକ୍ସେସ୍ କରିପାରିବ ନାହିଁ"</string>
- <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"ୱାର୍କ ଆପଗୁଡ଼ିକୁ ବ୍ୟାଜ୍ କରାଯାଇଛି ଏବଂ ଆପଣଙ୍କ IT ଆଡମିନଙ୍କୁ ଦେଖାଯାଉଛି"</string>
+ <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"ୱାର୍କ ଆପ୍ସ ବ୍ୟାଜ୍ କରାଯାଇଛି ଏବଂ ଆପଣଙ୍କ IT ଆଡମିନଙ୍କୁ ଦେଖାଯାଉଛି"</string>
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"ବୁଝିଗଲି"</string>
- <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"ୱାର୍କ ଆପଗୁଡ଼ିକୁ ବିରତ କରନ୍ତୁ"</string>
+ <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"ୱାର୍କ ଆପ୍ସ ବିରତ କରନ୍ତୁ"</string>
<string name="work_apps_enable_btn_text" msgid="1156432622148413741">"ୱାର୍କ ଆପଗୁଡ଼ିକୁ ଚାଲୁ କରନ୍ତୁ"</string>
<string name="developer_options_filter_hint" msgid="5896817443635989056">"ଫିଲ୍ଟର୍"</string>
<string name="search_pref_screen_title" msgid="3258959643336315962">"ଆପଣଙ୍କ ଫୋନରେ ସନ୍ଧାନ କରନ୍ତୁ"</string>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index a291a92..8e55b87 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -167,7 +167,7 @@
<string name="work_apps_paused_content_description" msgid="5149623040804051095">"ਕੰਮ ਸੰਬੰਧੀ ਐਪਾਂ ਬੰਦ ਹਨ। ਤੁਹਾਡੀਆਂ ਕੰਮ ਸੰਬੰਧੀ ਐਪਾਂ ਤੁਹਾਨੂੰ ਸੂਚਨਾਵਾਂ ਨਹੀਂ ਭੇਜ ਸਕਦੀਆਂ, ਤੁਹਾਡੀ ਬੈਟਰੀ ਨਹੀਂ ਵਰਤ ਸਕਦੀਆਂ ਜਾਂ ਤੁਹਾਡੇ ਟਿਕਾਣੇ ਤੱਕ ਪਹੁੰਚ ਨਹੀਂ ਕਰ ਸਕਦੀਆਂ"</string>
<string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"ਕੰਮ ਸੰਬੰਧੀ ਐਪਾਂ ਨੂੰ ਬੈਜ ਕੀਤਾ ਜਾਂਦਾ ਹੈ ਅਤੇ ਇਹ ਤੁਹਾਡੇ ਆਈ.ਟੀ. ਪ੍ਰਸ਼ਾਸਕ ਨੂੰ ਦਿਸਣਗੀਆਂ"</string>
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"ਸਮਝ ਲਿਆ"</string>
- <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"ਕੰਮ ਸੰਬੰਧੀ ਐਪ ਰੋਕੋ"</string>
+ <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"ਕੰਮ ਸੰਬੰਧੀ ਐਪਾਂ ਰੋਕੋ"</string>
<string name="work_apps_enable_btn_text" msgid="1156432622148413741">"ਕੰਮ ਸੰਬੰਧੀ ਐਪਾਂ ਚਾਲੂ ਕਰੋ"</string>
<string name="developer_options_filter_hint" msgid="5896817443635989056">"ਫਿਲਟਰ"</string>
<string name="search_pref_screen_title" msgid="3258959643336315962">"ਆਪਣਾ ਫ਼ੋਨ ਖੋਜੋ"</string>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 448a56b..8669949 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -106,13 +106,13 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Funkcja wyłączona przez administratora"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Zezwalaj na obrót ekranu głównego"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Po obróceniu telefonu"</string>
- <string name="notification_dots_title" msgid="9062440428204120317">"Plakietki z powiadomieniami"</string>
+ <string name="notification_dots_title" msgid="9062440428204120317">"Kropki powiadomień"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Włączono"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Wyłączono"</string>
<string name="title_missing_notification_access" msgid="7503287056163941064">"Wymagany jest dostęp do powiadomień"</string>
- <string name="msg_missing_notification_access" msgid="281113995110910548">"Aby pokazać plakietki z powiadomieniami, włącz powiadomienia aplikacji <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="msg_missing_notification_access" msgid="281113995110910548">"Aby pokazywać kropki powiadomień, włącz powiadomienia aplikacji <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="title_change_settings" msgid="1376365968844349552">"Zmień ustawienia"</string>
- <string name="notification_dots_service_title" msgid="4284221181793592871">"Pokaż plakietki z powiadomieniami"</string>
+ <string name="notification_dots_service_title" msgid="4284221181793592871">"Pokaż kropki powiadomień"</string>
<string name="developer_options_title" msgid="700788437593726194">"Opcje programisty"</string>
<string name="auto_add_shortcuts_label" msgid="4926805029653694105">"Dodawaj ikony aplikacji do ekranu głównego"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"W przypadku nowych aplikacji"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index e2d8c9d..bb1fb36 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -167,7 +167,7 @@
<string name="work_apps_paused_content_description" msgid="5149623040804051095">"As apps de trabalho estão desativadas. As apps de trabalho não podem enviar-lhe notificações, utilizar a bateria ou aceder à sua localização"</string>
<string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"As apps de trabalho têm um emblema e estão visíveis para o seu administrador de TI"</string>
<string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"OK"</string>
- <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Colocar apps de trabalho em pausa"</string>
+ <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Pausar apps de trabalho"</string>
<string name="work_apps_enable_btn_text" msgid="1156432622148413741">"Ativar apps de trabalho"</string>
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtrar"</string>
<string name="search_pref_screen_title" msgid="3258959643336315962">"Pesquise no telemóvel"</string>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index ff2c226..69fa362 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -114,7 +114,7 @@
<string name="title_change_settings" msgid="1376365968844349552">"Zmeniť nastavenia"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"Zobrazovať bodky upozornení"</string>
<string name="developer_options_title" msgid="700788437593726194">"Pre vývojárov"</string>
- <string name="auto_add_shortcuts_label" msgid="4926805029653694105">"Pridať ikony aplikácií na plochu"</string>
+ <string name="auto_add_shortcuts_label" msgid="4926805029653694105">"Pridávať ikony aplikácií na plochu"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Pri inštalácii novej aplikácie"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"Neznáme"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"Odstrániť"</string>
diff --git a/res/values-sw600dp/config.xml b/res/values-sw600dp/config.xml
index 072b92d..e718d9c 100644
--- a/res/values-sw600dp/config.xml
+++ b/res/values-sw600dp/config.xml
@@ -18,4 +18,11 @@
<!-- The duration of the PagedView page snap animation -->
<integer name="config_pageSnapAnimationDuration">550</integer>
+ <!-- The duration of the Widget picker opening and closing animation -->
+ <integer name="config_bottomSheetOpenDuration">500</integer>
+ <integer name="config_bottomSheetCloseDuration">500</integer>
+
+ <!-- The duration of the AllApps opening and closing animation -->
+ <integer name="config_allAppsOpenDuration">@integer/config_bottomSheetOpenDuration</integer>
+ <integer name="config_allAppsCloseDuration">@integer/config_bottomSheetCloseDuration</integer>
</resources>
diff --git a/res/values-sw720dp/dimens.xml b/res/values-sw720dp/dimens.xml
index 7b2ed8b..09b2d6f 100644
--- a/res/values-sw720dp/dimens.xml
+++ b/res/values-sw720dp/dimens.xml
@@ -43,4 +43,7 @@
<!-- Bottom sheet-->
<dimen name="bottom_sheet_extra_top_padding">300dp</dimen>
+
+ <!-- Folder spaces -->
+ <dimen name="folder_footer_horiz_padding">24dp</dimen>
</resources>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index 08bccad..605278f 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -58,7 +58,7 @@
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"విడ్జెట్ సెట్టింగ్లను మార్చండి"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"యాప్ల కోసం సెర్చ్ చేయండి"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"అప్లికేషన్లను లోడ్ చేస్తోంది…"</string>
- <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\"కి సరిపోలే అప్లికేషన్లేవీ కనుగొనబడలేదు"</string>
+ <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\"కి మ్యాచ్ అయ్యే అప్లికేషన్లేవీ కనుగొనబడలేదు"</string>
<string name="label_application" msgid="8531721983832654978">"యాప్"</string>
<string name="all_apps_label" msgid="5015784846527570951">"అన్ని యాప్లు"</string>
<string name="notifications_header" msgid="1404149926117359025">"నోటిఫికేషన్లు"</string>
@@ -74,7 +74,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"అన్ఇన్స్టాల్ చేయండి"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"యాప్ సమాచారం"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"ఇన్స్టాల్ చేయండి"</string>
- <string name="dismiss_prediction_label" msgid="3357562989568808658">"యాప్ను సూచించవద్దు"</string>
+ <string name="dismiss_prediction_label" msgid="3357562989568808658">"యాప్ సూచించకు"</string>
<string name="pin_prediction" msgid="4196423321649756498">"సూచనను పిన్ చేయండి"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"షార్ట్కట్లను ఇన్స్టాల్ చేయడం"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"వినియోగదారు ప్రమేయం లేకుండా షార్ట్కట్లను జోడించడానికి యాప్ను అనుమతిస్తుంది."</string>
@@ -146,12 +146,12 @@
<string name="create_folder_with" msgid="4050141361160214248">"ఈ పేరుతో ఫోల్డర్ను క్రియేట్ చేయండి: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"ఫోల్డర్ క్రియేట్ చేయబడింది"</string>
<string name="action_move_to_workspace" msgid="39528912300293768">"మొదటి స్క్రీన్కు తరలించండి"</string>
- <string name="action_resize" msgid="1802976324781771067">"పరిమాణం మార్చు"</string>
+ <string name="action_resize" msgid="1802976324781771067">"సైజ్ మార్చు"</string>
<string name="action_increase_width" msgid="8773715375078513326">"వెడల్పును పెంచు"</string>
<string name="action_increase_height" msgid="459390020612501122">"ఎత్తును పెంచు"</string>
<string name="action_decrease_width" msgid="1374549771083094654">"వెడల్పును తగ్గించు"</string>
<string name="action_decrease_height" msgid="282377193880900022">"ఎత్తును తగ్గించు"</string>
- <string name="widget_resized" msgid="9130327887929620">"విడ్జెట్ పరిమాణం వెడల్పు <xliff:g id="NUMBER_0">%1$s</xliff:g>కి, ఎత్తు <xliff:g id="NUMBER_1">%2$s</xliff:g>కి మార్చబడింది"</string>
+ <string name="widget_resized" msgid="9130327887929620">"విడ్జెట్ సైజ్ వెడల్పు <xliff:g id="NUMBER_0">%1$s</xliff:g>కి, ఎత్తు <xliff:g id="NUMBER_1">%2$s</xliff:g>కి మార్చబడింది"</string>
<string name="action_deep_shortcut" msgid="2864038805849372848">"షార్ట్కట్స్"</string>
<string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"షార్ట్కట్లు మరియు నోటిఫికేషన్లు"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"తీసివేయండి"</string>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index deb4834..ffeb20c 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -102,7 +102,7 @@
<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="settings_button_text" msgid="8873672322605444408">"การตั้งค่าหน้าแรก"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"การตั้งค่าหน้าจอหลัก"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"ปิดใช้โดยผู้ดูแลระบบ"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"อนุญาตให้หมุนหน้าจอหลัก"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"เมื่อหมุนโทรศัพท์"</string>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index 70f8e95..2f79f23 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -114,7 +114,7 @@
<string name="title_change_settings" msgid="1376365968844349552">"Змінити налаштування"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"Показувати значки сповіщень"</string>
<string name="developer_options_title" msgid="700788437593726194">"Параметри розробника"</string>
- <string name="auto_add_shortcuts_label" msgid="4926805029653694105">"Розміщати значки додатків на головний екран"</string>
+ <string name="auto_add_shortcuts_label" msgid="4926805029653694105">"Розміщати значки додатків на головному екрані"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Для нових додатків"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"Невідомо"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"Прибрати"</string>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index df1ca14..f270b10 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -56,12 +56,6 @@
<attr name="preloadIconAccentColor" format="color" />
<attr name="preloadIconBackgroundColor" format="color" />
- <attr name="allAppsButtonBgColor" format="color" />
- <attr name="allAppsButtonColor1" format="color" />
- <attr name="allAppsButtonColor2" format="color" />
- <attr name="allAppsButtonColor3" format="color" />
- <attr name="allAppsButtonColor4" format="color" />
-
<!-- BubbleTextView specific attributes. -->
<declare-styleable name="BubbleTextView">
<attr name="layoutHorizontal" format="boolean" />
@@ -147,9 +141,12 @@
<attr name="numColumns" format="integer" />
<!-- numSearchContainerColumns defaults to numColumns, if not specified -->
<attr name="numSearchContainerColumns" format="integer" />
+
<!-- numFolderRows & numFolderColumns defaults to numRows & numColumns, if not specified -->
<attr name="numFolderRows" format="integer" />
<attr name="numFolderColumns" format="integer" />
+ <attr name="folderStyle" format="reference" />
+
<!-- numAllAppsColumns defaults to numColumns, if not specified -->
<attr name="numAllAppsColumns" format="integer" />
<!-- Number of columns to use when extending the all-apps size,
@@ -184,7 +181,7 @@
<attr name="isScalable" format="boolean" />
<attr name="devicePaddingId" format="reference" />
<!-- By default all categories are enabled -->
- <attr name="deviceCategory" format="integer" >
+ <attr name="deviceCategory" format="integer">
<!-- Enable on phone only -->
<flag name="phone" value="1" />
<!-- Enable on tablets only -->
@@ -193,6 +190,18 @@
<flag name="multi_display" value="4" />
</attr>
+ <!-- By default all are false -->
+ <attr name="inlineQsb" format="integer">
+ <!-- Enable on landscape only -->
+ <flag name="portrait" value="1" />
+ <!-- Enable on portrait only -->
+ <flag name="landscape" value="2" />
+ <!-- Enable on two panel portrait only -->
+ <flag name="twoPanelPortrait" value="4" />
+ <!-- Enable on two panel landscape only -->
+ <flag name="twoPanelLandscape" value="8" />
+ </attr>
+
</declare-styleable>
<declare-styleable name="DevicePadding">
@@ -373,19 +382,24 @@
<!-- defaults to horizontalMargin if not specified -->
<attr name="horizontalMarginTwoPanelPortrait" format="float"/>
- <!-- By default all are false -->
- <attr name="inlineQsb" format="integer" >
- <!-- Enable on landscape only -->
- <flag name="portrait" value="1" />
- <!-- Enable on portrait only -->
- <flag name="landscape" value="2" />
- <!-- Enable on two panel portrait only -->
- <flag name="twoPanelPortrait" value="4" />
- <!-- Enable on two panel landscape only -->
- <flag name="twoPanelLandscape" value="8" />
- </attr>
</declare-styleable>
+ <declare-styleable name="FolderDisplayStyle">
+ <!-- defaults to minCellHeight if not specified
+ when GridDisplayOption#isScalable is true. -->
+ <attr name="folderCellHeight" format="dimension" />
+ <!-- defaults to minCellWidth, if not specified -->
+ <attr name="folderCellWidth" format="dimension" />
+ <!-- space to be used horizontally and vertically between icons,
+ and to the left and right of folder -->
+ <attr name="folderBorderSpace" format="dimension" />
+ <!-- height of the footer of the folder -->
+ <attr name="folderFooterHeight" format="dimension" />
+ <!-- padding on top of the folder -->
+ <attr name="folderTopPadding" format="dimension" />
+ </declare-styleable>
+
+
<declare-styleable name="CellLayout">
<attr name="containerType" format="integer">
<enum name="workspace" value="0" />
diff --git a/res/values/config.xml b/res/values/config.xml
index 1415ed0..d9b3da5 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -85,6 +85,7 @@
<string name="launcher_activity_logic_class" translatable="false"></string>
<string name="model_delegate_class" translatable="false"></string>
<string name="window_manager_proxy_class" translatable="false"></string>
+ <string name="secondary_display_predictions_class" translatable="false"></string>
<!-- View ID to use for QSB widget -->
<item type="id" name="qsb_widget" />
@@ -192,4 +193,16 @@
<dimen name="swipe_back_window_scale_x_margin">10dp</dimen>
<dimen name="swipe_back_window_max_delta_y">160dp</dimen>
<dimen name="swipe_back_window_corner_radius">40dp</dimen>
+
+ <!-- The duration of the bottom sheet opening and closing animation -->
+ <integer name="config_bottomSheetOpenDuration">267</integer>
+ <integer name="config_bottomSheetCloseDuration">267</integer>
+
+ <!-- The duration of the AllApps opening and closing animation -->
+ <integer name="config_allAppsOpenDuration">600</integer>
+ <integer name="config_allAppsCloseDuration">300</integer>
+
+ <!-- The max scale for the wallpaper when it's zoomed in -->
+ <item name="config_wallpaperMaxScale" format="float" type="dimen">0</item>
+
</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 546ee35..3b92ac4 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -121,6 +121,7 @@
<dimen name="all_apps_work_profile_tab_footer_bottom_padding">20dp</dimen>
<dimen name="all_apps_tabs_button_horizontal_padding">4dp</dimen>
<dimen name="all_apps_tabs_vertical_padding">6dp</dimen>
+ <dimen name="all_apps_tabs_margin_top">8dp</dimen>
<dimen name="all_apps_divider_height">2dp</dimen>
<dimen name="all_apps_divider_width">128dp</dimen>
<dimen name="all_apps_content_fade_in_offset">150dp</dimen>
@@ -128,9 +129,9 @@
<dimen name="all_apps_height_extra">6dp</dimen>
<dimen name="all_apps_bottom_sheet_horizontal_padding">0dp</dimen>
<dimen name="all_apps_paged_view_top_padding">40dp</dimen>
- <dimen name="all_apps_personal_work_tabs_vertical_margin">16dp</dimen>
<dimen name="all_apps_icon_drawable_padding">8dp</dimen>
+ <dimen name="all_apps_predicted_icon_vertical_padding">8dp</dimen>
<!-- The size of corner radius of the arrow in the arrow toast. -->
<dimen name="arrow_toast_corner_radius">2dp</dimen>
<dimen name="arrow_toast_elevation">2dp</dimen>
@@ -226,6 +227,7 @@
<dimen name="drop_target_text_size">16sp</dimen>
<dimen name="drop_target_shadow_elevation">2dp</dimen>
<dimen name="drop_target_bar_margin_horizontal">4dp</dimen>
+ <dimen name="drop_target_button_drawable_size">20dp</dimen>
<dimen name="drop_target_button_drawable_padding">8dp</dimen>
<dimen name="drop_target_button_drawable_horizontal_padding">16dp</dimen>
<dimen name="drop_target_button_drawable_vertical_padding">8dp</dimen>
@@ -246,12 +248,14 @@
<!-- Folders -->
<dimen name="page_indicator_dot_size">8dp</dimen>
+ <dimen name="page_indicator_size">10dp</dimen>
+
<dimen name="folder_cell_x_padding">9dp</dimen>
<dimen name="folder_cell_y_padding">6dp</dimen>
<!-- label text size = workspace text size multiplied by this scale -->
<dimen name="folder_label_text_scale">1.14</dimen>
- <dimen name="folder_label_height">48dp</dimen>
+ <dimen name="folder_footer_height_default">56dp</dimen>
<dimen name="folder_content_padding_left_right">8dp</dimen>
<dimen name="folder_content_padding_top">16dp</dimen>
@@ -329,7 +333,7 @@
<!-- Snackbar -->
<dimen name="snackbar_height">48dp</dimen>
- <dimen name="snackbar_content_height">32dp</dimen>
+ <dimen name="snackbar_content_height">48dp</dimen>
<dimen name="snackbar_padding">8dp</dimen>
<dimen name="snackbar_min_margin_left_right">6dp</dimen>
<dimen name="snackbar_max_margin_left_right">72dp</dimen>
@@ -357,6 +361,15 @@
<dimen name="min_hotseat_icon_space">18dp</dimen>
<dimen name="min_hotseat_qsb_width">0dp</dimen>
<dimen name="taskbar_icon_size">44dp</dimen>
+ <dimen name="transient_taskbar_icon_size">57dp</dimen>
+ <!-- Transient taskbar (placeholders to compile in Launcher3 without Quickstep) -->
+ <dimen name="transient_taskbar_size">0dp</dimen>
+ <dimen name="transient_taskbar_margin">0dp</dimen>
+ <dimen name="transient_taskbar_shadow_blur">0dp</dimen>
+ <dimen name="transient_taskbar_key_shadow_distance">0dp</dimen>
+ <dimen name="transient_taskbar_stashed_size">0dp</dimen>
+ <!-- Note that this applies to both sides of all icons, so visible space is double this. -->
+ <dimen name="transient_taskbar_icon_spacing">0dp</dimen>
<!-- Note that this applies to both sides of all icons, so visible space is double this. -->
<dimen name="taskbar_icon_spacing">8dp</dimen>
<dimen name="taskbar_nav_buttons_size">0dp</dimen>
@@ -364,10 +377,16 @@
<dimen name="taskbar_hotseat_nav_spacing">0dp</dimen>
<dimen name="taskbar_button_margin_default">0dp</dimen>
<dimen name="taskbar_button_space_inbetween">0dp</dimen>
+ <dimen name="taskbar_button_space_inbetween_phone">0dp</dimen>
<dimen name="taskbar_button_margin_5_5">0dp</dimen>
<dimen name="taskbar_button_margin_6_5">0dp</dimen>
<dimen name="taskbar_button_margin_4_5">0dp</dimen>
<dimen name="taskbar_button_margin_4_4">0dp</dimen>
+ <!-- Taskbar swipe up thresholds threshold -->
+ <dimen name="taskbar_nav_threshold">0dp</dimen>
+ <dimen name="taskbar_app_window_threshold">0dp</dimen>
+ <dimen name="taskbar_home_overview_threshold">0dp</dimen>
+ <dimen name="taskbar_catch_up_threshold">0dp</dimen>
<!-- Size of the maximum radius for the enforced rounded rectangles. -->
<dimen name="enforced_rounded_corner_max_radius">16dp</dimen>
@@ -422,4 +441,8 @@
<!-- State transition -->
<item name="workspace_content_scale" format="float" type="dimen">0.97</item>
+
+ <!-- Folder spaces -->
+ <dimen name="folder_top_padding_default">24dp</dimen>
+ <dimen name="folder_footer_horiz_padding">20dp</dimen>
</resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 90553a1..9e75a31 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -207,14 +207,6 @@
<item name="android:importantForAccessibility">no</item>
</style>
- <style name="AllAppsButtonTheme">
- <item name="allAppsButtonBgColor">@color/all_apps_button_bg_color</item>
- <item name="allAppsButtonColor1">@color/all_apps_button_color_1</item>
- <item name="allAppsButtonColor2">@color/all_apps_button_color_2</item>
- <item name="allAppsButtonColor3">@color/all_apps_button_color_3</item>
- <item name="allAppsButtonColor4">@color/all_apps_button_color_4</item>
- </style>
-
<style name="AllAppsTheme">
<item name="disabledIconAlpha">.54</item>
</style>
@@ -317,4 +309,13 @@
<item name="android:windowLightStatusBar">true</item>
<item name="android:windowTranslucentStatus">true</item>
</style>
+
+ <style name="FolderDefaultStyle">
+ <item name="folderTopPadding">24dp</item>
+ <item name="folderCellHeight">94dp</item>
+ <item name="folderCellWidth">80dp</item>
+ <item name="folderBorderSpace">16dp</item>
+ <item name="folderFooterHeight">56dp</item>
+ </style>
+
</resources>
diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java
index 21dbc5f..73acd87 100644
--- a/src/com/android/launcher3/AbstractFloatingView.java
+++ b/src/com/android/launcher3/AbstractFloatingView.java
@@ -94,6 +94,7 @@
public static final int TYPE_TASKBAR_EDUCATION_DIALOG = 1 << 16;
public static final int TYPE_TASKBAR_ALL_APPS = 1 << 17;
public static final int TYPE_ADD_TO_HOME_CONFIRMATION = 1 << 18;
+ public static final int TYPE_TASKBAR_OVERLAY_PROXY = 1 << 19;
public static final int TYPE_ALL = TYPE_FOLDER | TYPE_ACTION_POPUP
| TYPE_WIDGETS_BOTTOM_SHEET | TYPE_WIDGET_RESIZE_FRAME | TYPE_WIDGETS_FULL_SHEET
@@ -109,10 +110,6 @@
| TYPE_ALL_APPS_EDU | TYPE_ICON_SURFACE | TYPE_WIDGETS_EDUCATION_DIALOG
| TYPE_TASKBAR_EDUCATION_DIALOG | TYPE_TASKBAR_ALL_APPS | TYPE_OPTIONS_POPUP_DIALOG;
- // Usually we show the back button when a floating view is open. Instead, hide for these types.
- public static final int TYPE_HIDE_BACK_BUTTON = TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE
- | TYPE_SNACKBAR | TYPE_WIDGET_RESIZE_FRAME | TYPE_LISTENER;
-
public static final int TYPE_ACCESSIBLE = TYPE_ALL & ~TYPE_DISCOVERY_BOUNCE & ~TYPE_LISTENER
& ~TYPE_ALL_APPS_EDU;
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index fbb0a57..76a91c0 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -32,6 +32,7 @@
import androidx.annotation.Px;
import com.android.launcher3.accessibility.DragViewStateAnnouncer;
+import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
import com.android.launcher3.logging.InstanceId;
@@ -248,11 +249,11 @@
/* widgetHandler= */ null,
(ItemInfo) mWidgetView.getTag()));
mLauncher
- .getAppWidgetHost()
- .startConfigActivity(
- mLauncher,
- mWidgetView.getAppWidgetId(),
- Launcher.REQUEST_RECONFIGURE_APPWIDGET);
+ .getAppWidgetHolder()
+ .startConfigActivity(
+ mLauncher,
+ mWidgetView.getAppWidgetId(),
+ Launcher.REQUEST_RECONFIGURE_APPWIDGET);
});
if (!hasSeenReconfigurableWidgetEducationTip()) {
post(() -> {
@@ -265,7 +266,7 @@
}
}
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) mWidgetView.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) mWidgetView.getLayoutParams();
ItemInfo widgetInfo = (ItemInfo) mWidgetView.getTag();
lp.cellX = lp.tmpCellX = widgetInfo.cellX;
lp.cellY = lp.tmpCellY = widgetInfo.cellY;
@@ -405,7 +406,7 @@
*/
private void resizeWidgetIfNeeded(boolean onDismiss) {
ViewGroup.LayoutParams wlp = mWidgetView.getLayoutParams();
- if (!(wlp instanceof CellLayout.LayoutParams)) {
+ if (!(wlp instanceof CellLayoutLayoutParams)) {
return;
}
DeviceProfile dp = mLauncher.getDeviceProfile();
@@ -420,7 +421,7 @@
mDirectionVector[0] = 0;
mDirectionVector[1] = 0;
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) wlp;
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) wlp;
int spanX = lp.cellHSpan;
int spanY = lp.cellVSpan;
diff --git a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
index 75e89b2..eb6d096 100644
--- a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
+++ b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
@@ -2,7 +2,6 @@
import static android.os.Process.myUserHandle;
-import android.appwidget.AppWidgetHost;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
import android.content.BroadcastReceiver;
@@ -12,6 +11,7 @@
import android.database.Cursor;
import android.util.Log;
+import androidx.annotation.NonNull;
import androidx.annotation.WorkerThread;
import com.android.launcher3.LauncherSettings.Favorites;
@@ -21,7 +21,7 @@
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.provider.RestoreDbTask;
import com.android.launcher3.util.ContentWriter;
-import com.android.launcher3.widget.LauncherAppWidgetHost;
+import com.android.launcher3.widget.LauncherWidgetHolder;
public class AppWidgetsRestoredReceiver extends BroadcastReceiver {
@@ -32,7 +32,7 @@
if (AppWidgetManager.ACTION_APPWIDGET_HOST_RESTORED.equals(intent.getAction())) {
int hostId = intent.getIntExtra(AppWidgetManager.EXTRA_HOST_ID, 0);
Log.d(TAG, "Widget ID map received for host:" + hostId);
- if (hostId != LauncherAppWidgetHost.APPWIDGET_HOST_ID) {
+ if (hostId != LauncherWidgetHolder.APPWIDGET_HOST_ID) {
return;
}
@@ -50,11 +50,11 @@
* Updates the app widgets whose id has changed during the restore process.
*/
@WorkerThread
- public static void restoreAppWidgetIds(Context context, int[] oldWidgetIds, int[] newWidgetIds) {
- AppWidgetHost appWidgetHost = new LauncherAppWidgetHost(context);
+ public static void restoreAppWidgetIds(Context context, int[] oldWidgetIds, int[] newWidgetIds,
+ @NonNull LauncherWidgetHolder holder) {
if (WidgetsModel.GO_DISABLE_WIDGETS) {
Log.e(TAG, "Skipping widget ID remap as widgets not supported");
- appWidgetHost.deleteHost();
+ holder.deleteHost();
return;
}
if (!RestoreDbTask.isPending(context)) {
@@ -63,7 +63,7 @@
Log.e(TAG, "Skipping widget ID remap as DB already in use");
for (int widgetId : newWidgetIds) {
Log.d(TAG, "Deleting widgetId: " + widgetId);
- appWidgetHost.deleteAppWidgetId(widgetId);
+ holder.deleteAppWidgetId(widgetId);
}
return;
}
@@ -100,7 +100,7 @@
try {
if (!cursor.moveToFirst()) {
// The widget no long exists.
- appWidgetHost.deleteAppWidgetId(newWidgetIds[i]);
+ holder.deleteAppWidgetId(newWidgetIds[i]);
}
} finally {
cursor.close();
diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java
index 64666b0..efdd5e1 100644
--- a/src/com/android/launcher3/AutoInstallsLayout.java
+++ b/src/com/android/launcher3/AutoInstallsLayout.java
@@ -16,7 +16,6 @@
package com.android.launcher3;
-import android.appwidget.AppWidgetHost;
import android.content.ComponentName;
import android.content.ContentValues;
import android.content.Context;
@@ -48,6 +47,7 @@
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.Thunk;
+import com.android.launcher3.widget.LauncherWidgetHolder;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -74,7 +74,7 @@
private static final String FORMATTED_LAYOUT_RES = "default_layout_%dx%d";
private static final String LAYOUT_RES = "default_layout";
- static AutoInstallsLayout get(Context context, AppWidgetHost appWidgetHost,
+ static AutoInstallsLayout get(Context context, LauncherWidgetHolder appWidgetHolder,
LayoutParserCallback callback) {
Pair<String, Resources> customizationApkInfo = PackageManagerHelper.findSystemApk(
ACTION_LAUNCHER_CUSTOMIZATION, context.getPackageManager());
@@ -109,7 +109,7 @@
Log.e(TAG, "Layout definition not found in package: " + pkg);
return null;
}
- return new AutoInstallsLayout(context, appWidgetHost, callback, targetRes, layoutId,
+ return new AutoInstallsLayout(context, appWidgetHolder, callback, targetRes, layoutId,
TAG_WORKSPACE);
}
@@ -156,7 +156,7 @@
@Thunk
final Context mContext;
@Thunk
- final AppWidgetHost mAppWidgetHost;
+ final LauncherWidgetHolder mAppWidgetHolder;
protected final LayoutParserCallback mCallback;
protected final PackageManager mPackageManager;
@@ -174,17 +174,17 @@
protected SQLiteDatabase mDb;
- public AutoInstallsLayout(Context context, AppWidgetHost appWidgetHost,
+ public AutoInstallsLayout(Context context, LauncherWidgetHolder appWidgetHolder,
LayoutParserCallback callback, Resources res,
int layoutId, String rootTag) {
- this(context, appWidgetHost, callback, res, () -> res.getXml(layoutId), rootTag);
+ this(context, appWidgetHolder, callback, res, () -> res.getXml(layoutId), rootTag);
}
- public AutoInstallsLayout(Context context, AppWidgetHost appWidgetHost,
+ public AutoInstallsLayout(Context context, LauncherWidgetHolder appWidgetHolder,
LayoutParserCallback callback, Resources res,
Supplier<XmlPullParser> initialLayoutSupplier, String rootTag) {
mContext = context;
- mAppWidgetHost = appWidgetHost;
+ mAppWidgetHolder = appWidgetHolder;
mCallback = callback;
mPackageManager = context.getPackageManager();
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index d34f535..9bdc822 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -25,12 +25,16 @@
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.res.Configuration;
+import android.os.Bundle;
+import android.window.OnBackInvokedDispatcher;
import androidx.annotation.IntDef;
import com.android.launcher3.DeviceProfile.DeviceProfileListenable;
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.logging.StatsLogManager;
+import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.util.ViewCache;
import com.android.launcher3.views.AppLauncher;
@@ -172,6 +176,19 @@
}
@Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (Utilities.ATLEAST_T) {
+ getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
+ OnBackInvokedDispatcher.PRIORITY_DEFAULT,
+ () -> {
+ onBackPressed();
+ TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onBackInvoked");
+ });
+ }
+ }
+
+ @Override
protected void onStart() {
addActivityFlags(ACTIVITY_STATE_STARTED);
super.onStart();
@@ -179,8 +196,7 @@
@Override
protected void onResume() {
- addActivityFlags(ACTIVITY_STATE_RESUMED | ACTIVITY_STATE_USER_ACTIVE);
- removeActivityFlags(ACTIVITY_STATE_USER_WILL_BE_ACTIVE);
+ setResumed();
super.onResume();
}
@@ -211,7 +227,7 @@
@Override
protected void onPause() {
- removeActivityFlags(ACTIVITY_STATE_RESUMED | ACTIVITY_STATE_DEFERRED_RESUMED);
+ setPaused();
super.onPause();
// Reset the overridden sysui flags used for the task-swipe launch animation, we do this
@@ -243,6 +259,21 @@
return (mActivityFlags & ACTIVITY_STATE_RESUMED) != 0;
}
+ /**
+ * Sets the activity to appear as paused.
+ */
+ public void setPaused() {
+ removeActivityFlags(ACTIVITY_STATE_RESUMED | ACTIVITY_STATE_DEFERRED_RESUMED);
+ }
+
+ /**
+ * Sets the activity to appear as resumed.
+ */
+ public void setResumed() {
+ addActivityFlags(ACTIVITY_STATE_RESUMED | ACTIVITY_STATE_USER_ACTIVE);
+ removeActivityFlags(ACTIVITY_STATE_USER_WILL_BE_ACTIVE);
+ }
+
public boolean isUserActive() {
return (mActivityFlags & ACTIVITY_STATE_USER_ACTIVE) != 0;
}
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 5fb8925..9f54f09 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -607,15 +607,16 @@
* Get the icon bounds on the view depending on the layout type.
*/
public void getIconBounds(int iconSize, Rect outBounds) {
- Utilities.setRectToViewCenter(this, iconSize, outBounds);
+ outBounds.set(0, 0, iconSize, iconSize);
if (mLayoutHorizontal) {
+ int top = (getHeight() - iconSize) / 2;
if (mIsRtl) {
- outBounds.offsetTo(getWidth() - iconSize - getPaddingRight(), outBounds.top);
+ outBounds.offsetTo(getWidth() - iconSize - getPaddingRight(), top);
} else {
- outBounds.offsetTo(getPaddingLeft(), outBounds.top);
+ outBounds.offsetTo(getPaddingLeft(), top);
}
} else {
- outBounds.offsetTo(outBounds.left, getPaddingTop());
+ outBounds.offset((getWidth() - iconSize) / 2, getPaddingTop());
}
}
@@ -942,6 +943,11 @@
return mIconSize;
}
+ public boolean isDisplaySearchResult() {
+ return mDisplay == DISPLAY_SEARCH_RESULT ||
+ mDisplay == DISPLAY_SEARCH_RESULT_SMALL;
+ }
+
private void updateTranslation() {
super.setTranslationX(mTranslationForReorderBounce.x + mTranslationForReorderPreview.x
+ mTranslationForMoveFromCenterAnimation.x
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index 3b24df2..5abe3d3 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -91,7 +91,7 @@
Resources resources = getResources();
mDragDistanceThreshold = resources.getDimensionPixelSize(R.dimen.drag_distanceThreshold);
- mDrawableSize = resources.getDimensionPixelSize(R.dimen.drop_target_text_size);
+ mDrawableSize = resources.getDimensionPixelSize(R.dimen.drop_target_button_drawable_size);
mDrawablePadding = resources.getDimensionPixelSize(
R.dimen.drop_target_button_drawable_padding);
}
@@ -374,11 +374,63 @@
hideTooltip();
}
+ /**
+ * Returns if the text will be truncated within the provided availableWidth.
+ */
public boolean isTextTruncated(int availableWidth) {
- availableWidth -= (getPaddingLeft() + getPaddingRight() + mDrawable.getIntrinsicWidth()
- + getCompoundDrawablePadding());
- CharSequence displayedText = TextUtils.ellipsize(mText, getPaint(), availableWidth,
+ availableWidth -= getPaddingLeft() + getPaddingRight();
+ if (mIconVisible) {
+ availableWidth -= mDrawable.getIntrinsicWidth() + getCompoundDrawablePadding();
+ }
+ if (availableWidth <= 0) {
+ return true;
+ }
+ CharSequence firstLine = TextUtils.ellipsize(mText, getPaint(), availableWidth,
TextUtils.TruncateAt.END);
- return !mText.equals(displayedText);
+ if (!mTextMultiLine) {
+ return !TextUtils.equals(mText, firstLine);
+ }
+ if (TextUtils.equals(mText, firstLine)) {
+ // When multi-line is active, if it can display as one line, then text is not truncated.
+ return false;
+ }
+ CharSequence secondLine =
+ TextUtils.ellipsize(mText.subSequence(firstLine.length(), mText.length()),
+ getPaint(), availableWidth, TextUtils.TruncateAt.END);
+ return !(TextUtils.equals(mText.subSequence(0, firstLine.length()), firstLine)
+ && TextUtils.equals(mText.subSequence(firstLine.length(), secondLine.length()),
+ secondLine));
+ }
+
+ /**
+ * Reduce the size of the text until it fits the measured width or reaches a minimum.
+ *
+ * The minimum size is defined by {@code R.dimen.button_drop_target_min_text_size} and
+ * it diminishes by intervals defined by
+ * {@code R.dimen.button_drop_target_resize_text_increment}
+ * This functionality is very similar to the option
+ * {@link TextView#setAutoSizeTextTypeWithDefaults(int)} but can't be used in this view because
+ * the layout width is {@code WRAP_CONTENT}.
+ *
+ * @return The biggest text size in SP that makes the text fit or if the text can't fit returns
+ * the min available value
+ */
+ public float resizeTextToFit() {
+ float minSize = Utilities.pxToSp(getResources()
+ .getDimensionPixelSize(R.dimen.button_drop_target_min_text_size));
+ float step = Utilities.pxToSp(getResources()
+ .getDimensionPixelSize(R.dimen.button_drop_target_resize_text_increment));
+ float textSize = Utilities.pxToSp(getTextSize());
+
+ int availableWidth = getMeasuredWidth();
+ while (textSize > minSize) {
+ if (isTextTruncated(availableWidth)) {
+ textSize -= step;
+ setTextSize(textSize);
+ } else {
+ return textSize;
+ }
+ }
+ return minSize;
}
}
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 9f3e1fa..2103593 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -19,6 +19,7 @@
import static android.animation.ValueAnimator.areAnimatorsEnabled;
import static com.android.launcher3.anim.Interpolators.DEACCEL_1_5;
+import static com.android.launcher3.config.FeatureFlags.SHOW_HOME_GARDENING;
import static com.android.launcher3.dragndrop.DraggableView.DRAGGABLE_ICON;
import static com.android.launcher3.icons.IconNormalizer.ICON_VISIBLE_AREA_FACTOR;
@@ -54,13 +55,13 @@
import android.view.accessibility.AccessibilityEvent;
import androidx.annotation.IntDef;
-import androidx.annotation.Nullable;
import androidx.core.graphics.ColorUtils;
import androidx.core.view.ViewCompat;
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.config.FeatureFlags;
import com.android.launcher3.dragndrop.DraggableView;
import com.android.launcher3.folder.PreviewBackground;
@@ -130,7 +131,7 @@
// These arrays are used to implement the drag visualization on x-large screens.
// They are used as circular arrays, indexed by mDragOutlineCurrent.
- @Thunk final CellLayout.LayoutParams[] mDragOutlines = new CellLayout.LayoutParams[4];
+ @Thunk final CellLayoutLayoutParams[] mDragOutlines = new CellLayoutLayoutParams[4];
@Thunk final float[] mDragOutlineAlphas = new float[mDragOutlines.length];
private final InterruptibleInOutAnimator[] mDragOutlineAnims =
new InterruptibleInOutAnimator[mDragOutlines.length];
@@ -139,7 +140,7 @@
private int mDragOutlineCurrent = 0;
private final Paint mDragOutlinePaint = new Paint();
- @Thunk final ArrayMap<LayoutParams, Animator> mReorderAnimators = new ArrayMap<>();
+ @Thunk final ArrayMap<CellLayoutLayoutParams, Animator> mReorderAnimators = new ArrayMap<>();
@Thunk final ArrayMap<Reorderable, ReorderPreviewAnimation> mShakeAnimators = new ArrayMap<>();
private boolean mItemPlacementDirty = false;
@@ -191,7 +192,7 @@
private final Rect mOccupiedRect = new Rect();
private final int[] mDirectionVector = new int[2];
- final int[] mPreviousReorderDirection = new int[2];
+ ItemConfiguration mPreviousSolution = null;
private static final int INVALID_DIRECTION = -100;
private final Rect mTempRect = new Rect();
@@ -245,9 +246,6 @@
mOccupied = new GridOccupancy(mCountX, mCountY);
mTmpOccupied = new GridOccupancy(mCountX, mCountY);
- mPreviousReorderDirection[0] = INVALID_DIRECTION;
- mPreviousReorderDirection[1] = INVALID_DIRECTION;
-
mFolderLeaveBehind.mDelegateCellX = -1;
mFolderLeaveBehind.mDelegateCellY = -1;
@@ -269,7 +267,7 @@
mDragCell[0] = mDragCell[1] = -1;
mDragCellSpan[0] = mDragCellSpan[1] = -1;
for (int i = 0; i < mDragOutlines.length; i++) {
- mDragOutlines[i] = new CellLayout.LayoutParams(0, 0, 0, 0);
+ mDragOutlines[i] = new CellLayoutLayoutParams(0, 0, 0, 0);
}
mDragOutlinePaint.setColor(Themes.getAttrColor(context, R.attr.workspaceTextColor));
@@ -375,7 +373,8 @@
private void resetCellSizeInternal(DeviceProfile deviceProfile) {
switch (mContainerType) {
case FOLDER:
- mBorderSpace = new Point(deviceProfile.folderCellLayoutBorderSpacePx);
+ mBorderSpace = new Point(deviceProfile.folderCellLayoutBorderSpacePx,
+ deviceProfile.folderCellLayoutBorderSpacePx);
break;
case HOTSEAT:
mBorderSpace = new Point(deviceProfile.hotseatBorderSpace,
@@ -404,7 +403,6 @@
mCountY = y;
mOccupied = new GridOccupancy(mCountX, mCountY);
mTmpOccupied = new GridOccupancy(mCountX, mCountY);
- mTempRectStack.clear();
mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mCountX, mCountY,
mBorderSpace);
requestLayout();
@@ -551,7 +549,9 @@
public void setSpringLoadedProgress(float progress) {
if (Float.compare(progress, mSpringLoadedProgress) != 0) {
mSpringLoadedProgress = progress;
- updateBgAlpha();
+ if (!SHOW_HOME_GARDENING.get()) {
+ updateBgAlpha();
+ }
setGridAlpha(progress);
}
}
@@ -576,7 +576,9 @@
public void setScrollProgress(float progress) {
if (Float.compare(Math.abs(progress), mScrollProgress) != 0) {
mScrollProgress = Math.abs(progress);
- updateBgAlpha();
+ if (!SHOW_HOME_GARDENING.get()) {
+ updateBgAlpha();
+ }
}
}
@@ -615,7 +617,7 @@
}
}
- if (mVisualizeDropLocation) {
+ if (mVisualizeDropLocation && !SHOW_HOME_GARDENING.get()) {
for (int i = 0; i < mDragOutlines.length; i++) {
final float alpha = mDragOutlineAlphas[i];
if (alpha <= 0) continue;
@@ -737,9 +739,19 @@
return mContainerType == WORKSPACE;
}
- public boolean addViewToCellLayout(View child, int index, int childId, LayoutParams params,
- boolean markCells) {
- final LayoutParams lp = params;
+ /**
+ * Adds the given view to the CellLayout
+ *
+ * @param child view to add.
+ * @param index index of the CellLayout children where to add the view.
+ * @param childId id of the view.
+ * @param params represent the logic of the view on the CellLayout.
+ * @param markCells if the occupied cells should be marked or not
+ * @return if adding the view was successful
+ */
+ public boolean addViewToCellLayout(View child, int index, int childId,
+ CellLayoutLayoutParams params, boolean markCells) {
+ final CellLayoutLayoutParams lp = params;
// Hotseat icons - remove text
if (child instanceof BubbleTextView) {
@@ -926,7 +938,7 @@
cellToRect(targetCell[0], targetCell[1], spanX, spanY, cellBoundsWithSpacing);
cellBoundsWithSpacing.inset(-mBorderSpace.x / 2, -mBorderSpace.y / 2);
- if (canCreateFolder(getChildAt(targetCell[0], targetCell[1]))) {
+ if (canCreateFolder(getChildAt(targetCell[0], targetCell[1])) && spanX == 1 && spanY == 1) {
// Take only the circle in the smaller dimension, to ensure we don't start reordering
// too soon before accepting a folder drop.
int minRadius = centerPoint[0] - cellBoundsWithSpacing.left;
@@ -936,8 +948,9 @@
return minRadius;
}
// Take up the entire cell, including space between this cell and the adjacent ones.
- return (float) Math.hypot(cellBoundsWithSpacing.width() / 2f,
- cellBoundsWithSpacing.height() / 2f);
+ // Multiply by span to scale radius
+ return (float) Math.hypot(spanX * cellBoundsWithSpacing.width() / 2f,
+ spanY * cellBoundsWithSpacing.height() / 2f);
}
public int getCellWidth() {
@@ -1046,7 +1059,7 @@
ShortcutAndWidgetContainer clc = getShortcutsAndWidgets();
if (clc.indexOfChild(child) != -1 && (child instanceof Reorderable)) {
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ final CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
final ItemInfo info = (ItemInfo) child.getTag();
final Reorderable item = (Reorderable) child;
@@ -1153,7 +1166,7 @@
mDragOutlineAnims[oldIndex].animateOut();
mDragOutlineCurrent = (oldIndex + 1) % mDragOutlines.length;
- LayoutParams cell = mDragOutlines[mDragOutlineCurrent];
+ CellLayoutLayoutParams cell = mDragOutlines[mDragOutlineCurrent];
cell.cellX = cellX;
cell.cellY = cellY;
cell.cellHSpan = spanX;
@@ -1234,21 +1247,6 @@
result, resultSpan);
}
- private final Stack<Rect> mTempRectStack = new Stack<>();
- private void lazyInitTempRectStack() {
- if (mTempRectStack.isEmpty()) {
- for (int i = 0; i < mCountX * mCountY; i++) {
- mTempRectStack.push(new Rect());
- }
- }
- }
-
- private void recycleTempRects(Stack<Rect> used) {
- while (!used.isEmpty()) {
- mTempRectStack.push(used.pop());
- }
- }
-
/**
* Find a vacant area that will fit the given bounds nearest the requested
* cell location. Uses Euclidean distance to score multiple vacant areas.
@@ -1268,8 +1266,6 @@
*/
private int[] findNearestArea(int relativeXPos, int relativeYPos, int minSpanX, int minSpanY,
int spanX, int spanY, boolean ignoreOccupied, int[] result, int[] resultSpan) {
- lazyInitTempRectStack();
-
// For items with a spanX / spanY > 1, the passed in point (relativeXPos, relativeYPos)
// corresponds to the center of the item, but we are searching based on the top-left cell,
// so we translate the point over to correspond to the top-left.
@@ -1339,9 +1335,6 @@
hitMaxY |= ySize >= spanY;
incX = !incX;
}
- incX = true;
- hitMaxX = xSize >= spanX;
- hitMaxY = ySize >= spanY;
}
final int[] cellXY = mTmpPoint;
cellToCenterPoint(x, y, cellXY);
@@ -1349,8 +1342,7 @@
// We verify that the current rect is not a sub-rect of any of our previous
// candidates. In this case, the current rect is disqualified in favour of the
// containing rect.
- Rect currentRect = mTempRectStack.pop();
- currentRect.set(x, y, x + xSize, y + ySize);
+ Rect currentRect = new Rect(x, y, x + xSize, y + ySize);
boolean contained = false;
for (Rect r : validRegions) {
if (r.contains(currentRect)) {
@@ -1380,653 +1372,9 @@
bestXY[0] = -1;
bestXY[1] = -1;
}
- recycleTempRects(validRegions);
return bestXY;
}
- /**
- * Find a vacant area that will fit the given bounds nearest the requested
- * cell location, and will also weigh in a suggested direction vector of the
- * desired location. This method computers distance based on unit grid distances,
- * not pixel distances.
- *
- * @param cellX The X cell nearest to which you want to search for a vacant area.
- * @param cellY The Y cell nearest which you want to search for a vacant area.
- * @param spanX Horizontal span of the object.
- * @param spanY Vertical span of the object.
- * @param direction The favored direction in which the views should move from x, y
- * @param occupied The array which represents which cells in the CellLayout are occupied
- * @param blockOccupied The array which represents which cells in the specified block (cellX,
- * cellY, spanX, spanY) are occupied. This is used when try to move a group of views.
- * @param result Array in which to place the result, or null (in which case a new array will
- * be allocated)
- * @return The X, Y cell of a vacant area that can contain this object,
- * nearest the requested location.
- */
- private int[] findNearestArea(int cellX, int cellY, int spanX, int spanY, int[] direction,
- boolean[][] occupied, boolean blockOccupied[][], int[] result) {
- // Keep track of best-scoring drop area
- final int[] bestXY = result != null ? result : new int[2];
- float bestDistance = Float.MAX_VALUE;
- int bestDirectionScore = Integer.MIN_VALUE;
-
- final int countX = mCountX;
- final int countY = mCountY;
-
- for (int y = 0; y < countY - (spanY - 1); y++) {
- inner:
- for (int x = 0; x < countX - (spanX - 1); x++) {
- // First, let's see if this thing fits anywhere
- for (int i = 0; i < spanX; i++) {
- for (int j = 0; j < spanY; j++) {
- if (occupied[x + i][y + j] && (blockOccupied == null || blockOccupied[i][j])) {
- continue inner;
- }
- }
- }
-
- float distance = (float) Math.hypot(x - cellX, y - cellY);
- int[] curDirection = mTmpPoint;
- computeDirectionVector(x - cellX, y - cellY, curDirection);
- // The direction score is just the dot product of the two candidate direction
- // and that passed in.
- int curDirectionScore = direction[0] * curDirection[0] +
- direction[1] * curDirection[1];
- if (Float.compare(distance, bestDistance) < 0 ||
- (Float.compare(distance, bestDistance) == 0
- && curDirectionScore > bestDirectionScore)) {
- bestDistance = distance;
- bestDirectionScore = curDirectionScore;
- bestXY[0] = x;
- bestXY[1] = y;
- }
- }
- }
-
- // Return -1, -1 if no suitable location found
- if (bestDistance == Float.MAX_VALUE) {
- bestXY[0] = -1;
- bestXY[1] = -1;
- }
- return bestXY;
- }
-
- private boolean addViewToTempLocation(View v, Rect rectOccupiedByPotentialDrop,
- int[] direction, ItemConfiguration currentState) {
- CellAndSpan c = currentState.map.get(v);
- boolean success = false;
- mTmpOccupied.markCells(c, false);
- mTmpOccupied.markCells(rectOccupiedByPotentialDrop, true);
-
- findNearestArea(c.cellX, c.cellY, c.spanX, c.spanY, direction,
- mTmpOccupied.cells, null, mTempLocation);
-
- if (mTempLocation[0] >= 0 && mTempLocation[1] >= 0) {
- c.cellX = mTempLocation[0];
- c.cellY = mTempLocation[1];
- success = true;
- }
- mTmpOccupied.markCells(c, true);
- return success;
- }
-
- /**
- * This helper class defines a cluster of views. It helps with defining complex edges
- * of the cluster and determining how those edges interact with other views. The edges
- * essentially define a fine-grained boundary around the cluster of views -- like a more
- * precise version of a bounding box.
- */
- private class ViewCluster {
- final static int LEFT = 1 << 0;
- final static int TOP = 1 << 1;
- final static int RIGHT = 1 << 2;
- final static int BOTTOM = 1 << 3;
-
- final ArrayList<View> views;
- final ItemConfiguration config;
- final Rect boundingRect = new Rect();
-
- final int[] leftEdge = new int[mCountY];
- final int[] rightEdge = new int[mCountY];
- final int[] topEdge = new int[mCountX];
- final int[] bottomEdge = new int[mCountX];
- int dirtyEdges;
- boolean boundingRectDirty;
-
- @SuppressWarnings("unchecked")
- public ViewCluster(ArrayList<View> views, ItemConfiguration config) {
- this.views = (ArrayList<View>) views.clone();
- this.config = config;
- resetEdges();
- }
-
- void resetEdges() {
- for (int i = 0; i < mCountX; i++) {
- topEdge[i] = -1;
- bottomEdge[i] = -1;
- }
- for (int i = 0; i < mCountY; i++) {
- leftEdge[i] = -1;
- rightEdge[i] = -1;
- }
- dirtyEdges = LEFT | TOP | RIGHT | BOTTOM;
- boundingRectDirty = true;
- }
-
- void computeEdge(int which) {
- int count = views.size();
- for (int i = 0; i < count; i++) {
- CellAndSpan cs = config.map.get(views.get(i));
- switch (which) {
- case LEFT:
- int left = cs.cellX;
- for (int j = cs.cellY; j < cs.cellY + cs.spanY; j++) {
- if (left < leftEdge[j] || leftEdge[j] < 0) {
- leftEdge[j] = left;
- }
- }
- break;
- case RIGHT:
- int right = cs.cellX + cs.spanX;
- for (int j = cs.cellY; j < cs.cellY + cs.spanY; j++) {
- if (right > rightEdge[j]) {
- rightEdge[j] = right;
- }
- }
- break;
- case TOP:
- int top = cs.cellY;
- for (int j = cs.cellX; j < cs.cellX + cs.spanX; j++) {
- if (top < topEdge[j] || topEdge[j] < 0) {
- topEdge[j] = top;
- }
- }
- break;
- case BOTTOM:
- int bottom = cs.cellY + cs.spanY;
- for (int j = cs.cellX; j < cs.cellX + cs.spanX; j++) {
- if (bottom > bottomEdge[j]) {
- bottomEdge[j] = bottom;
- }
- }
- break;
- }
- }
- }
-
- boolean isViewTouchingEdge(View v, int whichEdge) {
- CellAndSpan cs = config.map.get(v);
-
- if ((dirtyEdges & whichEdge) == whichEdge) {
- computeEdge(whichEdge);
- dirtyEdges &= ~whichEdge;
- }
-
- switch (whichEdge) {
- case LEFT:
- for (int i = cs.cellY; i < cs.cellY + cs.spanY; i++) {
- if (leftEdge[i] == cs.cellX + cs.spanX) {
- return true;
- }
- }
- break;
- case RIGHT:
- for (int i = cs.cellY; i < cs.cellY + cs.spanY; i++) {
- if (rightEdge[i] == cs.cellX) {
- return true;
- }
- }
- break;
- case TOP:
- for (int i = cs.cellX; i < cs.cellX + cs.spanX; i++) {
- if (topEdge[i] == cs.cellY + cs.spanY) {
- return true;
- }
- }
- break;
- case BOTTOM:
- for (int i = cs.cellX; i < cs.cellX + cs.spanX; i++) {
- if (bottomEdge[i] == cs.cellY) {
- return true;
- }
- }
- break;
- }
- return false;
- }
-
- void shift(int whichEdge, int delta) {
- for (View v: views) {
- CellAndSpan c = config.map.get(v);
- switch (whichEdge) {
- case LEFT:
- c.cellX -= delta;
- break;
- case RIGHT:
- c.cellX += delta;
- break;
- case TOP:
- c.cellY -= delta;
- break;
- case BOTTOM:
- default:
- c.cellY += delta;
- break;
- }
- }
- resetEdges();
- }
-
- public void addView(View v) {
- views.add(v);
- resetEdges();
- }
-
- public Rect getBoundingRect() {
- if (boundingRectDirty) {
- config.getBoundingRectForViews(views, boundingRect);
- }
- return boundingRect;
- }
-
- final PositionComparator comparator = new PositionComparator();
- class PositionComparator implements Comparator<View> {
- int whichEdge = 0;
- public int compare(View left, View right) {
- CellAndSpan l = config.map.get(left);
- CellAndSpan r = config.map.get(right);
- switch (whichEdge) {
- case LEFT:
- return (r.cellX + r.spanX) - (l.cellX + l.spanX);
- case RIGHT:
- return l.cellX - r.cellX;
- case TOP:
- return (r.cellY + r.spanY) - (l.cellY + l.spanY);
- case BOTTOM:
- default:
- return l.cellY - r.cellY;
- }
- }
- }
-
- public void sortConfigurationForEdgePush(int edge) {
- comparator.whichEdge = edge;
- Collections.sort(config.sortedViews, comparator);
- }
- }
-
- private boolean pushViewsToTempLocation(ArrayList<View> views, Rect rectOccupiedByPotentialDrop,
- int[] direction, View dragView, ItemConfiguration currentState) {
-
- ViewCluster cluster = new ViewCluster(views, currentState);
- Rect clusterRect = cluster.getBoundingRect();
- int whichEdge;
- int pushDistance;
- boolean fail = false;
-
- // Determine the edge of the cluster that will be leading the push and how far
- // the cluster must be shifted.
- if (direction[0] < 0) {
- whichEdge = ViewCluster.LEFT;
- pushDistance = clusterRect.right - rectOccupiedByPotentialDrop.left;
- } else if (direction[0] > 0) {
- whichEdge = ViewCluster.RIGHT;
- pushDistance = rectOccupiedByPotentialDrop.right - clusterRect.left;
- } else if (direction[1] < 0) {
- whichEdge = ViewCluster.TOP;
- pushDistance = clusterRect.bottom - rectOccupiedByPotentialDrop.top;
- } else {
- whichEdge = ViewCluster.BOTTOM;
- pushDistance = rectOccupiedByPotentialDrop.bottom - clusterRect.top;
- }
-
- // Break early for invalid push distance.
- if (pushDistance <= 0) {
- return false;
- }
-
- // Mark the occupied state as false for the group of views we want to move.
- for (View v: views) {
- CellAndSpan c = currentState.map.get(v);
- mTmpOccupied.markCells(c, false);
- }
-
- // We save the current configuration -- if we fail to find a solution we will revert
- // to the initial state. The process of finding a solution modifies the configuration
- // in place, hence the need for revert in the failure case.
- currentState.save();
-
- // The pushing algorithm is simplified by considering the views in the order in which
- // they would be pushed by the cluster. For example, if the cluster is leading with its
- // left edge, we consider sort the views by their right edge, from right to left.
- cluster.sortConfigurationForEdgePush(whichEdge);
-
- while (pushDistance > 0 && !fail) {
- for (View v: currentState.sortedViews) {
- // For each view that isn't in the cluster, we see if the leading edge of the
- // cluster is contacting the edge of that view. If so, we add that view to the
- // cluster.
- if (!cluster.views.contains(v) && v != dragView) {
- if (cluster.isViewTouchingEdge(v, whichEdge)) {
- LayoutParams lp = (LayoutParams) v.getLayoutParams();
- if (!lp.canReorder) {
- // The push solution includes the all apps button, this is not viable.
- fail = true;
- break;
- }
- cluster.addView(v);
- CellAndSpan c = currentState.map.get(v);
-
- // Adding view to cluster, mark it as not occupied.
- mTmpOccupied.markCells(c, false);
- }
- }
- }
- pushDistance--;
-
- // The cluster has been completed, now we move the whole thing over in the appropriate
- // direction.
- cluster.shift(whichEdge, 1);
- }
-
- boolean foundSolution = false;
- clusterRect = cluster.getBoundingRect();
-
- // Due to the nature of the algorithm, the only check required to verify a valid solution
- // is to ensure that completed shifted cluster lies completely within the cell layout.
- if (!fail && clusterRect.left >= 0 && clusterRect.right <= mCountX && clusterRect.top >= 0 &&
- clusterRect.bottom <= mCountY) {
- foundSolution = true;
- } else {
- currentState.restore();
- }
-
- // In either case, we set the occupied array as marked for the location of the views
- for (View v: cluster.views) {
- CellAndSpan c = currentState.map.get(v);
- mTmpOccupied.markCells(c, true);
- }
-
- return foundSolution;
- }
-
- private boolean addViewsToTempLocation(ArrayList<View> views, Rect rectOccupiedByPotentialDrop,
- int[] direction, View dragView, ItemConfiguration currentState) {
- if (views.size() == 0) return true;
-
- boolean success = false;
- Rect boundingRect = new Rect();
- // We construct a rect which represents the entire group of views passed in
- currentState.getBoundingRectForViews(views, boundingRect);
-
- // Mark the occupied state as false for the group of views we want to move.
- for (View v: views) {
- CellAndSpan c = currentState.map.get(v);
- mTmpOccupied.markCells(c, false);
- }
-
- GridOccupancy blockOccupied = new GridOccupancy(boundingRect.width(), boundingRect.height());
- int top = boundingRect.top;
- int left = boundingRect.left;
- // We mark more precisely which parts of the bounding rect are truly occupied, allowing
- // for interlocking.
- for (View v: views) {
- CellAndSpan c = currentState.map.get(v);
- blockOccupied.markCells(c.cellX - left, c.cellY - top, c.spanX, c.spanY, true);
- }
-
- mTmpOccupied.markCells(rectOccupiedByPotentialDrop, true);
-
- findNearestArea(boundingRect.left, boundingRect.top, boundingRect.width(),
- boundingRect.height(), direction,
- mTmpOccupied.cells, blockOccupied.cells, mTempLocation);
-
- // If we successfuly found a location by pushing the block of views, we commit it
- if (mTempLocation[0] >= 0 && mTempLocation[1] >= 0) {
- int deltaX = mTempLocation[0] - boundingRect.left;
- int deltaY = mTempLocation[1] - boundingRect.top;
- for (View v: views) {
- CellAndSpan c = currentState.map.get(v);
- c.cellX += deltaX;
- c.cellY += deltaY;
- }
- success = true;
- }
-
- // In either case, we set the occupied array as marked for the location of the views
- for (View v: views) {
- CellAndSpan c = currentState.map.get(v);
- mTmpOccupied.markCells(c, true);
- }
- return success;
- }
-
- // This method tries to find a reordering solution which satisfies the push mechanic by trying
- // to push items in each of the cardinal directions, in an order based on the direction vector
- // passed.
- private boolean attemptPushInDirection(ArrayList<View> intersectingViews, Rect occupied,
- int[] direction, View ignoreView, ItemConfiguration solution) {
- if ((Math.abs(direction[0]) + Math.abs(direction[1])) > 1) {
- // If the direction vector has two non-zero components, we try pushing
- // separately in each of the components.
- int temp = direction[1];
- direction[1] = 0;
-
- if (pushViewsToTempLocation(intersectingViews, occupied, direction,
- ignoreView, solution)) {
- return true;
- }
- direction[1] = temp;
- temp = direction[0];
- direction[0] = 0;
-
- if (pushViewsToTempLocation(intersectingViews, occupied, direction,
- ignoreView, solution)) {
- return true;
- }
- // Revert the direction
- direction[0] = temp;
-
- // Now we try pushing in each component of the opposite direction
- direction[0] *= -1;
- direction[1] *= -1;
- temp = direction[1];
- direction[1] = 0;
- if (pushViewsToTempLocation(intersectingViews, occupied, direction,
- ignoreView, solution)) {
- return true;
- }
-
- direction[1] = temp;
- temp = direction[0];
- direction[0] = 0;
- if (pushViewsToTempLocation(intersectingViews, occupied, direction,
- ignoreView, solution)) {
- return true;
- }
- // revert the direction
- direction[0] = temp;
- direction[0] *= -1;
- direction[1] *= -1;
-
- } else {
- // If the direction vector has a single non-zero component, we push first in the
- // direction of the vector
- if (pushViewsToTempLocation(intersectingViews, occupied, direction,
- ignoreView, solution)) {
- return true;
- }
- // Then we try the opposite direction
- direction[0] *= -1;
- direction[1] *= -1;
- if (pushViewsToTempLocation(intersectingViews, occupied, direction,
- ignoreView, solution)) {
- return true;
- }
- // Switch the direction back
- direction[0] *= -1;
- direction[1] *= -1;
-
- // If we have failed to find a push solution with the above, then we try
- // to find a solution by pushing along the perpendicular axis.
-
- // Swap the components
- int temp = direction[1];
- direction[1] = direction[0];
- direction[0] = temp;
- if (pushViewsToTempLocation(intersectingViews, occupied, direction,
- ignoreView, solution)) {
- return true;
- }
-
- // Then we try the opposite direction
- direction[0] *= -1;
- direction[1] *= -1;
- if (pushViewsToTempLocation(intersectingViews, occupied, direction,
- ignoreView, solution)) {
- return true;
- }
- // Switch the direction back
- direction[0] *= -1;
- direction[1] *= -1;
-
- // Swap the components back
- temp = direction[1];
- direction[1] = direction[0];
- direction[0] = temp;
- }
- return false;
- }
-
- private boolean rearrangementExists(int cellX, int cellY, int spanX, int spanY, int[] direction,
- View ignoreView, ItemConfiguration solution) {
- // Return early if get invalid cell positions
- if (cellX < 0 || cellY < 0) return false;
-
- mIntersectingViews.clear();
- mOccupiedRect.set(cellX, cellY, cellX + spanX, cellY + spanY);
-
- // Mark the desired location of the view currently being dragged.
- if (ignoreView != null) {
- CellAndSpan c = solution.map.get(ignoreView);
- if (c != null) {
- c.cellX = cellX;
- c.cellY = cellY;
- }
- }
- Rect r0 = new Rect(cellX, cellY, cellX + spanX, cellY + spanY);
- Rect r1 = new Rect();
- for (View child: solution.map.keySet()) {
- if (child == ignoreView) continue;
- CellAndSpan c = solution.map.get(child);
- LayoutParams lp = (LayoutParams) child.getLayoutParams();
- r1.set(c.cellX, c.cellY, c.cellX + c.spanX, c.cellY + c.spanY);
- if (Rect.intersects(r0, r1)) {
- if (!lp.canReorder) {
- return false;
- }
- mIntersectingViews.add(child);
- }
- }
-
- solution.intersectingViews = new ArrayList<>(mIntersectingViews);
-
- // First we try to find a solution which respects the push mechanic. That is,
- // we try to find a solution such that no displaced item travels through another item
- // without also displacing that item.
- if (attemptPushInDirection(mIntersectingViews, mOccupiedRect, direction, ignoreView,
- solution)) {
- return true;
- }
-
- // Next we try moving the views as a block, but without requiring the push mechanic.
- if (addViewsToTempLocation(mIntersectingViews, mOccupiedRect, direction, ignoreView,
- solution)) {
- return true;
- }
-
- // Ok, they couldn't move as a block, let's move them individually
- for (View v : mIntersectingViews) {
- if (!addViewToTempLocation(v, mOccupiedRect, direction, solution)) {
- return false;
- }
- }
- return true;
- }
-
- /*
- * Returns a pair (x, y), where x,y are in {-1, 0, 1} corresponding to vector between
- * the provided point and the provided cell
- */
- private void computeDirectionVector(float deltaX, float deltaY, int[] result) {
- double angle = Math.atan(deltaY / deltaX);
-
- result[0] = 0;
- result[1] = 0;
- if (Math.abs(Math.cos(angle)) > 0.5f) {
- result[0] = (int) Math.signum(deltaX);
- }
- if (Math.abs(Math.sin(angle)) > 0.5f) {
- result[1] = (int) Math.signum(deltaY);
- }
- }
-
- private ItemConfiguration findReorderSolution(int pixelX, int pixelY, int minSpanX, int minSpanY,
- int spanX, int spanY, int[] direction, View dragView, boolean decX,
- ItemConfiguration solution) {
- // Copy the current state into the solution. This solution will be manipulated as necessary.
- copyCurrentStateToSolution(solution, false);
- // Copy the current occupied array into the temporary occupied array. This array will be
- // manipulated as necessary to find a solution.
- mOccupied.copyTo(mTmpOccupied);
-
- // We find the nearest cell into which we would place the dragged item, assuming there's
- // nothing in its way.
- int result[] = new int[2];
- result = findNearestArea(pixelX, pixelY, spanX, spanY, result);
-
- boolean success;
- // First we try the exact nearest position of the item being dragged,
- // we will then want to try to move this around to other neighbouring positions
- success = rearrangementExists(result[0], result[1], spanX, spanY, direction, dragView,
- solution);
-
- if (!success) {
- // We try shrinking the widget down to size in an alternating pattern, shrink 1 in
- // x, then 1 in y etc.
- if (spanX > minSpanX && (minSpanY == spanY || decX)) {
- return findReorderSolution(pixelX, pixelY, minSpanX, minSpanY, spanX - 1, spanY,
- direction, dragView, false, solution);
- } else if (spanY > minSpanY) {
- return findReorderSolution(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY - 1,
- direction, dragView, true, solution);
- }
- solution.isSolution = false;
- } else {
- solution.isSolution = true;
- solution.cellX = result[0];
- solution.cellY = result[1];
- solution.spanX = spanX;
- solution.spanY = spanY;
- }
- return solution;
- }
-
- private void copyCurrentStateToSolution(ItemConfiguration solution, boolean temp) {
- int childCount = mShortcutsAndWidgets.getChildCount();
- for (int i = 0; i < childCount; i++) {
- View child = mShortcutsAndWidgets.getChildAt(i);
- LayoutParams lp = (LayoutParams) child.getLayoutParams();
- CellAndSpan c;
- if (temp) {
- c = new CellAndSpan(lp.tmpCellX, lp.tmpCellY, lp.cellHSpan, lp.cellVSpan);
- } else {
- c = new CellAndSpan(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan);
- }
- solution.add(child, c);
- }
- }
-
private void copySolutionToTempState(ItemConfiguration solution, View dragView) {
mTmpOccupied.clear();
@@ -2034,7 +1382,7 @@
for (int i = 0; i < childCount; i++) {
View child = mShortcutsAndWidgets.getChildAt(i);
if (child == dragView) continue;
- LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
CellAndSpan c = solution.map.get(child);
if (c != null) {
lp.tmpCellX = c.cellX;
@@ -2082,7 +1430,7 @@
!= null && !solution.intersectingViews.contains(child);
- LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
if (c != null && !skip && (child instanceof Reorderable)) {
ReorderPreviewAnimation rha = new ReorderPreviewAnimation((Reorderable) child,
mode, lp.cellX, lp.cellY, c.cellX, c.cellY, c.spanX, c.spanY);
@@ -2273,7 +1621,7 @@
int childCount = mShortcutsAndWidgets.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = mShortcutsAndWidgets.getChildAt(i);
- LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
ItemInfo info = (ItemInfo) child.getTag();
// We do a null check here because the item info can be null in the case of the
// AllApps button in the hotseat.
@@ -2298,7 +1646,8 @@
private void setUseTempCoords(boolean useTempCoords) {
int childCount = mShortcutsAndWidgets.getChildCount();
for (int i = 0; i < childCount; i++) {
- LayoutParams lp = (LayoutParams) mShortcutsAndWidgets.getChildAt(i).getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) mShortcutsAndWidgets.getChildAt(
+ i).getLayoutParams();
lp.useTmpCoords = useTempCoords;
}
}
@@ -2322,6 +1671,569 @@
return solution;
}
+ // For a given cell and span, fetch the set of views intersecting the region.
+ private void getViewsIntersectingRegion(int cellX, int cellY, int spanX, int spanY,
+ View dragView, Rect boundingRect, ArrayList<View> intersectingViews) {
+ if (boundingRect != null) {
+ boundingRect.set(cellX, cellY, cellX + spanX, cellY + spanY);
+ }
+ intersectingViews.clear();
+ Rect r0 = new Rect(cellX, cellY, cellX + spanX, cellY + spanY);
+ Rect r1 = new Rect();
+ final int count = mShortcutsAndWidgets.getChildCount();
+ for (int i = 0; i < count; i++) {
+ View child = mShortcutsAndWidgets.getChildAt(i);
+ if (child == dragView) continue;
+ CellLayoutLayoutParams
+ lp = (CellLayoutLayoutParams) child.getLayoutParams();
+ r1.set(lp.cellX, lp.cellY, lp.cellX + lp.cellHSpan, lp.cellY + lp.cellVSpan);
+ if (Rect.intersects(r0, r1)) {
+ mIntersectingViews.add(child);
+ if (boundingRect != null) {
+ boundingRect.union(r1);
+ }
+ }
+ }
+ }
+
+ boolean isNearestDropLocationOccupied(int pixelX, int pixelY, int spanX, int spanY,
+ View dragView, int[] result) {
+ result = findNearestArea(pixelX, pixelY, spanX, spanY, result);
+ getViewsIntersectingRegion(result[0], result[1], spanX, spanY, dragView, null,
+ mIntersectingViews);
+ return !mIntersectingViews.isEmpty();
+ }
+
+ void revertTempState() {
+ completeAndClearReorderPreviewAnimations();
+ if (isItemPlacementDirty() && !DESTRUCTIVE_REORDER) {
+ final int count = mShortcutsAndWidgets.getChildCount();
+ for (int i = 0; i < count; i++) {
+ View child = mShortcutsAndWidgets.getChildAt(i);
+ CellLayoutLayoutParams
+ lp = (CellLayoutLayoutParams) child.getLayoutParams();
+ if (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.cellY) {
+ lp.tmpCellX = lp.cellX;
+ lp.tmpCellY = lp.cellY;
+ animateChildToPosition(child, lp.cellX, lp.cellY, REORDER_ANIMATION_DURATION,
+ 0, false, false);
+ }
+ }
+ setItemPlacementDirty(false);
+ }
+ }
+
+ boolean createAreaForResize(int cellX, int cellY, int spanX, int spanY,
+ View dragView, int[] direction, boolean commit) {
+ int[] pixelXY = new int[2];
+ regionToCenterPoint(cellX, cellY, spanX, spanY, pixelXY);
+
+ // First we determine if things have moved enough to cause a different layout
+ ItemConfiguration swapSolution = findReorderSolution(pixelXY[0], pixelXY[1], spanX, spanY,
+ spanX, spanY, direction, dragView, true, new ItemConfiguration());
+
+ setUseTempCoords(true);
+ if (swapSolution != null && swapSolution.isSolution) {
+ // If we're just testing for a possible location (MODE_ACCEPT_DROP), we don't bother
+ // committing anything or animating anything as we just want to determine if a solution
+ // exists
+ copySolutionToTempState(swapSolution, dragView);
+ setItemPlacementDirty(true);
+ animateItemsToSolution(swapSolution, dragView, commit);
+
+ if (commit) {
+ commitTempPlacement(null);
+ completeAndClearReorderPreviewAnimations();
+ setItemPlacementDirty(false);
+ } else {
+ beginOrAdjustReorderPreviewAnimations(swapSolution, dragView,
+ ReorderPreviewAnimation.MODE_PREVIEW);
+ }
+ mShortcutsAndWidgets.requestLayout();
+ }
+ return swapSolution.isSolution;
+ }
+
+ /**
+ * Find a vacant area that will fit the given bounds nearest the requested
+ * cell location, and will also weigh in a suggested direction vector of the
+ * desired location. This method computers distance based on unit grid distances,
+ * not pixel distances.
+ *
+ * @param cellX The X cell nearest to which you want to search for a vacant area.
+ * @param cellY The Y cell nearest which you want to search for a vacant area.
+ * @param spanX Horizontal span of the object.
+ * @param spanY Vertical span of the object.
+ * @param direction The favored direction in which the views should move from x, y
+ * @param occupied The array which represents which cells in the CellLayout are occupied
+ * @param blockOccupied The array which represents which cells in the specified block (cellX,
+ * cellY, spanX, spanY) are occupied. This is used when try to move a group of views.
+ * @param result Array in which to place the result, or null (in which case a new array will
+ * be allocated)
+ * @return The X, Y cell of a vacant area that can contain this object,
+ * nearest the requested location.
+ */
+ private int[] findNearestArea(int cellX, int cellY, int spanX, int spanY, int[] direction,
+ boolean[][] occupied, boolean blockOccupied[][], int[] result) {
+ // Keep track of best-scoring drop area
+ final int[] bestXY = result != null ? result : new int[2];
+ float bestDistance = Float.MAX_VALUE;
+ int bestDirectionScore = Integer.MIN_VALUE;
+
+ final int countX = mCountX;
+ final int countY = mCountY;
+
+ for (int y = 0; y < countY - (spanY - 1); y++) {
+ inner:
+ for (int x = 0; x < countX - (spanX - 1); x++) {
+ // First, let's see if this thing fits anywhere
+ for (int i = 0; i < spanX; i++) {
+ for (int j = 0; j < spanY; j++) {
+ if (occupied[x + i][y + j] && (blockOccupied == null || blockOccupied[i][j])) {
+ continue inner;
+ }
+ }
+ }
+
+ float distance = (float) Math.hypot(x - cellX, y - cellY);
+ int[] curDirection = mTmpPoint;
+ computeDirectionVector(x - cellX, y - cellY, curDirection);
+ // The direction score is just the dot product of the two candidate direction
+ // and that passed in.
+ int curDirectionScore = direction[0] * curDirection[0] +
+ direction[1] * curDirection[1];
+ if (Float.compare(distance, bestDistance) < 0 ||
+ (Float.compare(distance, bestDistance) == 0
+ && curDirectionScore > bestDirectionScore)) {
+ bestDistance = distance;
+ bestDirectionScore = curDirectionScore;
+ bestXY[0] = x;
+ bestXY[1] = y;
+ }
+ }
+ }
+
+ // Return -1, -1 if no suitable location found
+ if (bestDistance == Float.MAX_VALUE) {
+ bestXY[0] = -1;
+ bestXY[1] = -1;
+ }
+ return bestXY;
+ }
+
+ private boolean addViewToTempLocation(View v, Rect rectOccupiedByPotentialDrop,
+ int[] direction, ItemConfiguration currentState) {
+ CellAndSpan c = currentState.map.get(v);
+ boolean success = false;
+ mTmpOccupied.markCells(c, false);
+ mTmpOccupied.markCells(rectOccupiedByPotentialDrop, true);
+
+ findNearestArea(c.cellX, c.cellY, c.spanX, c.spanY, direction,
+ mTmpOccupied.cells, null, mTempLocation);
+
+ if (mTempLocation[0] >= 0 && mTempLocation[1] >= 0) {
+ c.cellX = mTempLocation[0];
+ c.cellY = mTempLocation[1];
+ success = true;
+ }
+ mTmpOccupied.markCells(c, true);
+ return success;
+ }
+
+ private boolean pushViewsToTempLocation(ArrayList<View> views, Rect rectOccupiedByPotentialDrop,
+ int[] direction, View dragView, ItemConfiguration currentState) {
+
+ ViewCluster cluster = new ViewCluster(views, currentState);
+ Rect clusterRect = cluster.getBoundingRect();
+ int whichEdge;
+ int pushDistance;
+ boolean fail = false;
+
+ // Determine the edge of the cluster that will be leading the push and how far
+ // the cluster must be shifted.
+ if (direction[0] < 0) {
+ whichEdge = ViewCluster.LEFT;
+ pushDistance = clusterRect.right - rectOccupiedByPotentialDrop.left;
+ } else if (direction[0] > 0) {
+ whichEdge = ViewCluster.RIGHT;
+ pushDistance = rectOccupiedByPotentialDrop.right - clusterRect.left;
+ } else if (direction[1] < 0) {
+ whichEdge = ViewCluster.TOP;
+ pushDistance = clusterRect.bottom - rectOccupiedByPotentialDrop.top;
+ } else {
+ whichEdge = ViewCluster.BOTTOM;
+ pushDistance = rectOccupiedByPotentialDrop.bottom - clusterRect.top;
+ }
+
+ // Break early for invalid push distance.
+ if (pushDistance <= 0) {
+ return false;
+ }
+
+ // Mark the occupied state as false for the group of views we want to move.
+ for (View v: views) {
+ CellAndSpan c = currentState.map.get(v);
+ mTmpOccupied.markCells(c, false);
+ }
+
+ // We save the current configuration -- if we fail to find a solution we will revert
+ // to the initial state. The process of finding a solution modifies the configuration
+ // in place, hence the need for revert in the failure case.
+ currentState.save();
+
+ // The pushing algorithm is simplified by considering the views in the order in which
+ // they would be pushed by the cluster. For example, if the cluster is leading with its
+ // left edge, we consider sort the views by their right edge, from right to left.
+ cluster.sortConfigurationForEdgePush(whichEdge);
+
+ while (pushDistance > 0 && !fail) {
+ for (View v: currentState.sortedViews) {
+ // For each view that isn't in the cluster, we see if the leading edge of the
+ // cluster is contacting the edge of that view. If so, we add that view to the
+ // cluster.
+ if (!cluster.views.contains(v) && v != dragView) {
+ if (cluster.isViewTouchingEdge(v, whichEdge)) {
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) v.getLayoutParams();
+ if (!lp.canReorder) {
+ // The push solution includes the all apps button, this is not viable.
+ fail = true;
+ break;
+ }
+ cluster.addView(v);
+ CellAndSpan c = currentState.map.get(v);
+
+ // Adding view to cluster, mark it as not occupied.
+ mTmpOccupied.markCells(c, false);
+ }
+ }
+ }
+ pushDistance--;
+
+ // The cluster has been completed, now we move the whole thing over in the appropriate
+ // direction.
+ cluster.shift(whichEdge, 1);
+ }
+
+ boolean foundSolution = false;
+ clusterRect = cluster.getBoundingRect();
+
+ // Due to the nature of the algorithm, the only check required to verify a valid solution
+ // is to ensure that completed shifted cluster lies completely within the cell layout.
+ if (!fail && clusterRect.left >= 0 && clusterRect.right <= mCountX && clusterRect.top >= 0 &&
+ clusterRect.bottom <= mCountY) {
+ foundSolution = true;
+ } else {
+ currentState.restore();
+ }
+
+ // In either case, we set the occupied array as marked for the location of the views
+ for (View v: cluster.views) {
+ CellAndSpan c = currentState.map.get(v);
+ mTmpOccupied.markCells(c, true);
+ }
+
+ return foundSolution;
+ }
+
+ /**
+ * This helper class defines a cluster of views. It helps with defining complex edges
+ * of the cluster and determining how those edges interact with other views. The edges
+ * essentially define a fine-grained boundary around the cluster of views -- like a more
+ * precise version of a bounding box.
+ */
+ private class ViewCluster {
+ final static int LEFT = 1 << 0;
+ final static int TOP = 1 << 1;
+ final static int RIGHT = 1 << 2;
+ final static int BOTTOM = 1 << 3;
+
+ final ArrayList<View> views;
+ final ItemConfiguration config;
+ final Rect boundingRect = new Rect();
+
+ final int[] leftEdge = new int[mCountY];
+ final int[] rightEdge = new int[mCountY];
+ final int[] topEdge = new int[mCountX];
+ final int[] bottomEdge = new int[mCountX];
+ int dirtyEdges;
+ boolean boundingRectDirty;
+
+ @SuppressWarnings("unchecked")
+ public ViewCluster(ArrayList<View> views, ItemConfiguration config) {
+ this.views = (ArrayList<View>) views.clone();
+ this.config = config;
+ resetEdges();
+ }
+
+ void resetEdges() {
+ for (int i = 0; i < mCountX; i++) {
+ topEdge[i] = -1;
+ bottomEdge[i] = -1;
+ }
+ for (int i = 0; i < mCountY; i++) {
+ leftEdge[i] = -1;
+ rightEdge[i] = -1;
+ }
+ dirtyEdges = LEFT | TOP | RIGHT | BOTTOM;
+ boundingRectDirty = true;
+ }
+
+ void computeEdge(int which) {
+ int count = views.size();
+ for (int i = 0; i < count; i++) {
+ CellAndSpan cs = config.map.get(views.get(i));
+ switch (which) {
+ case LEFT:
+ int left = cs.cellX;
+ for (int j = cs.cellY; j < cs.cellY + cs.spanY; j++) {
+ if (left < leftEdge[j] || leftEdge[j] < 0) {
+ leftEdge[j] = left;
+ }
+ }
+ break;
+ case RIGHT:
+ int right = cs.cellX + cs.spanX;
+ for (int j = cs.cellY; j < cs.cellY + cs.spanY; j++) {
+ if (right > rightEdge[j]) {
+ rightEdge[j] = right;
+ }
+ }
+ break;
+ case TOP:
+ int top = cs.cellY;
+ for (int j = cs.cellX; j < cs.cellX + cs.spanX; j++) {
+ if (top < topEdge[j] || topEdge[j] < 0) {
+ topEdge[j] = top;
+ }
+ }
+ break;
+ case BOTTOM:
+ int bottom = cs.cellY + cs.spanY;
+ for (int j = cs.cellX; j < cs.cellX + cs.spanX; j++) {
+ if (bottom > bottomEdge[j]) {
+ bottomEdge[j] = bottom;
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ boolean isViewTouchingEdge(View v, int whichEdge) {
+ CellAndSpan cs = config.map.get(v);
+
+ if ((dirtyEdges & whichEdge) == whichEdge) {
+ computeEdge(whichEdge);
+ dirtyEdges &= ~whichEdge;
+ }
+
+ switch (whichEdge) {
+ case LEFT:
+ for (int i = cs.cellY; i < cs.cellY + cs.spanY; i++) {
+ if (leftEdge[i] == cs.cellX + cs.spanX) {
+ return true;
+ }
+ }
+ break;
+ case RIGHT:
+ for (int i = cs.cellY; i < cs.cellY + cs.spanY; i++) {
+ if (rightEdge[i] == cs.cellX) {
+ return true;
+ }
+ }
+ break;
+ case TOP:
+ for (int i = cs.cellX; i < cs.cellX + cs.spanX; i++) {
+ if (topEdge[i] == cs.cellY + cs.spanY) {
+ return true;
+ }
+ }
+ break;
+ case BOTTOM:
+ for (int i = cs.cellX; i < cs.cellX + cs.spanX; i++) {
+ if (bottomEdge[i] == cs.cellY) {
+ return true;
+ }
+ }
+ break;
+ }
+ return false;
+ }
+
+ void shift(int whichEdge, int delta) {
+ for (View v: views) {
+ CellAndSpan c = config.map.get(v);
+ switch (whichEdge) {
+ case LEFT:
+ c.cellX -= delta;
+ break;
+ case RIGHT:
+ c.cellX += delta;
+ break;
+ case TOP:
+ c.cellY -= delta;
+ break;
+ case BOTTOM:
+ default:
+ c.cellY += delta;
+ break;
+ }
+ }
+ resetEdges();
+ }
+
+ public void addView(View v) {
+ views.add(v);
+ resetEdges();
+ }
+
+ public Rect getBoundingRect() {
+ if (boundingRectDirty) {
+ config.getBoundingRectForViews(views, boundingRect);
+ }
+ return boundingRect;
+ }
+
+ final PositionComparator comparator = new PositionComparator();
+ class PositionComparator implements Comparator<View> {
+ int whichEdge = 0;
+ public int compare(View left, View right) {
+ CellAndSpan l = config.map.get(left);
+ CellAndSpan r = config.map.get(right);
+ switch (whichEdge) {
+ case LEFT:
+ return (r.cellX + r.spanX) - (l.cellX + l.spanX);
+ case RIGHT:
+ return l.cellX - r.cellX;
+ case TOP:
+ return (r.cellY + r.spanY) - (l.cellY + l.spanY);
+ case BOTTOM:
+ default:
+ return l.cellY - r.cellY;
+ }
+ }
+ }
+
+ public void sortConfigurationForEdgePush(int edge) {
+ comparator.whichEdge = edge;
+ Collections.sort(config.sortedViews, comparator);
+ }
+ }
+
+ // This method tries to find a reordering solution which satisfies the push mechanic by trying
+ // to push items in each of the cardinal directions, in an order based on the direction vector
+ // passed.
+ private boolean attemptPushInDirection(ArrayList<View> intersectingViews, Rect occupied,
+ int[] direction, View ignoreView, ItemConfiguration solution) {
+ if ((Math.abs(direction[0]) + Math.abs(direction[1])) > 1) {
+ // If the direction vector has two non-zero components, we try pushing
+ // separately in each of the components.
+ int temp = direction[1];
+ direction[1] = 0;
+
+ if (pushViewsToTempLocation(intersectingViews, occupied, direction,
+ ignoreView, solution)) {
+ return true;
+ }
+ direction[1] = temp;
+ temp = direction[0];
+ direction[0] = 0;
+
+ if (pushViewsToTempLocation(intersectingViews, occupied, direction,
+ ignoreView, solution)) {
+ return true;
+ }
+ // Revert the direction
+ direction[0] = temp;
+
+ // Now we try pushing in each component of the opposite direction
+ direction[0] *= -1;
+ direction[1] *= -1;
+ temp = direction[1];
+ direction[1] = 0;
+ if (pushViewsToTempLocation(intersectingViews, occupied, direction,
+ ignoreView, solution)) {
+ return true;
+ }
+
+ direction[1] = temp;
+ temp = direction[0];
+ direction[0] = 0;
+ if (pushViewsToTempLocation(intersectingViews, occupied, direction,
+ ignoreView, solution)) {
+ return true;
+ }
+ // revert the direction
+ direction[0] = temp;
+ direction[0] *= -1;
+ direction[1] *= -1;
+
+ } else {
+ // If the direction vector has a single non-zero component, we push first in the
+ // direction of the vector
+ if (pushViewsToTempLocation(intersectingViews, occupied, direction,
+ ignoreView, solution)) {
+ return true;
+ }
+ // Then we try the opposite direction
+ direction[0] *= -1;
+ direction[1] *= -1;
+ if (pushViewsToTempLocation(intersectingViews, occupied, direction,
+ ignoreView, solution)) {
+ return true;
+ }
+ // Switch the direction back
+ direction[0] *= -1;
+ direction[1] *= -1;
+
+ // If we have failed to find a push solution with the above, then we try
+ // to find a solution by pushing along the perpendicular axis.
+
+ // Swap the components
+ int temp = direction[1];
+ direction[1] = direction[0];
+ direction[0] = temp;
+ if (pushViewsToTempLocation(intersectingViews, occupied, direction,
+ ignoreView, solution)) {
+ return true;
+ }
+
+ // Then we try the opposite direction
+ direction[0] *= -1;
+ direction[1] *= -1;
+ if (pushViewsToTempLocation(intersectingViews, occupied, direction,
+ ignoreView, solution)) {
+ return true;
+ }
+ // Switch the direction back
+ direction[0] *= -1;
+ direction[1] *= -1;
+
+ // Swap the components back
+ temp = direction[1];
+ direction[1] = direction[0];
+ direction[0] = temp;
+ }
+ return false;
+ }
+
+ /*
+ * Returns a pair (x, y), where x,y are in {-1, 0, 1} corresponding to vector between
+ * the provided point and the provided cell
+ */
+ private void computeDirectionVector(float deltaX, float deltaY, int[] result) {
+ double angle = Math.atan(deltaY / deltaX);
+
+ result[0] = 0;
+ result[1] = 0;
+ if (Math.abs(Math.cos(angle)) > 0.5f) {
+ result[0] = (int) Math.signum(deltaX);
+ }
+ if (Math.abs(Math.sin(angle)) > 0.5f) {
+ result[1] = (int) Math.signum(deltaY);
+ }
+ }
+
/* This seems like it should be obvious and straight-forward, but when the direction vector
needs to match with the notion of the dragView pushing other views, we have to employ
a slightly more subtle notion of the direction vector. The question is what two points is
@@ -2370,113 +2282,217 @@
}
}
- // For a given cell and span, fetch the set of views intersecting the region.
- private void getViewsIntersectingRegion(int cellX, int cellY, int spanX, int spanY,
- View dragView, Rect boundingRect, ArrayList<View> intersectingViews) {
- if (boundingRect != null) {
- boundingRect.set(cellX, cellY, cellX + spanX, cellY + spanY);
+ private boolean addViewsToTempLocation(ArrayList<View> views, Rect rectOccupiedByPotentialDrop,
+ int[] direction, View dragView, ItemConfiguration currentState) {
+ if (views.size() == 0) return true;
+
+ boolean success = false;
+ Rect boundingRect = new Rect();
+ // We construct a rect which represents the entire group of views passed in
+ currentState.getBoundingRectForViews(views, boundingRect);
+
+ // Mark the occupied state as false for the group of views we want to move.
+ for (View v: views) {
+ CellAndSpan c = currentState.map.get(v);
+ mTmpOccupied.markCells(c, false);
}
- intersectingViews.clear();
+
+ GridOccupancy blockOccupied = new GridOccupancy(boundingRect.width(), boundingRect.height());
+ int top = boundingRect.top;
+ int left = boundingRect.left;
+ // We mark more precisely which parts of the bounding rect are truly occupied, allowing
+ // for interlocking.
+ for (View v: views) {
+ CellAndSpan c = currentState.map.get(v);
+ blockOccupied.markCells(c.cellX - left, c.cellY - top, c.spanX, c.spanY, true);
+ }
+
+ mTmpOccupied.markCells(rectOccupiedByPotentialDrop, true);
+
+ findNearestArea(boundingRect.left, boundingRect.top, boundingRect.width(),
+ boundingRect.height(), direction,
+ mTmpOccupied.cells, blockOccupied.cells, mTempLocation);
+
+ // If we successfully found a location by pushing the block of views, we commit it
+ if (mTempLocation[0] >= 0 && mTempLocation[1] >= 0) {
+ int deltaX = mTempLocation[0] - boundingRect.left;
+ int deltaY = mTempLocation[1] - boundingRect.top;
+ for (View v: views) {
+ CellAndSpan c = currentState.map.get(v);
+ c.cellX += deltaX;
+ c.cellY += deltaY;
+ }
+ success = true;
+ }
+
+ // In either case, we set the occupied array as marked for the location of the views
+ for (View v: views) {
+ CellAndSpan c = currentState.map.get(v);
+ mTmpOccupied.markCells(c, true);
+ }
+ return success;
+ }
+
+ private boolean rearrangementExists(int cellX, int cellY, int spanX, int spanY, int[] direction,
+ View ignoreView, ItemConfiguration solution) {
+ // Return early if get invalid cell positions
+ if (cellX < 0 || cellY < 0) return false;
+
+ mIntersectingViews.clear();
+ mOccupiedRect.set(cellX, cellY, cellX + spanX, cellY + spanY);
+
+ // Mark the desired location of the view currently being dragged.
+ if (ignoreView != null) {
+ CellAndSpan c = solution.map.get(ignoreView);
+ if (c != null) {
+ c.cellX = cellX;
+ c.cellY = cellY;
+ }
+ }
Rect r0 = new Rect(cellX, cellY, cellX + spanX, cellY + spanY);
Rect r1 = new Rect();
- final int count = mShortcutsAndWidgets.getChildCount();
- for (int i = 0; i < count; i++) {
- View child = mShortcutsAndWidgets.getChildAt(i);
- if (child == dragView) continue;
- LayoutParams lp = (LayoutParams) child.getLayoutParams();
- r1.set(lp.cellX, lp.cellY, lp.cellX + lp.cellHSpan, lp.cellY + lp.cellVSpan);
+ for (View child: solution.map.keySet()) {
+ if (child == ignoreView) continue;
+ CellAndSpan c = solution.map.get(child);
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
+ r1.set(c.cellX, c.cellY, c.cellX + c.spanX, c.cellY + c.spanY);
if (Rect.intersects(r0, r1)) {
+ if (!lp.canReorder) {
+ return false;
+ }
mIntersectingViews.add(child);
- if (boundingRect != null) {
- boundingRect.union(r1);
- }
}
}
- }
- boolean isNearestDropLocationOccupied(int pixelX, int pixelY, int spanX, int spanY,
- View dragView, int[] result) {
- result = findNearestArea(pixelX, pixelY, spanX, spanY, result);
- getViewsIntersectingRegion(result[0], result[1], spanX, spanY, dragView, null,
- mIntersectingViews);
- return !mIntersectingViews.isEmpty();
- }
+ solution.intersectingViews = new ArrayList<>(mIntersectingViews);
- void revertTempState() {
- completeAndClearReorderPreviewAnimations();
- if (isItemPlacementDirty() && !DESTRUCTIVE_REORDER) {
- final int count = mShortcutsAndWidgets.getChildCount();
- for (int i = 0; i < count; i++) {
- View child = mShortcutsAndWidgets.getChildAt(i);
- LayoutParams lp = (LayoutParams) child.getLayoutParams();
- if (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.cellY) {
- lp.tmpCellX = lp.cellX;
- lp.tmpCellY = lp.cellY;
- animateChildToPosition(child, lp.cellX, lp.cellY, REORDER_ANIMATION_DURATION,
- 0, false, false);
- }
- }
- setItemPlacementDirty(false);
+ // First we try to find a solution which respects the push mechanic. That is,
+ // we try to find a solution such that no displaced item travels through another item
+ // without also displacing that item.
+ if (attemptPushInDirection(mIntersectingViews, mOccupiedRect, direction, ignoreView,
+ solution)) {
+ return true;
}
- }
- boolean createAreaForResize(int cellX, int cellY, int spanX, int spanY,
- View dragView, int[] direction, boolean commit) {
- int[] pixelXY = new int[2];
- regionToCenterPoint(cellX, cellY, spanX, spanY, pixelXY);
-
- // First we determine if things have moved enough to cause a different layout
- ItemConfiguration swapSolution = findReorderSolution(pixelXY[0], pixelXY[1], spanX, spanY,
- spanX, spanY, direction, dragView, true, new ItemConfiguration());
-
- setUseTempCoords(true);
- if (swapSolution != null && swapSolution.isSolution) {
- // If we're just testing for a possible location (MODE_ACCEPT_DROP), we don't bother
- // committing anything or animating anything as we just want to determine if a solution
- // exists
- copySolutionToTempState(swapSolution, dragView);
- setItemPlacementDirty(true);
- animateItemsToSolution(swapSolution, dragView, commit);
-
- if (commit) {
- commitTempPlacement(null);
- completeAndClearReorderPreviewAnimations();
- setItemPlacementDirty(false);
- } else {
- beginOrAdjustReorderPreviewAnimations(swapSolution, dragView,
- ReorderPreviewAnimation.MODE_PREVIEW);
- }
- mShortcutsAndWidgets.requestLayout();
+ // Next we try moving the views as a block, but without requiring the push mechanic.
+ if (addViewsToTempLocation(mIntersectingViews, mOccupiedRect, direction, ignoreView,
+ solution)) {
+ return true;
}
- return swapSolution.isSolution;
+
+ // Ok, they couldn't move as a block, let's move them individually
+ for (View v : mIntersectingViews) {
+ if (!addViewToTempLocation(v, mOccupiedRect, direction, solution)) {
+ return false;
+ }
+ }
+ return true;
}
- int[] performReorder(int pixelX, int pixelY, int minSpanX, int minSpanY, int spanX, int spanY,
- View dragView, int[] result, int resultSpan[], int mode) {
- // First we determine if things have moved enough to cause a different layout
+ private ItemConfiguration findReorderSolution(int pixelX, int pixelY, int minSpanX, int minSpanY,
+ int spanX, int spanY, int[] direction, View dragView, boolean decX,
+ ItemConfiguration solution) {
+ // Copy the current state into the solution. This solution will be manipulated as necessary.
+ copyCurrentStateToSolution(solution, false);
+ // Copy the current occupied array into the temporary occupied array. This array will be
+ // manipulated as necessary to find a solution.
+ mOccupied.copyTo(mTmpOccupied);
+
+ // We find the nearest cell into which we would place the dragged item, assuming there's
+ // nothing in its way.
+ int result[] = new int[2];
result = findNearestArea(pixelX, pixelY, spanX, spanY, result);
- if (resultSpan == null) {
- resultSpan = new int[2];
- }
+ boolean success;
+ // First we try the exact nearest position of the item being dragged,
+ // we will then want to try to move this around to other neighbouring positions
+ success = rearrangementExists(result[0], result[1], spanX, spanY, direction, dragView,
+ solution);
- // When we are checking drop validity or actually dropping, we don't recompute the
- // direction vector, since we want the solution to match the preview, and it's possible
- // that the exact position of the item has changed to result in a new reordering outcome.
- if ((mode == MODE_ON_DROP || mode == MODE_ON_DROP_EXTERNAL || mode == MODE_ACCEPT_DROP)
- && mPreviousReorderDirection[0] != INVALID_DIRECTION) {
- mDirectionVector[0] = mPreviousReorderDirection[0];
- mDirectionVector[1] = mPreviousReorderDirection[1];
- // We reset this vector after drop
- if (mode == MODE_ON_DROP || mode == MODE_ON_DROP_EXTERNAL) {
- mPreviousReorderDirection[0] = INVALID_DIRECTION;
- mPreviousReorderDirection[1] = INVALID_DIRECTION;
+ if (!success) {
+ // We try shrinking the widget down to size in an alternating pattern, shrink 1 in
+ // x, then 1 in y etc.
+ if (spanX > minSpanX && (minSpanY == spanY || decX)) {
+ return findReorderSolution(pixelX, pixelY, minSpanX, minSpanY, spanX - 1, spanY,
+ direction, dragView, false, solution);
+ } else if (spanY > minSpanY) {
+ return findReorderSolution(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY - 1,
+ direction, dragView, true, solution);
}
+ solution.isSolution = false;
} else {
- getDirectionVectorForDrop(pixelX, pixelY, spanX, spanY, dragView, mDirectionVector);
- mPreviousReorderDirection[0] = mDirectionVector[0];
- mPreviousReorderDirection[1] = mDirectionVector[1];
+ solution.isSolution = true;
+ solution.cellX = result[0];
+ solution.cellY = result[1];
+ solution.spanX = spanX;
+ solution.spanY = spanY;
}
+ return solution;
+ }
+
+ private void copyCurrentStateToSolution(ItemConfiguration solution, boolean temp) {
+ int childCount = mShortcutsAndWidgets.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ View child = mShortcutsAndWidgets.getChildAt(i);
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
+ CellAndSpan c;
+ if (temp) {
+ c = new CellAndSpan(lp.tmpCellX, lp.tmpCellY, lp.cellHSpan, lp.cellVSpan);
+ } else {
+ c = new CellAndSpan(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan);
+ }
+ solution.add(child, c);
+ }
+ }
+
+ /**
+ * Returns a "reorder" where we simply drop the item in the closest empty space, without moving
+ * any other item in the way.
+ *
+ * @param pixelX X coordinate in pixels in the screen
+ * @param pixelY Y coordinate in pixels in the screen
+ * @param spanX horizontal cell span
+ * @param spanY vertical cell span
+ * @return the configuration that represents the found reorder
+ */
+ public ItemConfiguration closestEmptySpaceReorder(int pixelX, int pixelY, int spanX,
+ int spanY) {
+ int[] result = new int[2];
+ result = findNearestArea(pixelX, pixelY, spanX, spanY, result);
+ ItemConfiguration solution = new ItemConfiguration();
+ copyCurrentStateToSolution(solution, false);
+ solution.isSolution = result[0] != -1;
+ if (!solution.isSolution) {
+ return solution;
+ }
+ solution.cellX = result[0];
+ solution.cellY = result[1];
+ solution.spanX = spanX;
+ solution.spanY = spanY;
+ return solution;
+ }
+
+ /**
+ * When the user drags an Item in the workspace sometimes we need to move the items already in
+ * the workspace to make space for the new item, this function return a solution for that
+ * reorder.
+ *
+ * @param pixelX X coordinate in the screen of the dragView in pixels
+ * @param pixelY Y coordinate in the screen of the dragView in pixels
+ * @param minSpanX minimum horizontal span the item can be shrunk to
+ * @param minSpanY minimum vertical span the item can be shrunk to
+ * @param spanX occupied horizontal span
+ * @param spanY occupied vertical span
+ * @param dragView the view of the item being draged
+ * @return returns a solution for the given parameters, the solution contains all the icons and
+ * the locations they should be in the given solution.
+ */
+ public ItemConfiguration calculateReorder(int pixelX, int pixelY, int minSpanX, int minSpanY,
+ int spanX, int spanY, View dragView) {
+ getDirectionVectorForDrop(pixelX, pixelY, spanX, spanY, dragView, mDirectionVector);
+
+ ItemConfiguration closestSpaceSolution = closestEmptySpaceReorder(pixelX, pixelY, spanX,
+ spanY);
// Find a solution involving pushing / displacing any items in the way
ItemConfiguration swapSolution = findReorderSolution(pixelX, pixelY, minSpanX, minSpanY,
@@ -2486,73 +2502,104 @@
ItemConfiguration noShuffleSolution = findConfigurationNoShuffle(pixelX, pixelY, minSpanX,
minSpanY, spanX, spanY, dragView, new ItemConfiguration());
- ItemConfiguration finalSolution = null;
-
// If the reorder solution requires resizing (shrinking) the item being dropped, we instead
// favor a solution in which the item is not resized, but
if (swapSolution.isSolution && swapSolution.area() >= noShuffleSolution.area()) {
- finalSolution = swapSolution;
+ return swapSolution;
} else if (noShuffleSolution.isSolution) {
- finalSolution = noShuffleSolution;
+ return noShuffleSolution;
+ } else if (closestSpaceSolution.isSolution) {
+ return closestSpaceSolution;
+ }
+ return null;
+ }
+
+ int[] performReorder(int pixelX, int pixelY, int minSpanX, int minSpanY, int spanX, int spanY,
+ View dragView, int[] result, int[] resultSpan, int mode) {
+ if (resultSpan == null) {
+ resultSpan = new int[]{-1, -1};
+ }
+ if (result == null) {
+ result = new int[]{-1, -1};
}
- if (mode == MODE_SHOW_REORDER_HINT) {
- if (finalSolution != null) {
- beginOrAdjustReorderPreviewAnimations(finalSolution, dragView,
- ReorderPreviewAnimation.MODE_HINT);
- result[0] = finalSolution.cellX;
- result[1] = finalSolution.cellY;
- resultSpan[0] = finalSolution.spanX;
- resultSpan[1] = finalSolution.spanY;
- } else {
- result[0] = result[1] = resultSpan[0] = resultSpan[1] = -1;
+ ItemConfiguration finalSolution = null;
+ // We want the solution to match the animation of the preview and to match the drop so we
+ // only recalculate in mode MODE_SHOW_REORDER_HINT because that the first one to run in the
+ // reorder cycle.
+ if (mode == MODE_SHOW_REORDER_HINT || mPreviousSolution == null) {
+ finalSolution = calculateReorder(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY,
+ dragView);
+ mPreviousSolution = finalSolution;
+ } else {
+ finalSolution = mPreviousSolution;
+ // We reset this vector after drop
+ if (mode == MODE_ON_DROP || mode == MODE_ON_DROP_EXTERNAL) {
+ mPreviousSolution = null;
}
- return result;
}
- boolean foundSolution = true;
- if (!DESTRUCTIVE_REORDER) {
- setUseTempCoords(true);
- }
-
- if (finalSolution != null) {
+ if (finalSolution == null || !finalSolution.isSolution) {
+ result[0] = result[1] = resultSpan[0] = resultSpan[1] = -1;
+ } else {
result[0] = finalSolution.cellX;
result[1] = finalSolution.cellY;
resultSpan[0] = finalSolution.spanX;
resultSpan[1] = finalSolution.spanY;
+ }
+ performReorder(finalSolution, dragView, mode);
+ return result;
+ }
- // If we're just testing for a possible location (MODE_ACCEPT_DROP), we don't bother
- // committing anything or animating anything as we just want to determine if a solution
- // exists
- if (mode == MODE_DRAG_OVER || mode == MODE_ON_DROP || mode == MODE_ON_DROP_EXTERNAL) {
- if (!DESTRUCTIVE_REORDER) {
- copySolutionToTempState(finalSolution, dragView);
- }
- setItemPlacementDirty(true);
- animateItemsToSolution(finalSolution, dragView, mode == MODE_ON_DROP);
-
- if (!DESTRUCTIVE_REORDER &&
- (mode == MODE_ON_DROP || mode == MODE_ON_DROP_EXTERNAL)) {
- // Since the temp solution didn't update dragView, don't commit it either
- commitTempPlacement(dragView);
- completeAndClearReorderPreviewAnimations();
- setItemPlacementDirty(false);
- } else {
- beginOrAdjustReorderPreviewAnimations(finalSolution, dragView,
- ReorderPreviewAnimation.MODE_PREVIEW);
- }
+ /**
+ * Animates and submits in the DB the given ItemConfiguration depending of the mode.
+ *
+ * @param solution represents widgets on the screen which the Workspace will animate to and
+ * would be submitted to the database.
+ * @param dragView view which is being dragged over the workspace that trigger the reorder
+ * @param mode depending on the mode different animations would be played and depending on the
+ * mode the solution would be submitted or not the database.
+ * The possible modes are {@link MODE_SHOW_REORDER_HINT}, {@link MODE_DRAG_OVER},
+ * {@link MODE_ON_DROP}, {@link MODE_ON_DROP_EXTERNAL}, {@link MODE_ACCEPT_DROP}
+ * defined in {@link CellLayout}.
+ */
+ void performReorder(ItemConfiguration solution, View dragView, int mode) {
+ if (mode == MODE_SHOW_REORDER_HINT) {
+ beginOrAdjustReorderPreviewAnimations(solution, dragView,
+ ReorderPreviewAnimation.MODE_HINT);
+ return;
+ }
+ // If we're just testing for a possible location (MODE_ACCEPT_DROP), we don't bother
+ // committing anything or animating anything as we just want to determine if a solution
+ // exists
+ if (mode == MODE_DRAG_OVER || mode == MODE_ON_DROP || mode == MODE_ON_DROP_EXTERNAL) {
+ if (!DESTRUCTIVE_REORDER) {
+ setUseTempCoords(true);
}
- } else {
- foundSolution = false;
- result[0] = result[1] = resultSpan[0] = resultSpan[1] = -1;
+
+ if (!DESTRUCTIVE_REORDER) {
+ copySolutionToTempState(solution, dragView);
+ }
+ setItemPlacementDirty(true);
+ animateItemsToSolution(solution, dragView, mode == MODE_ON_DROP);
+
+ if (!DESTRUCTIVE_REORDER
+ && (mode == MODE_ON_DROP || mode == MODE_ON_DROP_EXTERNAL)) {
+ // Since the temp solution didn't update dragView, don't commit it either
+ commitTempPlacement(dragView);
+ completeAndClearReorderPreviewAnimations();
+ setItemPlacementDirty(false);
+ } else {
+ beginOrAdjustReorderPreviewAnimations(solution, dragView,
+ ReorderPreviewAnimation.MODE_PREVIEW);
+ }
}
- if ((mode == MODE_ON_DROP || !foundSolution) && !DESTRUCTIVE_REORDER) {
+ if (mode == MODE_ON_DROP && !DESTRUCTIVE_REORDER) {
setUseTempCoords(false);
}
mShortcutsAndWidgets.requestLayout();
- return result;
}
void setItemPlacementDirty(boolean dirty) {
@@ -2685,7 +2732,8 @@
*/
void onDropChild(View child) {
if (child != null) {
- LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ CellLayoutLayoutParams
+ lp = (CellLayoutLayoutParams) child.getLayoutParams();
lp.dropped = true;
child.requestLayout();
markCellsAsOccupiedForView(child);
@@ -2727,7 +2775,8 @@
return;
}
if (view == null || view.getParent() != mShortcutsAndWidgets) return;
- LayoutParams lp = (LayoutParams) view.getLayoutParams();
+ CellLayoutLayoutParams
+ lp = (CellLayoutLayoutParams) view.getLayoutParams();
mOccupied.markCells(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, true);
}
@@ -2739,7 +2788,8 @@
return;
}
if (view == null || view.getParent() != mShortcutsAndWidgets) return;
- LayoutParams lp = (LayoutParams) view.getLayoutParams();
+ CellLayoutLayoutParams
+ lp = (CellLayoutLayoutParams) view.getLayoutParams();
mOccupied.markCells(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, false);
}
@@ -2763,165 +2813,17 @@
@Override
public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
- return new CellLayout.LayoutParams(getContext(), attrs);
+ return new CellLayoutLayoutParams(getContext(), attrs);
}
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
- return p instanceof CellLayout.LayoutParams;
+ return p instanceof CellLayoutLayoutParams;
}
@Override
protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
- return new CellLayout.LayoutParams(p);
- }
-
- public static class LayoutParams extends ViewGroup.MarginLayoutParams {
- /**
- * Horizontal location of the item in the grid.
- */
- @ViewDebug.ExportedProperty
- public int cellX;
-
- /**
- * Vertical location of the item in the grid.
- */
- @ViewDebug.ExportedProperty
- public int cellY;
-
- /**
- * Temporary horizontal location of the item in the grid during reorder
- */
- public int tmpCellX;
-
- /**
- * Temporary vertical location of the item in the grid during reorder
- */
- public int tmpCellY;
-
- /**
- * Indicates that the temporary coordinates should be used to layout the items
- */
- public boolean useTmpCoords;
-
- /**
- * Number of cells spanned horizontally by the item.
- */
- @ViewDebug.ExportedProperty
- public int cellHSpan;
-
- /**
- * Number of cells spanned vertically by the item.
- */
- @ViewDebug.ExportedProperty
- public int cellVSpan;
-
- /**
- * Indicates whether the item will set its x, y, width and height parameters freely,
- * or whether these will be computed based on cellX, cellY, cellHSpan and cellVSpan.
- */
- public boolean isLockedToGrid = true;
-
- /**
- * Indicates whether this item can be reordered. Always true except in the case of the
- * the AllApps button and QSB place holder.
- */
- public boolean canReorder = true;
-
- // X coordinate of the view in the layout.
- @ViewDebug.ExportedProperty
- public int x;
- // Y coordinate of the view in the layout.
- @ViewDebug.ExportedProperty
- public int y;
-
- boolean dropped;
-
- public LayoutParams(Context c, AttributeSet attrs) {
- super(c, attrs);
- cellHSpan = 1;
- cellVSpan = 1;
- }
-
- public LayoutParams(ViewGroup.LayoutParams source) {
- super(source);
- cellHSpan = 1;
- cellVSpan = 1;
- }
-
- public LayoutParams(LayoutParams source) {
- super(source);
- this.cellX = source.cellX;
- this.cellY = source.cellY;
- this.cellHSpan = source.cellHSpan;
- this.cellVSpan = source.cellVSpan;
- }
-
- public LayoutParams(int cellX, int cellY, int cellHSpan, int cellVSpan) {
- super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
- this.cellX = cellX;
- this.cellY = cellY;
- this.cellHSpan = cellHSpan;
- this.cellVSpan = cellVSpan;
- }
-
- public void setup(int cellWidth, int cellHeight, boolean invertHorizontally, int colCount,
- int rowCount, Point borderSpace, @Nullable Rect inset) {
- setup(cellWidth, cellHeight, invertHorizontally, colCount, rowCount, 1.0f, 1.0f,
- borderSpace, inset);
- }
-
- /**
- * Use this method, as opposed to {@link #setup(int, int, boolean, int, int, Point, Rect)},
- * if the view needs to be scaled.
- *
- * ie. In multi-window mode, we setup widgets so that they are measured and laid out
- * using their full/invariant device profile sizes.
- */
- public void setup(int cellWidth, int cellHeight, boolean invertHorizontally, int colCount,
- int rowCount, float cellScaleX, float cellScaleY, Point borderSpace,
- @Nullable Rect inset) {
- if (isLockedToGrid) {
- final int myCellHSpan = cellHSpan;
- final int myCellVSpan = cellVSpan;
- int myCellX = useTmpCoords ? tmpCellX : cellX;
- int myCellY = useTmpCoords ? tmpCellY : cellY;
-
- if (invertHorizontally) {
- myCellX = colCount - myCellX - cellHSpan;
- }
-
- int hBorderSpacing = (myCellHSpan - 1) * borderSpace.x;
- int vBorderSpacing = (myCellVSpan - 1) * borderSpace.y;
-
- float myCellWidth = ((myCellHSpan * cellWidth) + hBorderSpacing) / cellScaleX;
- float myCellHeight = ((myCellVSpan * cellHeight) + vBorderSpacing) / cellScaleY;
-
- width = Math.round(myCellWidth) - leftMargin - rightMargin;
- height = Math.round(myCellHeight) - topMargin - bottomMargin;
- x = leftMargin + (myCellX * cellWidth) + (myCellX * borderSpace.x);
- y = topMargin + (myCellY * cellHeight) + (myCellY * borderSpace.y);
-
- if (inset != null) {
- x -= inset.left;
- y -= inset.top;
- width += inset.left + inset.right;
- height += inset.top + inset.bottom;
- }
- }
- }
-
- /**
- * Sets the position to the provided point
- */
- public void setCellXY(Point point) {
- cellX = point.x;
- cellY = point.y;
- }
-
- public String toString() {
- return "(" + this.cellX + ", " + this.cellY + ")";
- }
+ return new CellLayoutLayoutParams(p);
}
// This class stores info for two purposes:
diff --git a/src/com/android/launcher3/DefaultLayoutParser.java b/src/com/android/launcher3/DefaultLayoutParser.java
index 4daca8b..af13bea 100644
--- a/src/com/android/launcher3/DefaultLayoutParser.java
+++ b/src/com/android/launcher3/DefaultLayoutParser.java
@@ -1,6 +1,5 @@
package com.android.launcher3;
-import android.appwidget.AppWidgetHost;
import android.appwidget.AppWidgetManager;
import android.content.ComponentName;
import android.content.Context;
@@ -21,6 +20,7 @@
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.util.Thunk;
+import com.android.launcher3.widget.LauncherWidgetHolder;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -55,9 +55,9 @@
private static final String ACTION_APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE =
"com.android.launcher.action.APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE";
- public DefaultLayoutParser(Context context, AppWidgetHost appWidgetHost,
+ public DefaultLayoutParser(Context context, LauncherWidgetHolder appWidgetHolder,
LayoutParserCallback callback, Resources sourceRes, int layoutId) {
- super(context, appWidgetHost, callback, sourceRes, layoutId, TAG_FAVORITES);
+ super(context, appWidgetHolder, callback, sourceRes, layoutId, TAG_FAVORITES);
}
@Override
@@ -336,11 +336,11 @@
final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
int insertedId = -1;
try {
- int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
+ int appWidgetId = mAppWidgetHolder.allocateAppWidgetId();
if (!appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, cn)) {
Log.e(TAG, "Unable to bind app widget id " + cn);
- mAppWidgetHost.deleteAppWidgetId(appWidgetId);
+ mAppWidgetHolder.deleteAppWidgetId(appWidgetId);
return -1;
}
@@ -349,7 +349,7 @@
mValues.put(Favorites._ID, mCallback.generateNewItemId());
insertedId = mCallback.insertAndCheck(mDb, mValues);
if (insertedId < 0) {
- mAppWidgetHost.deleteAppWidgetId(appWidgetId);
+ mAppWidgetHolder.deleteAppWidgetId(appWidgetId);
return insertedId;
}
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 20352a3..3cb3461 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -22,14 +22,17 @@
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.folder.ClippedFolderIconLayoutRule.ICON_OVERLAP_FACTOR;
import static com.android.launcher3.icons.GraphicsUtils.getShapePath;
+import static com.android.launcher3.testing.shared.ResourceUtils.INVALID_RESOURCE_HANDLE;
import static com.android.launcher3.testing.shared.ResourceUtils.pxFromDp;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.content.res.TypedArray;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
@@ -42,6 +45,7 @@
import com.android.launcher3.CellLayout.ContainerType;
import com.android.launcher3.DevicePaddings.DevicePadding;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.DotRenderer;
import com.android.launcher3.icons.IconNormalizer;
import com.android.launcher3.model.data.ItemInfo;
@@ -52,12 +56,14 @@
import java.io.PrintWriter;
import java.util.List;
+import java.util.Locale;
@SuppressLint("NewApi")
public class DeviceProfile {
private static final int DEFAULT_DOT_SIZE = 100;
private static final float ALL_APPS_TABLET_MAX_ROWS = 5.5f;
+ private static final float MIN_FOLDER_TEXT_SIZE_SP = 16f;
public static final PointF DEFAULT_SCALE = new PointF(1.0f, 1.0f);
public static final ViewScaleProvider DEFAULT_PROVIDER = itemInfo -> DEFAULT_SCALE;
@@ -144,12 +150,12 @@
// Folder
public float folderLabelTextScale;
public int folderLabelTextSizePx;
+ public int folderFooterHeightPx;
public int folderIconSizePx;
public int folderIconOffsetYPx;
// Folder content
- public Point folderCellLayoutBorderSpacePx;
- public int folderCellLayoutBorderSpaceOriginalPx;
+ public int folderCellLayoutBorderSpacePx;
public int folderContentPaddingLeftRight;
public int folderContentPaddingTop;
@@ -181,11 +187,19 @@
private final int hotseatQsbShadowHeight;
public int hotseatBorderSpace;
+ // Bottom sheets
+ public int bottomSheetTopPadding;
+ public int bottomSheetOpenDuration;
+ public int bottomSheetCloseDuration;
+ public float bottomSheetWorkspaceScale;
+ public float bottomSheetDepth;
+
// All apps
public Point allAppsBorderSpacePx;
public int allAppsShiftRange;
public int allAppsTopPadding;
- public int bottomSheetTopPadding;
+ public int allAppsOpenDuration;
+ public int allAppsCloseDuration;
public int allAppsCellHeightPx;
public int allAppsCellWidthPx;
public int allAppsIconSizePx;
@@ -208,6 +222,9 @@
public int overviewRowSpacing;
public int overviewGridSideMargin;
+ // Split staging
+ public int splitPlaceholderInset;
+
// Widgets
private final ViewScaleProvider mViewScaleProvider;
@@ -238,6 +255,7 @@
public boolean isTaskbarPresentInApps;
public int taskbarSize;
public int stashedTaskbarSize;
+ public int transientTaskbarMargin;
// DragController
public int flingToDeleteThresholdVelocity;
@@ -299,8 +317,16 @@
}
if (isTaskbarPresent) {
- taskbarSize = res.getDimensionPixelSize(R.dimen.taskbar_size);
- stashedTaskbarSize = res.getDimensionPixelSize(R.dimen.taskbar_stashed_size);
+ if (DisplayController.isTransientTaskbar(context)) {
+ taskbarSize = res.getDimensionPixelSize(R.dimen.transient_taskbar_size);
+ stashedTaskbarSize =
+ res.getDimensionPixelSize(R.dimen.transient_taskbar_stashed_size);
+ transientTaskbarMargin =
+ res.getDimensionPixelSize(R.dimen.transient_taskbar_margin);
+ } else {
+ taskbarSize = res.getDimensionPixelSize(R.dimen.taskbar_size);
+ stashedTaskbarSize = res.getDimensionPixelSize(R.dimen.taskbar_stashed_size);
+ }
}
edgeMarginPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin);
@@ -316,20 +342,51 @@
bottomSheetTopPadding = mInsets.top // statusbar height
+ res.getDimensionPixelSize(R.dimen.bottom_sheet_extra_top_padding)
+ (isTablet ? 0 : edgeMarginPx); // phones need edgeMarginPx additional padding
+ bottomSheetOpenDuration = res.getInteger(R.integer.config_bottomSheetOpenDuration);
+ bottomSheetCloseDuration = res.getInteger(R.integer.config_bottomSheetCloseDuration);
+ if (isTablet) {
+ bottomSheetWorkspaceScale = workspaceContentScale;
+ // The goal is to set wallpaper to zoom at workspaceContentScale when in AllApps.
+ // When depth is 0, wallpaper zoom is set to maxWallpaperScale.
+ // When depth is 1, wallpaper zoom is set to 1.
+ // For depth to achieve zoom set to maxWallpaperScale * workspaceContentScale:
+ float maxWallpaperScale = res.getFloat(R.dimen.config_wallpaperMaxScale);
+ bottomSheetDepth = Utilities.mapToRange(maxWallpaperScale * workspaceContentScale,
+ maxWallpaperScale, 1f, 0f, 1f, LINEAR);
+ } else {
+ bottomSheetWorkspaceScale = 1f;
+ bottomSheetDepth = 0f;
+ }
folderLabelTextScale = res.getFloat(R.dimen.folder_label_text_scale);
- folderContentPaddingLeftRight =
- res.getDimensionPixelSize(R.dimen.folder_content_padding_left_right);
- folderContentPaddingTop = res.getDimensionPixelSize(R.dimen.folder_content_padding_top);
+
+ if (inv.folderStyle != INVALID_RESOURCE_HANDLE) {
+ TypedArray folderStyle = context.obtainStyledAttributes(inv.folderStyle,
+ R.styleable.FolderDisplayStyle);
+ // These are re-set in #updateFolderCellSize if the grid is not scalable
+ folderCellHeightPx = folderStyle.getDimensionPixelSize(
+ R.styleable.FolderDisplayStyle_folderCellHeight, 0);
+ folderCellWidthPx = folderStyle.getDimensionPixelSize(
+ R.styleable.FolderDisplayStyle_folderCellWidth, 0);
+
+ folderContentPaddingTop = folderStyle.getDimensionPixelSize(
+ R.styleable.FolderDisplayStyle_folderTopPadding, 0);
+ folderCellLayoutBorderSpacePx = folderStyle.getDimensionPixelSize(
+ R.styleable.FolderDisplayStyle_folderBorderSpace, 0);
+ folderFooterHeightPx = folderStyle.getDimensionPixelSize(
+ R.styleable.FolderDisplayStyle_folderFooterHeight, 0);
+ folderStyle.recycle();
+ } else {
+ folderCellLayoutBorderSpacePx = 0;
+ folderFooterHeightPx = 0;
+ 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));
- cellLayoutBorderSpaceOriginalPx = new Point(cellLayoutBorderSpacePx);
- folderCellLayoutBorderSpaceOriginalPx = pxFromDp(inv.folderBorderSpace, mMetrics);
- folderCellLayoutBorderSpacePx = new Point(folderCellLayoutBorderSpaceOriginalPx,
- folderCellLayoutBorderSpaceOriginalPx);
workspacePageIndicatorHeight = res.getDimensionPixelSize(
R.dimen.workspace_page_indicator_height);
@@ -435,6 +492,8 @@
overviewRowSpacing = res.getDimensionPixelSize(R.dimen.overview_grid_row_spacing);
overviewGridSideMargin = res.getDimensionPixelSize(R.dimen.overview_grid_side_margin);
+ splitPlaceholderInset = res.getDimensionPixelSize(R.dimen.split_placeholder_inset);
+
// Calculate all of the remaining variables.
extraSpace = updateAvailableDimensions(res);
@@ -475,6 +534,8 @@
allAppsShiftRange =
res.getDimensionPixelSize(R.dimen.all_apps_starting_vertical_translate);
}
+ allAppsOpenDuration = res.getInteger(R.integer.config_allAppsOpenDuration);
+ allAppsCloseDuration = res.getInteger(R.integer.config_allAppsCloseDuration);
flingToDeleteThresholdVelocity = res.getDimensionPixelSize(
R.dimen.drag_flingToDeleteMinVelocity);
@@ -515,7 +576,7 @@
private int getIconToIconWidthForColumns(int columns) {
return columns * getCellSize().x
+ (columns - 1) * cellLayoutBorderSpacePx.x
- - (getCellSize().x - iconSizePx); // left and right cell space
+ - getCellHorizontalSpace();
}
private int getHorizontalMarginPx(InvariantDeviceProfile idp, Resources res) {
@@ -894,22 +955,20 @@
private void updateAvailableFolderCellDimensions(Resources res) {
updateFolderCellSize(1f, res);
- final int folderBottomPanelSize = res.getDimensionPixelSize(R.dimen.folder_label_height);
-
// Don't let the folder get too close to the edges of the screen.
int folderMargin = edgeMarginPx * 2;
Point totalWorkspacePadding = getTotalWorkspacePadding();
// Check if the icons fit within the available height.
float contentUsedHeight = folderCellHeightPx * inv.numFolderRows
- + ((inv.numFolderRows - 1) * folderCellLayoutBorderSpacePx.y);
- int contentMaxHeight = availableHeightPx - totalWorkspacePadding.y - folderBottomPanelSize
+ + ((inv.numFolderRows - 1) * folderCellLayoutBorderSpacePx);
+ int contentMaxHeight = availableHeightPx - totalWorkspacePadding.y - folderFooterHeightPx
- folderMargin - folderContentPaddingTop;
float scaleY = contentMaxHeight / contentUsedHeight;
// Check if the icons fit within the available width.
float contentUsedWidth = folderCellWidthPx * inv.numFolderColumns
- + ((inv.numFolderColumns - 1) * folderCellLayoutBorderSpacePx.x);
+ + ((inv.numFolderColumns - 1) * folderCellLayoutBorderSpacePx);
int contentMaxWidth = availableWidthPx - totalWorkspacePadding.x - folderMargin
- folderContentPaddingLeftRight * 2;
float scaleX = contentMaxWidth / contentUsedWidth;
@@ -927,21 +986,18 @@
folderChildIconSizePx = Math.max(1, pxFromDp(invIconSizeDp, mMetrics, scale));
folderChildTextSizePx =
pxFromSp(inv.iconTextSize[INDEX_DEFAULT], mMetrics, scale);
- folderLabelTextSizePx = (int) (folderChildTextSizePx * folderLabelTextScale);
+ folderLabelTextSizePx = Math.max(pxFromSp(MIN_FOLDER_TEXT_SIZE_SP, mMetrics),
+ (int) (folderChildTextSizePx * folderLabelTextScale));
int textHeight = Utilities.calculateTextHeight(folderChildTextSizePx);
if (isScalableGrid) {
- int minWidth = folderChildIconSizePx + iconDrawablePaddingPx * 2;
- int minHeight = folderChildIconSizePx + iconDrawablePaddingPx * 2 + textHeight;
+ if (inv.folderStyle == INVALID_RESOURCE_HANDLE) {
+ folderCellWidthPx = pxFromDp(getCellSize().x, mMetrics, scale);
+ folderCellHeightPx = pxFromDp(getCellSize().y, mMetrics, scale);
+ }
- folderCellWidthPx = (int) Math.max(minWidth, cellWidthPx * scale);
- folderCellHeightPx = (int) Math.max(minHeight, cellHeightPx * scale);
-
- int scaledSpace = (int) (folderCellLayoutBorderSpaceOriginalPx * scale);
- folderCellLayoutBorderSpacePx = new Point(scaledSpace, scaledSpace);
- folderContentPaddingLeftRight = scaledSpace;
- folderContentPaddingTop = scaledSpace;
+ folderContentPaddingLeftRight = folderCellLayoutBorderSpacePx;
} else {
int cellPaddingX = (int) (res.getDimensionPixelSize(R.dimen.folder_cell_x_padding)
* scale);
@@ -950,6 +1006,10 @@
folderCellWidthPx = folderChildIconSizePx + 2 * cellPaddingX;
folderCellHeightPx = folderChildIconSizePx + 2 * cellPaddingY + textHeight;
+ folderContentPaddingLeftRight =
+ res.getDimensionPixelSize(R.dimen.folder_content_padding_left_right);
+ folderFooterHeightPx =
+ res.getDimensionPixelSize(R.dimen.folder_footer_height_default);
}
folderChildDrawablePaddingPx = Math.max(0,
@@ -989,6 +1049,13 @@
}
/**
+ * Returns the left and right space on the cell, which is the cell width - icon size
+ */
+ public int getCellHorizontalSpace() {
+ return getCellSize().x - iconSizePx;
+ }
+
+ /**
* Gets the number of panels within the workspace.
*/
public int getPanelCount() {
@@ -1034,7 +1101,7 @@
/ getCellLayoutHeight();
scale = Math.min(scale, 1f);
- // Reduce scale if next pages would not be visible after scaling the workspace
+ // Reduce scale if next pages would not be visible after scaling the workspace.
int workspaceWidth = availableWidthPx;
float scaledWorkspaceWidth = workspaceWidth * scale;
float maxAvailableWidth = workspaceWidth - (2 * workspaceSpringLoadedMinNextPageVisiblePx);
@@ -1241,12 +1308,17 @@
* Returns the number of pixels required below OverviewActions excluding insets.
*/
public int getOverviewActionsClaimedSpaceBelow() {
- if (isTaskbarPresent && !isGestureMode) {
- // Align vertically to where nav buttons are.
- return ((taskbarSize - overviewActionsHeight) / 2) + getTaskbarOffsetY();
- }
+ if (isTaskbarPresent) {
+ if (FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get()) {
+ return taskbarSize + transientTaskbarMargin;
+ }
- return isTaskbarPresent ? stashedTaskbarSize : mInsets.bottom;
+ return isGestureMode
+ ? stashedTaskbarSize
+ // Align vertically to where nav buttons are.
+ : ((taskbarSize - overviewActionsHeight) / 2) + getTaskbarOffsetY();
+ }
+ return mInsets.bottom;
}
/** Gets the space that the overview actions will take, including bottom margin. */
@@ -1351,6 +1423,10 @@
return "\t" + name + ": " + value + "px (" + dpiFromPx(value, mMetrics.densityDpi) + "dp)";
}
+ private String dpPointFToString(String name, PointF value) {
+ return String.format(Locale.ENGLISH, "\t%s: PointF(%.1f, %.1f)dp", name, value.x, value.y);
+ }
+
/** Dumps various DeviceProfile variables to the specified writer. */
public void dump(Context context, String prefix, PrintWriter writer) {
writer.println(prefix + "DeviceProfile:");
@@ -1386,7 +1462,7 @@
writer.println(prefix + "\tinv.numSearchContainerColumns: "
+ inv.numSearchContainerColumns);
- writer.println(prefix + "\tminCellSize: " + inv.minCellSize[mTypeIndex] + "dp");
+ writer.println(prefix + dpPointFToString("minCellSize", inv.minCellSize[mTypeIndex]));
writer.println(prefix + pxToDpStr("cellWidthPx", cellWidthPx));
writer.println(prefix + pxToDpStr("cellHeightPx", cellHeightPx));
@@ -1417,17 +1493,23 @@
writer.println(prefix + pxToDpStr("folderChildTextSizePx", folderChildTextSizePx));
writer.println(prefix + pxToDpStr("folderChildDrawablePaddingPx",
folderChildDrawablePaddingPx));
- writer.println(prefix + pxToDpStr("folderCellLayoutBorderSpaceOriginalPx",
- folderCellLayoutBorderSpaceOriginalPx));
- writer.println(prefix + pxToDpStr("folderCellLayoutBorderSpacePx Horizontal",
- folderCellLayoutBorderSpacePx.x));
- writer.println(prefix + pxToDpStr("folderCellLayoutBorderSpacePx Vertical",
- folderCellLayoutBorderSpacePx.y));
+ writer.println(prefix + pxToDpStr("folderCellLayoutBorderSpacePx",
+ folderCellLayoutBorderSpacePx));
+ writer.println(prefix + pxToDpStr("folderContentPaddingLeftRight",
+ folderContentPaddingLeftRight));
+ writer.println(prefix + pxToDpStr("folderTopPadding", folderContentPaddingTop));
+ writer.println(prefix + pxToDpStr("folderFooterHeight", folderFooterHeightPx));
writer.println(prefix + pxToDpStr("bottomSheetTopPadding", bottomSheetTopPadding));
+ writer.println(prefix + "\tbottomSheetOpenDuration: " + bottomSheetOpenDuration);
+ writer.println(prefix + "\tbottomSheetCloseDuration: " + bottomSheetCloseDuration);
+ writer.println(prefix + "\tbottomSheetWorkspaceScale: " + bottomSheetWorkspaceScale);
+ writer.println(prefix + "\tbottomSheetDepth: " + bottomSheetDepth);
writer.println(prefix + pxToDpStr("allAppsShiftRange", allAppsShiftRange));
writer.println(prefix + pxToDpStr("allAppsTopPadding", allAppsTopPadding));
+ writer.println(prefix + "\tallAppsOpenDuration: " + allAppsOpenDuration);
+ writer.println(prefix + "\tallAppsCloseDuration: " + allAppsCloseDuration);
writer.println(prefix + pxToDpStr("allAppsIconSizePx", allAppsIconSizePx));
writer.println(prefix + pxToDpStr("allAppsIconTextSizePx", allAppsIconTextSizePx));
writer.println(prefix + pxToDpStr("allAppsIconDrawablePaddingPx",
@@ -1665,7 +1747,7 @@
mTransposeLayoutWithOrientation = !mInfo.isTablet(mWindowBounds);
}
if (mIsGestureMode == null) {
- mIsGestureMode = DisplayController.getNavigationMode(mContext).hasGestures;
+ mIsGestureMode = mInfo.navigationMode.hasGestures;
}
if (mDotRendererCache == null) {
mDotRendererCache = new SparseArray<>();
diff --git a/src/com/android/launcher3/DropTarget.java b/src/com/android/launcher3/DropTarget.java
index 70d8476..2d99510 100644
--- a/src/com/android/launcher3/DropTarget.java
+++ b/src/com/android/launcher3/DropTarget.java
@@ -20,7 +20,6 @@
import android.graphics.Rect;
import com.android.launcher3.accessibility.DragViewStateAnnouncer;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.dragndrop.DraggableView;
@@ -82,11 +81,8 @@
public final InstanceId logInstanceId = new InstanceIdSequence().newInstanceId();
public DragObject(Context context) {
- if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
- Executors.MODEL_EXECUTOR.post(() -> {
- folderNameProvider = FolderNameProvider.newInstance(context);
- });
- }
+ Executors.MODEL_EXECUTOR.post(() ->
+ folderNameProvider = FolderNameProvider.newInstance(context));
}
/**
diff --git a/src/com/android/launcher3/DropTargetBar.java b/src/com/android/launcher3/DropTargetBar.java
index d64cb26..5225731 100644
--- a/src/com/android/launcher3/DropTargetBar.java
+++ b/src/com/android/launcher3/DropTargetBar.java
@@ -18,6 +18,7 @@
import static com.android.launcher3.ButtonDropTarget.TOOLTIP_DEFAULT;
import static com.android.launcher3.anim.AlphaUpdateListener.updateVisibility;
+import static com.android.launcher3.config.FeatureFlags.HOME_GARDENING_WORKSPACE_BUTTONS;
import android.animation.TimeInterpolator;
import android.content.Context;
@@ -118,7 +119,13 @@
lp.rightMargin = (grid.widthPx - lp.width) / 2;
}
lp.height = grid.dropTargetBarSizePx;
- lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
+ // TODO: Add tablet support for DropTargetBar when HOME_GARDENING_WORKSPACE_BUTTONS flag
+ // is on
+ if (HOME_GARDENING_WORKSPACE_BUTTONS.get()) {
+ lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
+ } else {
+ lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
+ }
DeviceProfile dp = mLauncher.getDeviceProfile();
int horizontalPadding = dp.dropTargetHorizontalPaddingPx;
@@ -151,6 +158,8 @@
int widthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST);
ButtonDropTarget firstButton = mTempTargets[0];
+ firstButton.setTextSize(TypedValue.COMPLEX_UNIT_PX,
+ mLauncher.getDeviceProfile().dropTargetTextSizePx);
firstButton.setTextVisible(true);
firstButton.setIconVisible(true);
firstButton.measure(widthSpec, heightSpec);
@@ -160,14 +169,16 @@
int horizontalPadding = dp.dropTargetHorizontalPaddingPx;
ButtonDropTarget firstButton = mTempTargets[0];
+ firstButton.setTextSize(TypedValue.COMPLEX_UNIT_PX, dp.dropTargetTextSizePx);
firstButton.setTextVisible(true);
firstButton.setIconVisible(true);
firstButton.setTextMultiLine(false);
- // Reset second button padding in case it was previously changed to multi-line text.
+ // Reset first button padding in case it was previously changed to multi-line text.
firstButton.setPadding(horizontalPadding, verticalPadding, horizontalPadding,
verticalPadding);
ButtonDropTarget secondButton = mTempTargets[1];
+ secondButton.setTextSize(TypedValue.COMPLEX_UNIT_PX, dp.dropTargetTextSizePx);
secondButton.setTextVisible(true);
secondButton.setIconVisible(true);
secondButton.setTextMultiLine(false);
@@ -175,20 +186,14 @@
secondButton.setPadding(horizontalPadding, verticalPadding, horizontalPadding,
verticalPadding);
- float scale = dp.getWorkspaceSpringLoadScale(mLauncher);
- int scaledPanelWidth = (int) (dp.getCellLayoutWidth() * scale);
-
int availableWidth;
if (dp.isTwoPanels) {
- // Both buttons for two panel fit to the width of one Cell Layout (less
- // half of the center gap between the buttons).
- int halfButtonGap = dp.dropTargetGapPx / 2;
- availableWidth = scaledPanelWidth - halfButtonGap / 2;
+ // Each button for two panel fits to half the width of the screen excluding the
+ // center gap between the buttons.
+ availableWidth = (dp.availableWidthPx - dp.dropTargetGapPx) / 2;
} else {
- // Both buttons plus the button gap do not display past the edge of the scaled
- // workspace, less a pre-defined gap from the edge of the workspace.
- availableWidth = scaledPanelWidth - dp.dropTargetGapPx
- - 2 * dp.dropTargetButtonWorkspaceEdgeGapPx;
+ // Both buttons plus the button gap do not display past the edge of the screen.
+ availableWidth = dp.availableWidthPx - dp.dropTargetGapPx;
}
int widthSpec = MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST);
@@ -219,6 +224,15 @@
horizontalPadding, verticalPadding / 2);
}
}
+
+ // If text is still truncated, shrink to fit in measured width and resize both targets.
+ float minTextSize =
+ Math.min(firstButton.resizeTextToFit(), secondButton.resizeTextToFit());
+ if (firstButton.getTextSize() != minTextSize
+ || secondButton.getTextSize() != minTextSize) {
+ firstButton.setTextSize(minTextSize);
+ secondButton.setTextSize(minTextSize);
+ }
}
setMeasuredDimension(width, height);
}
diff --git a/src/com/android/launcher3/ExtendedEditText.java b/src/com/android/launcher3/ExtendedEditText.java
index 4629ca7..11f2020 100644
--- a/src/com/android/launcher3/ExtendedEditText.java
+++ b/src/com/android/launcher3/ExtendedEditText.java
@@ -15,7 +15,7 @@
*/
package com.android.launcher3;
-import static com.android.launcher3.util.UiThreadHelper.hideKeyboardAsync;
+import static com.android.launcher3.logging.KeyboardStateManager.KeyboardState.SHOW;
import android.content.Context;
import android.text.TextUtils;
@@ -33,8 +33,6 @@
* Note: AppCompatEditText doesn't fully support #displayCompletions and #onCommitCompletion
*/
public class ExtendedEditText extends EditText {
-
- private boolean mShowImeAfterFirstLayout;
private boolean mForceDisableSuggestions = false;
/**
@@ -85,28 +83,21 @@
return false;
}
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
- if (mShowImeAfterFirstLayout) {
- // soft input only shows one frame after the layout of the EditText happens,
- post(() -> {
- showSoftInput();
- mShowImeAfterFirstLayout = false;
- });
- }
- }
-
-
public void showKeyboard() {
- mShowImeAfterFirstLayout = !showSoftInput();
+ onKeyboardShown();
+ showSoftInput();
}
public void hideKeyboard() {
- hideKeyboardAsync(ActivityContext.lookupContext(getContext()), getWindowToken());
+ ActivityContext.lookupContext(getContext()).hideKeyboard();
clearFocus();
}
+ protected void onKeyboardShown() {
+ ActivityContext.lookupContext(getContext()).getStatsLogManager()
+ .keyboardStateManager().setKeyboardState(SHOW);
+ }
+
private boolean showSoftInput() {
return requestFocus() &&
getContext().getSystemService(InputMethodManager.class)
diff --git a/src/com/android/launcher3/FastScrollRecyclerView.java b/src/com/android/launcher3/FastScrollRecyclerView.java
index 747b755..2f927d3 100644
--- a/src/com/android/launcher3/FastScrollRecyclerView.java
+++ b/src/com/android/launcher3/FastScrollRecyclerView.java
@@ -24,7 +24,6 @@
import android.view.accessibility.AccessibilityNodeInfo;
import androidx.annotation.Nullable;
-import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.android.launcher3.compat.AccessibilityManagerCompat;
@@ -92,8 +91,7 @@
protected int getAvailableScrollHeight() {
// AvailableScrollHeight = Total height of the all items - first page height
int firstPageHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();
- int totalHeightOfAllItems = getItemsHeight(/* untilIndex= */ getAdapter().getItemCount());
- int availableScrollHeight = totalHeightOfAllItems - firstPageHeight;
+ int availableScrollHeight = computeVerticalScrollRange() - firstPageHeight;
return Math.max(0, availableScrollHeight);
}
@@ -146,10 +144,7 @@
// IF scroller is at the very top OR there is no scroll bar because there is probably not
// enough items to scroll, THEN it's okay for the container to be pulled down.
- if (getCurrentScrollY() == 0) {
- return true;
- }
- return getAdapter() == null || getAdapter().getItemCount() == 0;
+ return computeVerticalScrollOffset() == 0;
}
/**
@@ -160,53 +155,6 @@
}
/**
- * @return the scroll top of this recycler view.
- */
- public int getCurrentScrollY() {
- Adapter adapter = getAdapter();
- if (adapter == null) {
- return -1;
- }
- if (adapter.getItemCount() == 0 || getChildCount() == 0) {
- return -1;
- }
-
- int itemPosition = NO_POSITION;
- View child = null;
-
- LayoutManager layoutManager = getLayoutManager();
- if (layoutManager instanceof LinearLayoutManager) {
- // Use the LayoutManager as the source of truth for visible positions. During
- // animations, the view group child may not correspond to the visible views that appear
- // at the top.
- itemPosition = ((LinearLayoutManager) layoutManager).findFirstVisibleItemPosition();
- child = layoutManager.findViewByPosition(itemPosition);
- }
-
- if (child == null) {
- // If the layout manager returns null for any reason, which can happen before layout
- // has occurred for the position, then look at the child of this view as a ViewGroup.
- child = getChildAt(0);
- itemPosition = getChildAdapterPosition(child);
- }
- if (itemPosition == NO_POSITION) {
- return -1;
- }
- return getPaddingTop() + getItemsHeight(itemPosition)
- - layoutManager.getDecoratedTop(child);
- }
-
- /**
- * Returns the sum of the height, in pixels, of this list adapter's items from index
- * 0 (inclusive) until {@code untilIndex} (exclusive). If untilIndex is same as the itemCount,
- * it returns the full height of all the items.
- *
- * <p>If the untilIndex is larger than the total number of items in this adapter, returns the
- * sum of all items' height.
- */
- protected abstract int getItemsHeight(int untilIndex);
-
- /**
* Maps the touch (from 0..1) to the adapter position that should be visible.
* <p>Override in each subclass of this base class.
*/
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index cf2a3f8..9c5ec38 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -17,7 +17,9 @@
package com.android.launcher3;
import static com.android.launcher3.Utilities.dpiFromPx;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_DEVICE_PROFILE_LOGGING;
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;
import static com.android.launcher3.util.DisplayController.CHANGE_SUPPORTED_BOUNDS;
@@ -43,8 +45,10 @@
import android.util.Xml;
import android.view.Display;
+import androidx.annotation.DimenRes;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
+import androidx.annotation.StyleRes;
import androidx.annotation.VisibleForTesting;
import androidx.core.content.res.ResourcesCompat;
@@ -64,6 +68,8 @@
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -127,8 +133,9 @@
public PointF[] minCellSize;
public PointF[] borderSpaces;
- public float folderBorderSpace;
- public int inlineNavButtonsEndSpacing;
+ public @DimenRes int inlineNavButtonsEndSpacing;
+
+ public @StyleRes int folderStyle;
public float[] horizontalMargin;
@@ -255,8 +262,6 @@
COUNT_SIZES);
System.arraycopy(defaultDisplayOption.borderSpaces, 0, result.borderSpaces, 0,
COUNT_SIZES);
- System.arraycopy(defaultDisplayOption.inlineQsb, 0, result.inlineQsb, 0,
- COUNT_SIZES);
initGrid(context, myInfo, result, deviceType);
}
@@ -303,8 +308,7 @@
}
public static String getCurrentGridName(Context context) {
- return Utilities.isGridOptionsEnabled(context)
- ? Utilities.getPrefs(context).getString(KEY_IDP_GRID_NAME, null) : null;
+ return Utilities.getPrefs(context).getString(KEY_IDP_GRID_NAME, null);
}
private String initGrid(Context context, String gridName) {
@@ -320,6 +324,11 @@
return displayOption.grid.name;
}
+ @VisibleForTesting
+ public static String getDefaultGridName(Context context) {
+ return new InvariantDeviceProfile().initGrid(context, null);
+ }
+
private void initGrid(Context context, Info displayInfo, DisplayOption displayOption,
@DeviceType int deviceType) {
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
@@ -330,8 +339,11 @@
dbFile = closestProfile.dbFile;
defaultLayoutId = closestProfile.defaultLayoutId;
demoModeLayoutId = closestProfile.demoModeLayoutId;
+
numFolderRows = closestProfile.numFolderRows;
numFolderColumns = closestProfile.numFolderColumns;
+ folderStyle = closestProfile.folderStyle;
+
isScalable = closestProfile.isScalable;
devicePaddingId = closestProfile.devicePaddingId;
this.deviceType = deviceType;
@@ -353,7 +365,6 @@
minCellSize = displayOption.minCellSize;
borderSpaces = displayOption.borderSpaces;
- folderBorderSpace = displayOption.folderBorderSpace;
horizontalMargin = displayOption.horizontalMargin;
@@ -372,16 +383,12 @@
allAppsBorderSpaces = displayOption.allAppsBorderSpaces;
allAppsIconSize = displayOption.allAppsIconSizes;
allAppsIconTextSize = displayOption.allAppsIconTextSizes;
- if (!Utilities.isGridOptionsEnabled(context)) {
- allAppsIconSize = iconSize;
- allAppsIconTextSize = iconTextSize;
- }
if (devicePaddingId != 0) {
devicePaddings = new DevicePaddings(context, devicePaddingId);
}
- inlineQsb = displayOption.inlineQsb;
+ inlineQsb = closestProfile.inlineQsb;
// If the partner customization apk contains any grid overrides, apply them
// Supported overrides: numRows, numColumns, iconSize
@@ -637,6 +644,18 @@
float screenHeight = config.screenHeightDp * res.getDisplayMetrics().density;
int rotation = WindowManagerProxy.INSTANCE.get(context).getRotation(context);
+ if (Utilities.IS_DEBUG_DEVICE && ENABLE_DEVICE_PROFILE_LOGGING.get()) {
+ StringWriter stringWriter = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(stringWriter);
+ DisplayController.INSTANCE.get(context).dump(printWriter);
+ printWriter.flush();
+ Log.d("b/253338238", "getDeviceProfile -"
+ + "\nconfig: " + config
+ + "\ndisplayMetrics: " + res.getDisplayMetrics()
+ + "\nrotation: " + rotation
+ + "\n" + stringWriter,
+ new Exception());
+ }
return getBestMatch(screenWidth, screenHeight, rotation);
}
@@ -717,6 +736,12 @@
private static final int DEVICE_CATEGORY_ALL =
DEVICE_CATEGORY_PHONE | DEVICE_CATEGORY_TABLET | DEVICE_CATEGORY_MULTI_DISPLAY;
+ private static final int INLINE_QSB_FOR_PORTRAIT = 1 << 0;
+ private static final int INLINE_QSB_FOR_LANDSCAPE = 1 << 1;
+ private static final int INLINE_QSB_FOR_TWO_PANEL_PORTRAIT = 1 << 2;
+ private static final int INLINE_QSB_FOR_TWO_PANEL_LANDSCAPE = 1 << 3;
+ private static final int DONT_INLINE_QSB = 0;
+
public final String name;
public final int numRows;
public final int numColumns;
@@ -725,6 +750,7 @@
private final int numFolderRows;
private final int numFolderColumns;
+ private final @StyleRes int folderStyle;
private final int numAllAppsColumns;
private final int numDatabaseAllAppsColumns;
@@ -733,7 +759,9 @@
private final int[] hotseatColumnSpan = new int[COUNT_SIZES];
- private int inlineNavButtonsEndSpacing;
+ private final boolean[] inlineQsb = new boolean[COUNT_SIZES];
+
+ private @DimenRes int inlineNavButtonsEndSpacing;
private final String dbFile;
private final int defaultLayoutId;
@@ -785,11 +813,15 @@
inlineNavButtonsEndSpacing =
a.getResourceId(R.styleable.GridDisplayOption_inlineNavButtonsEndSpacing,
R.dimen.taskbar_button_margin_default);
+
numFolderRows = a.getInt(
R.styleable.GridDisplayOption_numFolderRows, numRows);
numFolderColumns = a.getInt(
R.styleable.GridDisplayOption_numFolderColumns, numColumns);
+ folderStyle = a.getResourceId(R.styleable.GridDisplayOption_folderStyle,
+ INVALID_RESOURCE_HANDLE);
+
isScalable = a.getBoolean(
R.styleable.GridDisplayOption_isScalable, false);
devicePaddingId = a.getResourceId(
@@ -805,6 +837,19 @@
&& ((deviceCategory & DEVICE_CATEGORY_MULTI_DISPLAY)
== DEVICE_CATEGORY_MULTI_DISPLAY));
+ int inlineForRotation = a.getInt(R.styleable.GridDisplayOption_inlineQsb,
+ DONT_INLINE_QSB);
+ inlineQsb[INDEX_DEFAULT] =
+ (inlineForRotation & INLINE_QSB_FOR_PORTRAIT) == INLINE_QSB_FOR_PORTRAIT;
+ inlineQsb[INDEX_LANDSCAPE] =
+ (inlineForRotation & INLINE_QSB_FOR_LANDSCAPE) == INLINE_QSB_FOR_LANDSCAPE;
+ inlineQsb[INDEX_TWO_PANEL_PORTRAIT] =
+ (inlineForRotation & INLINE_QSB_FOR_TWO_PANEL_PORTRAIT)
+ == INLINE_QSB_FOR_TWO_PANEL_PORTRAIT;
+ inlineQsb[INDEX_TWO_PANEL_LANDSCAPE] =
+ (inlineForRotation & INLINE_QSB_FOR_TWO_PANEL_LANDSCAPE)
+ == INLINE_QSB_FOR_TWO_PANEL_LANDSCAPE;
+
a.recycle();
extraAttrs = Themes.createValueMap(context, attrs,
IntArray.wrap(R.styleable.GridDisplayOption));
@@ -813,22 +858,14 @@
@VisibleForTesting
static final class DisplayOption {
- private static final int INLINE_QSB_FOR_PORTRAIT = 1 << 0;
- private static final int INLINE_QSB_FOR_LANDSCAPE = 1 << 1;
- private static final int INLINE_QSB_FOR_TWO_PANEL_PORTRAIT = 1 << 2;
- private static final int INLINE_QSB_FOR_TWO_PANEL_LANDSCAPE = 1 << 3;
- private static final int DONT_INLINE_QSB = 0;
-
public final GridOption grid;
private final float minWidthDps;
private final float minHeightDps;
private final boolean canBeDefault;
- private final boolean[] inlineQsb = new boolean[COUNT_SIZES];
private final PointF[] minCellSize = new PointF[COUNT_SIZES];
- private float folderBorderSpace;
private final PointF[] borderSpaces = new PointF[COUNT_SIZES];
private final float[] horizontalMargin = new float[COUNT_SIZES];
private final float[] hotseatBarBottomSpace = new float[COUNT_SIZES];
@@ -852,19 +889,6 @@
canBeDefault = a.getBoolean(R.styleable.ProfileDisplayOption_canBeDefault, false);
- int inlineForRotation = a.getInt(R.styleable.ProfileDisplayOption_inlineQsb,
- DONT_INLINE_QSB);
- inlineQsb[INDEX_DEFAULT] =
- (inlineForRotation & INLINE_QSB_FOR_PORTRAIT) == INLINE_QSB_FOR_PORTRAIT;
- inlineQsb[INDEX_LANDSCAPE] =
- (inlineForRotation & INLINE_QSB_FOR_LANDSCAPE) == INLINE_QSB_FOR_LANDSCAPE;
- inlineQsb[INDEX_TWO_PANEL_PORTRAIT] =
- (inlineForRotation & INLINE_QSB_FOR_TWO_PANEL_PORTRAIT)
- == INLINE_QSB_FOR_TWO_PANEL_PORTRAIT;
- inlineQsb[INDEX_TWO_PANEL_LANDSCAPE] =
- (inlineForRotation & INLINE_QSB_FOR_TWO_PANEL_LANDSCAPE)
- == INLINE_QSB_FOR_TWO_PANEL_LANDSCAPE;
-
float x;
float y;
@@ -924,8 +948,6 @@
borderSpaceTwoPanelLandscape);
borderSpaces[INDEX_TWO_PANEL_LANDSCAPE] = new PointF(x, y);
- folderBorderSpace = borderSpace;
-
x = a.getFloat(R.styleable.ProfileDisplayOption_allAppsCellWidth,
minCellSize[INDEX_DEFAULT].x);
y = a.getFloat(R.styleable.ProfileDisplayOption_allAppsCellHeight,
@@ -1006,7 +1028,7 @@
R.styleable.ProfileDisplayOption_allAppsIconSize, iconSizes[INDEX_DEFAULT]);
allAppsIconSizes[INDEX_LANDSCAPE] = a.getFloat(
R.styleable.ProfileDisplayOption_allAppsIconSizeLandscape,
- iconSizes[INDEX_DEFAULT]);
+ allAppsIconSizes[INDEX_DEFAULT]);
allAppsIconSizes[INDEX_TWO_PANEL_PORTRAIT] = a.getFloat(
R.styleable.ProfileDisplayOption_allAppsIconSizeTwoPanelPortrait,
allAppsIconSizes[INDEX_DEFAULT]);
@@ -1097,7 +1119,6 @@
allAppsIconSizes[i] = 0;
allAppsIconTextSizes[i] = 0;
allAppsBorderSpaces[i] = new PointF();
- inlineQsb[i] = false;
}
}
@@ -1120,8 +1141,6 @@
allAppsBorderSpaces[i].y *= w;
}
- folderBorderSpace *= w;
-
return this;
}
@@ -1142,11 +1161,8 @@
allAppsIconTextSizes[i] += p.allAppsIconTextSizes[i];
allAppsBorderSpaces[i].x += p.allAppsBorderSpaces[i].x;
allAppsBorderSpaces[i].y += p.allAppsBorderSpaces[i].y;
- inlineQsb[i] |= p.inlineQsb[i];
}
- folderBorderSpace += p.folderBorderSpace;
-
return this;
}
}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 761f198..578efdf 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -42,6 +42,9 @@
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.SHOW_DELIGHTFUL_PAGINATION;
+import static com.android.launcher3.config.FeatureFlags.SHOW_DOT_PAGINATION;
import static com.android.launcher3.logging.StatsLogManager.EventEnum;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
@@ -50,6 +53,8 @@
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_EXIT;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ONRESUME;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ONSTOP;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SWIPELEFT;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SWIPERIGHT;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGET_RECONFIGURED;
import static com.android.launcher3.model.ItemInstallQueue.FLAG_ACTIVITY_PAUSED;
import static com.android.launcher3.model.ItemInstallQueue.FLAG_DRAG_AND_DROP;
@@ -96,6 +101,7 @@
import android.os.UserHandle;
import android.text.TextUtils;
import android.text.method.TextKeyListener;
+import android.util.AttributeSet;
import android.util.FloatProperty;
import android.util.Log;
import android.util.SparseArray;
@@ -128,6 +134,7 @@
import com.android.launcher3.allapps.AllAppsStore;
import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.allapps.BaseAllAppsContainerView;
+import com.android.launcher3.allapps.BaseSearchConfig;
import com.android.launcher3.allapps.DiscoveryBounce;
import com.android.launcher3.anim.PropertyListBuilder;
import com.android.launcher3.compat.AccessibilityManagerCompat;
@@ -138,6 +145,7 @@
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.dragndrop.LauncherDragController;
+import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderGridOrganizer;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.icons.BitmapRenderer;
@@ -162,6 +170,7 @@
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.notification.NotificationListener;
+import com.android.launcher3.pageindicators.WorkspacePageIndicator;
import com.android.launcher3.pm.PinRequestHelper;
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.popup.ArrowPopup;
@@ -176,7 +185,6 @@
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.touch.AllAppsSwipeController;
-import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.launcher3.util.ActivityResultInfo;
import com.android.launcher3.util.ActivityTracker;
@@ -194,17 +202,15 @@
import com.android.launcher3.util.Thunk;
import com.android.launcher3.util.TouchController;
import com.android.launcher3.util.TraceHelper;
-import com.android.launcher3.util.UiThreadHelper;
-import com.android.launcher3.util.ViewCapture;
import com.android.launcher3.util.ViewOnDrawExecutor;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.FloatingIconView;
import com.android.launcher3.views.FloatingSurfaceView;
import com.android.launcher3.views.OptionsPopupView;
import com.android.launcher3.views.ScrimView;
-import com.android.launcher3.widget.LauncherAppWidgetHost;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.widget.LauncherWidgetHolder;
import com.android.launcher3.widget.PendingAddShortcutInfo;
import com.android.launcher3.widget.PendingAddWidgetInfo;
import com.android.launcher3.widget.PendingAppWidgetHostView;
@@ -218,7 +224,6 @@
import com.android.systemui.plugins.shared.LauncherExterns;
import com.android.systemui.plugins.shared.LauncherOverlayManager;
import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlay;
-import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlayCallbacks;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -238,7 +243,7 @@
*/
public class Launcher extends StatefulActivity<LauncherState>
implements LauncherExterns, Callbacks, InvariantDeviceProfile.OnIDPChangeListener,
- PluginListener<LauncherOverlayPlugin>, LauncherOverlayCallbacks {
+ PluginListener<LauncherOverlayPlugin> {
public static final String TAG = "Launcher";
public static final ActivityTracker<Launcher> ACTIVITY_TRACKER = new ActivityTracker<>();
@@ -314,7 +319,7 @@
DragLayer mDragLayer;
private WidgetManagerHelper mAppWidgetManager;
- private LauncherAppWidgetHost mAppWidgetHost;
+ private LauncherWidgetHolder mAppWidgetHolder;
private final int[] mTmpAddItemCellCoordinates = new int[2];
@@ -393,7 +398,7 @@
private LauncherState mPrevLauncherState;
private StringCache mStringCache;
- private ViewCapture mViewCapture;
+ private BaseSearchConfig mBaseSearchConfig;
@Override
@TargetApi(Build.VERSION_CODES.S)
@@ -478,11 +483,13 @@
mOnboardingPrefs = createOnboardingPrefs(mSharedPrefs);
- mAppWidgetManager = new WidgetManagerHelper(this);
- mAppWidgetHost = createAppWidgetHost();
- mAppWidgetHost.startListening();
+ // TODO: move the SearchConfig to SearchState when new LauncherState is created.
+ mBaseSearchConfig = new BaseSearchConfig();
- inflateRootView(R.layout.launcher);
+ mAppWidgetManager = new WidgetManagerHelper(this);
+ mAppWidgetHolder = createAppWidgetHolder();
+ mAppWidgetHolder.startListening();
+
setupViews();
crossFadeWithPreviousAppearance();
mPopupDataProvider = new PopupDataProvider(this::updateNotificationDots);
@@ -642,14 +649,6 @@
}
/**
- * Called when one handed mode activated and deactivated.
- * @param activated true if one handed mode activated, false otherwise.
- */
- public void onOneHandedStateChanged(boolean activated) {
- mDragLayer.onOneHandedModeStateChanged(activated);
- }
-
- /**
* Returns {@code true} if a new DeviceProfile is initialized, and {@code false} otherwise.
*/
protected boolean initDeviceProfile(InvariantDeviceProfile idp) {
@@ -690,17 +689,9 @@
*/
@Override
public void setLauncherOverlay(LauncherOverlay overlay) {
- if (overlay != null) {
- overlay.setOverlayCallbacks(this);
- }
mWorkspace.setLauncherOverlay(overlay);
}
- @Override
- public void runOnOverlayHidden(Runnable runnable) {
- getWorkspace().runOnOverlayHidden(runnable);
- }
-
public boolean setLauncherCallbacks(LauncherCallbacks callbacks) {
mLauncherCallbacks = callbacks;
return true;
@@ -958,7 +949,7 @@
AppWidgetHostView boundWidget = null;
if (resultCode == RESULT_OK) {
animationType = Workspace.COMPLETE_TWO_STAGE_WIDGET_DROP_ANIMATION;
- final AppWidgetHostView layout = mAppWidgetHost.createView(this, appWidgetId,
+ final AppWidgetHostView layout = mAppWidgetHolder.createView(this, appWidgetId,
requestArgs.getWidgetHandler().getProviderInfo(this));
boundWidget = layout;
onCompleteRunnable = new Runnable() {
@@ -969,7 +960,7 @@
}
};
} else if (resultCode == RESULT_CANCELED) {
- mAppWidgetHost.deleteAppWidgetId(appWidgetId);
+ mAppWidgetHolder.deleteAppWidgetId(appWidgetId);
animationType = Workspace.CANCEL_TWO_STAGE_WIDGET_DROP_ANIMATION;
}
if (mDragLayer.getAnimatedView() != null) {
@@ -992,7 +983,7 @@
}
hideKeyboard();
logStopAndResume(false /* isResume */);
- mAppWidgetHost.setActivityStarted(false);
+ mAppWidgetHolder.setActivityStarted(false);
NotificationListener.removeNotificationsChangedListener(getPopupDataProvider());
}
@@ -1005,7 +996,7 @@
mOverlayManager.onActivityStarted(this);
}
- mAppWidgetHost.setActivityStarted(true);
+ mAppWidgetHolder.setActivityStarted(true);
TraceHelper.INSTANCE.endSection(traceToken);
}
@@ -1025,7 +1016,7 @@
NotificationListener.addNotificationsChangedListener(mPopupDataProvider);
DiscoveryBounce.showForHomeIfNeeded(this);
- mAppWidgetHost.setActivityResumed(true);
+ mAppWidgetHolder.setActivityResumed(true);
}
private void logStopAndResume(boolean isResume) {
@@ -1125,6 +1116,7 @@
.log(getAllAppsEntryEvent().get());
}
}
+ updateDisallowBack();
}
/**
@@ -1139,7 +1131,7 @@
@Override
public void onStateSetEnd(LauncherState state) {
super.onStateSetEnd(state);
- getAppWidgetHost().setStateIsNormal(state == LauncherState.NORMAL);
+ getAppWidgetHolder().setStateIsNormal(state == LauncherState.NORMAL);
getWorkspace().setClipChildren(!state.hasFlag(FLAG_MULTI_PAGE));
finishAutoCancelActionMode();
@@ -1186,7 +1178,6 @@
mOverlayManager.onActivityResumed(this);
}
- AbstractFloatingView.closeAllOpenViewsExcept(this, false, TYPE_REBIND_SAFE);
DragView.removeAllViews(this);
TraceHelper.INSTANCE.endSection(traceToken);
}
@@ -1204,19 +1195,7 @@
if (!mDeferOverlayCallbacks) {
mOverlayManager.onActivityPaused(this);
}
- mAppWidgetHost.setActivityResumed(false);
- }
-
- /**
- * {@code LauncherOverlayCallbacks} scroll amount.
- * Indicates transition progress to -1 screen.
- * @param progress From 0 to 1.
- */
- @Override
- public void onScrollChanged(float progress) {
- if (mWorkspace != null) {
- mWorkspace.onOverlayScrollChanged(progress);
- }
+ mAppWidgetHolder.setActivityResumed(false);
}
/**
@@ -1260,6 +1239,7 @@
* Finds all the views we need and configure them properly.
*/
protected void setupViews() {
+ inflateRootView(R.layout.launcher);
mDragLayer = findViewById(R.id.drag_layer);
mFocusHandler = mDragLayer.getFocusIndicatorHelper();
mWorkspace = mDragLayer.findViewById(R.id.workspace);
@@ -1292,6 +1272,16 @@
mAllAppsController.setupViews(mScrimView, mAppsView);
}
+ @Override
+ public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
+ if ((SHOW_DOT_PAGINATION.get() || SHOW_DELIGHTFUL_PAGINATION.get())
+ && WorkspacePageIndicator.class.getName().equals(name)) {
+ return LayoutInflater.from(context).inflate(R.layout.page_indicator_dots,
+ (ViewGroup) parent, false);
+ }
+ return super.onCreateView(parent, name, context, attrs);
+ }
+
/**
* Creates a view representing a shortcut.
*
@@ -1315,7 +1305,7 @@
BubbleTextView favorite = (BubbleTextView) LayoutInflater.from(parent.getContext())
.inflate(R.layout.app_icon, parent, false);
favorite.applyFromWorkspaceItem(info);
- favorite.setOnClickListener(ItemClickHandler.INSTANCE);
+ favorite.setOnClickListener(getItemOnClickListener());
favorite.setOnFocusChangeListener(mFocusHandler);
return favorite;
}
@@ -1418,7 +1408,7 @@
if (hostView == null) {
// Perform actual inflation because we're live
- hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
+ hostView = mAppWidgetHolder.createView(this, appWidgetId, appWidgetInfo);
}
LauncherAppWidgetInfo launcherInfo;
@@ -1488,14 +1478,6 @@
public void onAttachedToWindow() {
super.onAttachedToWindow();
mOverlayManager.onAttachedToWindow();
- if (FeatureFlags.CONTINUOUS_VIEW_TREE_CAPTURE.get()) {
- View root = getDragLayer().getRootView();
- if (mViewCapture != null) {
- root.getViewTreeObserver().removeOnDrawListener(mViewCapture);
- }
- mViewCapture = new ViewCapture(root);
- mViewCapture.attach();
- }
}
@Override
@@ -1557,12 +1539,12 @@
return mScrimView;
}
- public LauncherAppWidgetHost getAppWidgetHost() {
- return mAppWidgetHost;
+ public LauncherWidgetHolder getAppWidgetHolder() {
+ return mAppWidgetHolder;
}
- protected LauncherAppWidgetHost createAppWidgetHost() {
- return new LauncherAppWidgetHost(this,
+ protected LauncherWidgetHolder createAppWidgetHolder() {
+ return new LauncherWidgetHolder(this,
appWidgetId -> getWorkspace().removeWidget(appWidgetId));
}
@@ -1588,6 +1570,10 @@
return mOldConfig.orientation;
}
+ public BaseSearchConfig getSearchConfig() {
+ return mBaseSearchConfig;
+ }
+
@Override
protected void onNewIntent(Intent intent) {
if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
@@ -1605,7 +1591,6 @@
&& AbstractFloatingView.getTopOpenView(this) == null;
boolean isActionMain = Intent.ACTION_MAIN.equals(intent.getAction());
boolean internalStateHandled = ACTIVITY_TRACKER.handleNewIntent(this);
- hideKeyboard();
if (isActionMain) {
if (!internalStateHandled) {
@@ -1663,16 +1648,6 @@
}
}
- /**
- * Hides the keyboard if visible
- */
- public void hideKeyboard() {
- final View v = getWindow().peekDecorView();
- if (v != null && v.getWindowToken() != null) {
- UiThreadHelper.hideKeyboardAsync(this, v.getWindowToken());
- }
- }
-
@Override
public void onRestoreInstanceState(Bundle state) {
super.onRestoreInstanceState(state);
@@ -1702,6 +1677,10 @@
outState.remove(RUNTIME_STATE_WIDGET_PANEL);
}
+ // We close any open folders and shortcut containers that are not safe for rebind,
+ // and we need to make sure this state is reflected.
+ AbstractFloatingView.closeAllOpenViewsExcept(
+ this, isStarted() && !isForceInvisible(), TYPE_REBIND_SAFE);
finishAutoCancelActionMode();
if (mPendingRequestArgs != null) {
@@ -1730,10 +1709,11 @@
mRotationHelper.destroy();
try {
- mAppWidgetHost.stopListening();
+ mAppWidgetHolder.stopListening();
} catch (NullPointerException ex) {
Log.w(TAG, "problem while stopping AppWidgetHost during Launcher destruction", ex);
}
+ mAppWidgetHolder.destroy();
TextKeyListener.getInstance().release();
clearPendingBinds();
@@ -1905,7 +1885,7 @@
appWidgetId = CustomWidgetManager.INSTANCE.get(this).getWidgetIdForCustomProvider(
info.componentName);
} else {
- appWidgetId = getAppWidgetHost().allocateAppWidgetId();
+ appWidgetId = getAppWidgetHolder().allocateAppWidgetId();
}
Bundle options = info.bindOptions;
@@ -2019,7 +1999,7 @@
final LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo) itemInfo;
mWorkspace.removeWorkspaceItem(v);
if (deleteFromDb) {
- getModelWriter().deleteWidgetInfo(widgetInfo, getAppWidgetHost(), reason);
+ getModelWriter().deleteWidgetInfo(widgetInfo, getAppWidgetHolder(), reason);
}
} else {
return false;
@@ -2277,7 +2257,7 @@
mWorkspace.clearDropTargets();
mWorkspace.removeAllWorkspaceScreens();
- mAppWidgetHost.clearViews();
+ mAppWidgetHolder.clearViews();
if (mHotseat != null) {
mHotseat.resetLayout(getDeviceProfile().isVerticalBarLayout());
@@ -2584,7 +2564,7 @@
if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) {
if (!item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_ALLOCATED)) {
// Id has not been allocated yet. Allocate a new id.
- item.appWidgetId = mAppWidgetHost.allocateAppWidgetId();
+ item.appWidgetId = mAppWidgetHolder.allocateAppWidgetId();
item.restoreStatus |= LauncherAppWidgetInfo.FLAG_ID_ALLOCATED;
// Also try to bind the widget. If the bind fails, the user will be shown
@@ -2646,18 +2626,18 @@
// Verify that we own the widget
if (appWidgetInfo == null) {
FileLog.e(TAG, "Removing invalid widget: id=" + item.appWidgetId);
- getModelWriter().deleteWidgetInfo(item, getAppWidgetHost(), removalReason);
+ getModelWriter().deleteWidgetInfo(item, getAppWidgetHolder(), removalReason);
return null;
}
item.minSpanX = appWidgetInfo.minSpanX;
item.minSpanY = appWidgetInfo.minSpanY;
- view = mAppWidgetHost.createView(this, item.appWidgetId, appWidgetInfo);
+ view = mAppWidgetHolder.createView(this, item.appWidgetId, appWidgetInfo);
} else if (!item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)
&& appWidgetInfo != null) {
- mAppWidgetHost.addPendingView(item.appWidgetId,
+ mAppWidgetHolder.addPendingView(item.appWidgetId,
new PendingAppWidgetHostView(this, item, mIconCache, false));
- view = mAppWidgetHost.createView(this, item.appWidgetId, appWidgetInfo);
+ view = mAppWidgetHolder.createView(this, item.appWidgetId, appWidgetInfo);
} else {
view = new PendingAppWidgetHostView(this, item, mIconCache, false);
}
@@ -2761,6 +2741,8 @@
getViewCache().setCacheSize(R.layout.folder_page, 2);
TraceHelper.INSTANCE.endSection(traceToken);
+
+ mWorkspace.removeExtraEmptyScreen(true);
}
private boolean canAnimatePageChange() {
@@ -2799,7 +2781,7 @@
View v = getFirstMatch(Collections.singletonList(activeRecyclerView),
preferredItem, packageAndUserAndApp);
- if (v != null && activeRecyclerView.getCurrentScrollY() > 0) {
+ if (v != null && activeRecyclerView.computeVerticalScrollOffset() > 0) {
RectF locationBounds = new RectF();
FloatingIconView.getLocationBoundsForView(this, v, false, locationBounds,
new Rect());
@@ -2810,16 +2792,30 @@
}
return v;
- } else {
- List<ViewGroup> containers = new ArrayList<>(mWorkspace.getPanelCount() + 1);
- containers.add(mWorkspace.getHotseat().getShortcutsAndWidgets());
- mWorkspace.forEachVisiblePage(page
- -> containers.add(((CellLayout) page).getShortcutsAndWidgets()));
-
- // Order: Preferred item by itself or in folder, then by matching package/user
- return getFirstMatch(containers, preferredItem, forFolderMatch(preferredItem),
- packageAndUserAndApp, forFolderMatch(packageAndUserAndApp));
}
+
+ // Look for the item inside the folder at the current page
+ Folder folder = Folder.getOpen(this);
+ if (folder != null) {
+ View v = getFirstMatch(Collections.singletonList(
+ folder.getContent().getCurrentCellLayout().getShortcutsAndWidgets()),
+ preferredItem,
+ packageAndUserAndApp);
+ if (v == null) {
+ folder.close(isStarted() && !isForceInvisible());
+ } else {
+ return v;
+ }
+ }
+
+ List<ViewGroup> containers = new ArrayList<>(mWorkspace.getPanelCount() + 1);
+ containers.add(mWorkspace.getHotseat().getShortcutsAndWidgets());
+ mWorkspace.forEachVisiblePage(page
+ -> containers.add(((CellLayout) page).getShortcutsAndWidgets()));
+
+ // Order: Preferred item by itself or in folder, then by matching package/user
+ return getFirstMatch(containers, preferredItem, forFolderMatch(preferredItem),
+ packageAndUserAndApp, forFolderMatch(packageAndUserAndApp));
}
/**
@@ -2872,7 +2868,16 @@
/**
* Informs us that the overlay (-1 screen, typically), has either become visible or invisible.
*/
- public void onOverlayVisibilityChanged(boolean visible) {}
+ public void onOverlayVisibilityChanged(boolean visible) {
+ getStatsLogManager().logger()
+ .withSrcState(LAUNCHER_STATE_HOME)
+ .withDstState(LAUNCHER_STATE_HOME)
+ .withContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
+ .setWorkspace(WorkspaceContainer.newBuilder()
+ .setPageIndex(visible ? 0 : -1))
+ .build())
+ .log(visible ? LAUNCHER_SWIPELEFT : LAUNCHER_SWIPERIGHT);
+ }
/**
* Informs us that the page transition has ended, so that we can react to the newly selected
@@ -3013,14 +3018,8 @@
writer.println(prefix + "\tmPendingRequestArgs=" + mPendingRequestArgs
+ " mPendingActivityResult=" + mPendingActivityResult);
writer.println(prefix + "\tmRotationHelper: " + mRotationHelper);
- writer.println(prefix + "\tmAppWidgetHost.isListening: " + mAppWidgetHost.isListening());
-
- if (mViewCapture != null) {
- writer.print(prefix + "\tmViewCapture: ");
- writer.flush();
- mViewCapture.dump(fd);
- writer.println();
- }
+ writer.println(prefix + "\tmAppWidgetHolder.isListening: "
+ + mAppWidgetHolder.isListening());
// Extra logging for general debugging
mDragLayer.dump(prefix, writer);
@@ -3150,7 +3149,18 @@
public void useFadeOutAnimationForLauncherStart(CancellationSignal signal) { }
- public void onDragLayerHierarchyChanged() { }
+ public void onDragLayerHierarchyChanged() {
+ updateDisallowBack();
+ }
+
+ private void updateDisallowBack() {
+ LauncherRootView rv = getRootView();
+ if (rv != null) {
+ boolean disableBack = getStateManager().getState() == NORMAL
+ && AbstractFloatingView.getTopOpenView(this) == null;
+ rv.setDisallowBackGesture(disableBack);
+ }
+ }
@Override
public void returnToHomescreen() {
@@ -3250,12 +3260,10 @@
* @param progress Transition progress from 0 to 1; where 0 => home and 1 => widgets.
*/
public void onWidgetsTransition(float progress) {
- if (mDeviceProfile.isTablet) {
- float scale =
- Utilities.comp(Utilities.comp(mDeviceProfile.workspaceContentScale) * progress);
- WORKSPACE_WIDGET_SCALE.set(getWorkspace(), scale);
- HOTSEAT_WIDGET_SCALE.set(getHotseat(), scale);
- }
+ float scale = Utilities.mapToRange(progress, 0f, 1f, 1f,
+ mDeviceProfile.bottomSheetWorkspaceScale, EMPHASIZED);
+ WORKSPACE_WIDGET_SCALE.set(getWorkspace(), scale);
+ HOTSEAT_WIDGET_SCALE.set(getHotseat(), scale);
}
private static class NonConfigInstance {
diff --git a/src/com/android/launcher3/LauncherAnimUtils.java b/src/com/android/launcher3/LauncherAnimUtils.java
index b858d1a..4e80d41 100644
--- a/src/com/android/launcher3/LauncherAnimUtils.java
+++ b/src/com/android/launcher3/LauncherAnimUtils.java
@@ -218,4 +218,32 @@
}
};
}
+
+ /**
+ * A property that updates the specified property within a given range of values (ie. even if
+ * the animator goes beyond 0..1, the interpolated value will still be bounded).
+ * @param <T> the specified property
+ */
+ public static class ClampedProperty<T> extends FloatProperty<T> {
+ private final FloatProperty<T> mProperty;
+ private final float mMinValue;
+ private final float mMaxValue;
+
+ public ClampedProperty(FloatProperty<T> property, float minValue, float maxValue) {
+ super(property.getName() + "Clamped");
+ mProperty = property;
+ mMinValue = minValue;
+ mMaxValue = maxValue;
+ }
+
+ @Override
+ public void setValue(T t, float v) {
+ mProperty.set(t, Utilities.boundToRange(v, mMinValue, mMaxValue));
+ }
+
+ @Override
+ public Float get(T t) {
+ return mProperty.get(t);
+ }
+ }
}
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 597bc8d..ea3f723 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -19,7 +19,6 @@
import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_RESOURCE_UPDATED;
import static com.android.launcher3.Utilities.getDevicePrefs;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_THEMED_ICONS;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import static com.android.launcher3.util.SettingsCache.NOTIFICATION_BADGING_URI;
@@ -118,12 +117,10 @@
observer, MODEL_EXECUTOR.getHandler());
mOnTerminateCallback.add(iconChangeTracker::close);
MODEL_EXECUTOR.execute(observer::verifyIconChanged);
- if (ENABLE_THEMED_ICONS.get()) {
- SharedPreferences prefs = Utilities.getPrefs(mContext);
- prefs.registerOnSharedPreferenceChangeListener(observer);
- mOnTerminateCallback.add(
- () -> prefs.unregisterOnSharedPreferenceChangeListener(observer));
- }
+ SharedPreferences prefs = Utilities.getPrefs(mContext);
+ prefs.registerOnSharedPreferenceChangeListener(observer);
+ mOnTerminateCallback.add(
+ () -> prefs.unregisterOnSharedPreferenceChangeListener(observer));
InstallSessionTracker installSessionTracker =
InstallSessionHelper.INSTANCE.get(context).registerInstallTracker(mModel);
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index f790789..20df897 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -33,6 +33,7 @@
import android.util.Log;
import android.util.Pair;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
@@ -89,9 +90,11 @@
static final String TAG = "Launcher.Model";
+ @NonNull
private final LauncherAppState mApp;
+ @NonNull
private final Object mLock = new Object();
-
+ @Nullable
private LoaderTask mLoaderTask;
private boolean mIsLoaderTaskRunning;
@@ -107,20 +110,25 @@
}
}
+ @NonNull
private final ArrayList<Callbacks> mCallbacksList = new ArrayList<>(1);
// < only access in worker thread >
+ @NonNull
private final AllAppsList mBgAllAppsList;
/**
* All the static data should be accessed on the background thread, A lock should be acquired
* on this object when accessing any data from this model.
*/
+ @NonNull
private final BgDataModel mBgDataModel = new BgDataModel();
+ @NonNull
private final ModelDelegate mModelDelegate;
// Runnable to check if the shortcuts permission has changed.
+ @NonNull
private final Runnable mDataValidationCheck = new Runnable() {
@Override
public void run() {
@@ -130,14 +138,16 @@
}
};
- LauncherModel(Context context, LauncherAppState app, IconCache iconCache, AppFilter appFilter,
- boolean isPrimaryInstance) {
+ LauncherModel(@NonNull final Context context, @NonNull final LauncherAppState app,
+ @NonNull final IconCache iconCache, @NonNull final AppFilter appFilter,
+ final boolean isPrimaryInstance) {
mApp = app;
mBgAllAppsList = new AllAppsList(iconCache, appFilter);
mModelDelegate = ModelDelegate.newInstance(context, app, mBgAllAppsList, mBgDataModel,
isPrimaryInstance);
}
+ @NonNull
public ModelDelegate getModelDelegate() {
return mModelDelegate;
}
@@ -145,52 +155,57 @@
/**
* Adds the provided items to the workspace.
*/
- public void addAndBindAddedWorkspaceItems(List<Pair<ItemInfo, Object>> itemList) {
+ public void addAndBindAddedWorkspaceItems(
+ @NonNull final List<Pair<ItemInfo, Object>> itemList) {
for (Callbacks cb : getCallbacks()) {
cb.preAddApps();
}
enqueueModelUpdateTask(new AddWorkspaceItemsTask(itemList));
}
- public ModelWriter getWriter(boolean hasVerticalHotseat, boolean verifyChanges,
- @Nullable Callbacks owner) {
+ @NonNull
+ public ModelWriter getWriter(final boolean hasVerticalHotseat, final boolean verifyChanges,
+ @Nullable final Callbacks owner) {
return new ModelWriter(mApp.getContext(), this, mBgDataModel,
hasVerticalHotseat, verifyChanges, owner);
}
@Override
- public void onPackageChanged(String packageName, UserHandle user) {
+ public void onPackageChanged(
+ @NonNull final String packageName, @NonNull final UserHandle user) {
int op = PackageUpdatedTask.OP_UPDATE;
enqueueModelUpdateTask(new PackageUpdatedTask(op, user, packageName));
}
@Override
- public void onPackageRemoved(String packageName, UserHandle user) {
+ public void onPackageRemoved(
+ @NonNull final String packageName, @NonNull final UserHandle user) {
onPackagesRemoved(user, packageName);
}
- public void onPackagesRemoved(UserHandle user, String... packages) {
+ public void onPackagesRemoved(
+ @NonNull final UserHandle user, @NonNull final String... packages) {
int op = PackageUpdatedTask.OP_REMOVE;
FileLog.d(TAG, "package removed received " + TextUtils.join(",", packages));
enqueueModelUpdateTask(new PackageUpdatedTask(op, user, packages));
}
@Override
- public void onPackageAdded(String packageName, UserHandle user) {
+ public void onPackageAdded(@NonNull final String packageName, @NonNull final UserHandle user) {
int op = PackageUpdatedTask.OP_ADD;
enqueueModelUpdateTask(new PackageUpdatedTask(op, user, packageName));
}
@Override
- public void onPackagesAvailable(String[] packageNames, UserHandle user,
- boolean replacing) {
+ public void onPackagesAvailable(@NonNull final String[] packageNames,
+ @NonNull final UserHandle user, final boolean replacing) {
enqueueModelUpdateTask(
new PackageUpdatedTask(PackageUpdatedTask.OP_UPDATE, user, packageNames));
}
@Override
- public void onPackagesUnavailable(String[] packageNames, UserHandle user,
- boolean replacing) {
+ public void onPackagesUnavailable(@NonNull final String[] packageNames,
+ @NonNull final UserHandle user, final boolean replacing) {
if (!replacing) {
enqueueModelUpdateTask(new PackageUpdatedTask(
PackageUpdatedTask.OP_UNAVAILABLE, user, packageNames));
@@ -198,20 +213,22 @@
}
@Override
- public void onPackagesSuspended(String[] packageNames, UserHandle user) {
+ public void onPackagesSuspended(
+ @NonNull final String[] packageNames, @NonNull final UserHandle user) {
enqueueModelUpdateTask(new PackageUpdatedTask(
PackageUpdatedTask.OP_SUSPEND, user, packageNames));
}
@Override
- public void onPackagesUnsuspended(String[] packageNames, UserHandle user) {
+ public void onPackagesUnsuspended(
+ @NonNull final String[] packageNames, @NonNull final UserHandle user) {
enqueueModelUpdateTask(new PackageUpdatedTask(
PackageUpdatedTask.OP_UNSUSPEND, user, packageNames));
}
@Override
- public void onPackageLoadingProgressChanged(
- String packageName, UserHandle user, float progress) {
+ public void onPackageLoadingProgressChanged(@NonNull final String packageName,
+ @NonNull final UserHandle user, final float progress) {
if (Utilities.ATLEAST_S) {
enqueueModelUpdateTask(new PackageIncrementalDownloadUpdatedTask(
packageName, user, progress));
@@ -219,8 +236,8 @@
}
@Override
- public void onShortcutsChanged(String packageName, List<ShortcutInfo> shortcuts,
- UserHandle user) {
+ public void onShortcutsChanged(@NonNull final String packageName,
+ @NonNull final List<ShortcutInfo> shortcuts, @NonNull final UserHandle user) {
enqueueModelUpdateTask(new ShortcutsChangedTask(packageName, shortcuts, user, true));
}
@@ -228,7 +245,8 @@
* Called when the icon for an app changes, outside of package event
*/
@WorkerThread
- public void onAppIconChanged(String packageName, UserHandle user) {
+ public void onAppIconChanged(@NonNull final String packageName,
+ @NonNull final UserHandle user) {
// Update the icon for the calendar package
Context context = mApp.getContext();
onPackageChanged(packageName, user);
@@ -256,7 +274,7 @@
MODEL_EXECUTOR.execute(mModelDelegate::destroy);
}
- public void onBroadcastIntent(Intent intent) {
+ public void onBroadcastIntent(@NonNull final Intent intent) {
if (DEBUG_RECEIVER) Log.d(TAG, "onReceive intent=" + intent);
final String action = intent.getAction();
if (Intent.ACTION_LOCALE_CHANGED.equals(action)) {
@@ -322,7 +340,7 @@
/**
* Removes an existing callback
*/
- public void removeCallbacks(Callbacks callbacks) {
+ public void removeCallbacks(@NonNull final Callbacks callbacks) {
synchronized (mCallbacksList) {
Preconditions.assertUIThread();
if (mCallbacksList.remove(callbacks)) {
@@ -338,7 +356,7 @@
* Adds a callbacks to receive model updates
* @return true if workspace load was performed synchronously
*/
- public boolean addCallbacksAndLoad(Callbacks callbacks) {
+ public boolean addCallbacksAndLoad(@NonNull final Callbacks callbacks) {
synchronized (mLock) {
addCallbacks(callbacks);
return startLoader(new Callbacks[] { callbacks });
@@ -349,7 +367,7 @@
/**
* Adds a callbacks to receive model updates
*/
- public void addCallbacks(Callbacks callbacks) {
+ public void addCallbacks(@NonNull final Callbacks callbacks) {
Preconditions.assertUIThread();
synchronized (mCallbacksList) {
if (TestProtocol.sDebugTracing) {
@@ -370,7 +388,7 @@
return startLoader(new Callbacks[0]);
}
- private boolean startLoader(Callbacks[] newCallbacks) {
+ private boolean startLoader(@NonNull final Callbacks[] newCallbacks) {
// Enable queue before starting loader. It will get disabled in Launcher#finishBindingItems
ItemInstallQueue.INSTANCE.get(mApp.getContext())
.pauseModelPush(ItemInstallQueue.FLAG_LOADER_RUNNING);
@@ -433,7 +451,7 @@
* Loads the model if not loaded
* @param callback called with the data model upon successful load or null on model thread.
*/
- public void loadAsync(Consumer<BgDataModel> callback) {
+ public void loadAsync(@NonNull final Consumer<BgDataModel> callback) {
synchronized (mLock) {
if (!mModelLoaded && !mIsLoaderTaskRunning) {
startLoader();
@@ -443,11 +461,12 @@
}
@Override
- public void onInstallSessionCreated(final PackageInstallInfo sessionInfo) {
+ public void onInstallSessionCreated(@NonNull final PackageInstallInfo sessionInfo) {
if (FeatureFlags.PROMISE_APPS_IN_ALL_APPS.get()) {
enqueueModelUpdateTask(new BaseModelUpdateTask() {
@Override
- public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+ public void execute(@NonNull final LauncherAppState app,
+ @NonNull final BgDataModel dataModel, @NonNull final AllAppsList apps) {
apps.addPromiseApp(app.getContext(), sessionInfo);
bindApplicationsIfNeeded();
}
@@ -456,13 +475,12 @@
}
@Override
- public void onSessionFailure(String packageName, UserHandle user) {
- if (!FeatureFlags.PROMISE_APPS_NEW_INSTALLS.get()) {
- return;
- }
+ public void onSessionFailure(@NonNull final String packageName,
+ @NonNull final UserHandle user) {
enqueueModelUpdateTask(new BaseModelUpdateTask() {
@Override
- public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+ public void execute(@NonNull final LauncherAppState app,
+ @NonNull final BgDataModel dataModel, @NonNull final AllAppsList apps) {
final IntSet removedIds = new IntSet();
synchronized (dataModel) {
for (ItemInfo info : dataModel.itemsIdMap) {
@@ -486,7 +504,7 @@
}
@Override
- public void onPackageStateChanged(PackageInstallInfo installInfo) {
+ public void onPackageStateChanged(@NonNull final PackageInstallInfo installInfo) {
enqueueModelUpdateTask(new PackageInstallStateChangedTask(installInfo));
}
@@ -494,7 +512,8 @@
* Updates the icons and label of all pending icons for the provided package name.
*/
@Override
- public void onUpdateSessionDisplay(PackageUserKey key, PackageInstaller.SessionInfo info) {
+ public void onUpdateSessionDisplay(@NonNull final PackageUserKey key,
+ @NonNull final PackageInstaller.SessionInfo info) {
mApp.getIconCache().updateSessionCache(key, info);
HashSet<String> packages = new HashSet<>();
@@ -505,9 +524,10 @@
public class LoaderTransaction implements AutoCloseable {
+ @NonNull
private final LoaderTask mTask;
- private LoaderTransaction(LoaderTask task) throws CancellationException {
+ private LoaderTransaction(@NonNull final LoaderTask task) throws CancellationException {
synchronized (mLock) {
if (mLoaderTask != task) {
throw new CancellationException("Loader already stopped");
@@ -537,7 +557,8 @@
}
}
- public LoaderTransaction beginLoader(LoaderTask task) throws CancellationException {
+ public LoaderTransaction beginLoader(@NonNull final LoaderTask task)
+ throws CancellationException {
return new LoaderTransaction(task);
}
@@ -554,7 +575,8 @@
/**
* Called when the icons for packages have been updated in the icon cache.
*/
- public void onPackageIconsUpdated(HashSet<String> updatedPackages, UserHandle user) {
+ public void onPackageIconsUpdated(@NonNull final HashSet<String> updatedPackages,
+ @NonNull final UserHandle user) {
// If any package icon has changed (app was updated while launcher was dead),
// update the corresponding shortcuts.
enqueueModelUpdateTask(new CacheDataUpdatedTask(
@@ -564,17 +586,19 @@
/**
* Called when the labels for the widgets has updated in the icon cache.
*/
- public void onWidgetLabelsUpdated(HashSet<String> updatedPackages, UserHandle user) {
+ public void onWidgetLabelsUpdated(@NonNull final HashSet<String> updatedPackages,
+ @NonNull final UserHandle user) {
enqueueModelUpdateTask(new BaseModelUpdateTask() {
@Override
- public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+ public void execute(@NonNull final LauncherAppState app,
+ @NonNull final BgDataModel dataModel, @NonNull final AllAppsList apps) {
dataModel.widgetsModel.onPackageIconsUpdated(updatedPackages, user, app);
bindUpdatedWidgets(dataModel);
}
});
}
- public void enqueueModelUpdateTask(ModelUpdateTask task) {
+ public void enqueueModelUpdateTask(@NonNull final ModelUpdateTask task) {
if (mModelDestroyed) {
return;
}
@@ -588,7 +612,7 @@
*/
public interface CallbackTask {
- void execute(Callbacks callbacks);
+ void execute(@NonNull Callbacks callbacks);
}
/**
@@ -599,12 +623,14 @@
/**
* Called before the task is posted to initialize the internal state.
*/
- void init(LauncherAppState app, LauncherModel model,
- BgDataModel dataModel, AllAppsList allAppsList, Executor uiExecutor);
+ void init(@NonNull LauncherAppState app, @NonNull LauncherModel model,
+ @NonNull BgDataModel dataModel, @NonNull AllAppsList allAppsList,
+ @NonNull Executor uiExecutor);
}
- public void updateAndBindWorkspaceItem(WorkspaceItemInfo si, ShortcutInfo info) {
+ public void updateAndBindWorkspaceItem(@NonNull final WorkspaceItemInfo si,
+ @NonNull final ShortcutInfo info) {
updateAndBindWorkspaceItem(() -> {
si.updateFromDeepShortcutInfo(info, mApp.getContext());
mApp.getIconCache().getShortcutIcon(si, info);
@@ -615,10 +641,12 @@
/**
* Utility method to update a shortcut on the background thread.
*/
- public void updateAndBindWorkspaceItem(final Supplier<WorkspaceItemInfo> itemProvider) {
+ public void updateAndBindWorkspaceItem(
+ @NonNull final Supplier<WorkspaceItemInfo> itemProvider) {
enqueueModelUpdateTask(new BaseModelUpdateTask() {
@Override
- public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+ public void execute(@NonNull final LauncherAppState app,
+ @NonNull final BgDataModel dataModel, @NonNull final AllAppsList apps) {
WorkspaceItemInfo info = itemProvider.get();
getModelWriter().updateItemInDatabase(info);
ArrayList<WorkspaceItemInfo> update = new ArrayList<>();
@@ -631,14 +659,16 @@
public void refreshAndBindWidgetsAndShortcuts(@Nullable final PackageUserKey packageUser) {
enqueueModelUpdateTask(new BaseModelUpdateTask() {
@Override
- public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+ public void execute(@NonNull final LauncherAppState app,
+ @NonNull final BgDataModel dataModel, @NonNull final AllAppsList apps) {
dataModel.widgetsModel.update(app, packageUser);
bindUpdatedWidgets(dataModel);
}
});
}
- public void dumpState(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+ public void dumpState(@Nullable final String prefix, @Nullable final FileDescriptor fd,
+ @NonNull final PrintWriter writer, @NonNull final String[] args) {
if (args.length > 0 && TextUtils.equals(args[0], "--all")) {
writer.println(prefix + "All apps list: size=" + mBgAllAppsList.data.size());
for (AppInfo info : mBgAllAppsList.data) {
@@ -664,6 +694,7 @@
/**
* Returns an array of currently attached callbacks
*/
+ @NonNull
public Callbacks[] getCallbacks() {
synchronized (mCallbacksList) {
return mCallbacksList.toArray(new Callbacks[mCallbacksList.size()]);
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index a20ff8c..457e126 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -22,7 +22,6 @@
import android.annotation.TargetApi;
import android.app.backup.BackupManager;
-import android.appwidget.AppWidgetHost;
import android.appwidget.AppWidgetManager;
import android.content.ComponentName;
import android.content.ContentProvider;
@@ -55,6 +54,8 @@
import android.util.Log;
import android.util.Xml;
+import androidx.annotation.NonNull;
+
import com.android.launcher3.AutoInstallsLayout.LayoutParserCallback;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.config.FeatureFlags;
@@ -70,7 +71,7 @@
import com.android.launcher3.util.NoLocaleSQLiteHelper;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.Thunk;
-import com.android.launcher3.widget.LauncherAppWidgetHost;
+import com.android.launcher3.widget.LauncherWidgetHolder;
import org.xmlpull.v1.XmlPullParser;
@@ -85,6 +86,7 @@
import java.util.Locale;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
+import java.util.stream.Collectors;
public class LauncherProvider extends ContentProvider {
private static final String TAG = "LauncherProvider";
@@ -254,17 +256,20 @@
values.getAsString(Favorites.APPWIDGET_PROVIDER));
if (cn != null) {
+ LauncherWidgetHolder widgetHolder = mOpenHelper.newLauncherWidgetHolder();
try {
- AppWidgetHost widgetHost = mOpenHelper.newLauncherWidgetHost();
- int appWidgetId = widgetHost.allocateAppWidgetId();
+ int appWidgetId = widgetHolder.allocateAppWidgetId();
values.put(LauncherSettings.Favorites.APPWIDGET_ID, appWidgetId);
if (!appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId,cn)) {
- widgetHost.deleteAppWidgetId(appWidgetId);
+ widgetHolder.deleteAppWidgetId(appWidgetId);
return false;
}
} catch (RuntimeException e) {
Log.e(TAG, "Failed to initialize external widget", e);
return false;
+ } finally {
+ // Necessary to destroy the holder to free up possible activity context
+ widgetHolder.destroy();
}
} else {
return false;
@@ -532,10 +537,10 @@
if (sp.getBoolean(mOpenHelper.getKey(EMPTY_DATABASE_CREATED), false)) {
Log.d(TAG, "loading default workspace");
- AppWidgetHost widgetHost = mOpenHelper.newLauncherWidgetHost();
- AutoInstallsLayout loader = createWorkspaceLoaderFromAppRestriction(widgetHost);
+ LauncherWidgetHolder widgetHolder = mOpenHelper.newLauncherWidgetHolder();
+ AutoInstallsLayout loader = createWorkspaceLoaderFromAppRestriction(widgetHolder);
if (loader == null) {
- loader = AutoInstallsLayout.get(getContext(),widgetHost, mOpenHelper);
+ loader = AutoInstallsLayout.get(getContext(), widgetHolder, mOpenHelper);
}
if (loader == null) {
final Partner partner = Partner.get(getContext().getPackageManager());
@@ -544,7 +549,7 @@
int workspaceResId = partnerRes.getIdentifier(Partner.RES_DEFAULT_LAYOUT,
"xml", partner.getPackageName());
if (workspaceResId != 0) {
- loader = new DefaultLayoutParser(getContext(), widgetHost,
+ loader = new DefaultLayoutParser(getContext(), widgetHolder,
mOpenHelper, partnerRes, workspaceResId);
}
}
@@ -552,7 +557,7 @@
final boolean usingExternallyProvidedLayout = loader != null;
if (loader == null) {
- loader = getDefaultLayoutParser(widgetHost);
+ loader = getDefaultLayoutParser(widgetHolder);
}
// There might be some partially restored DB items, due to buggy restore logic in
@@ -564,9 +569,10 @@
// Unable to load external layout. Cleanup and load the internal layout.
mOpenHelper.createEmptyDB(mOpenHelper.getWritableDatabase());
mOpenHelper.loadFavorites(mOpenHelper.getWritableDatabase(),
- getDefaultLayoutParser(widgetHost));
+ getDefaultLayoutParser(widgetHolder));
}
clearFlagEmptyDbCreated();
+ widgetHolder.destroy();
}
}
@@ -575,7 +581,8 @@
*
* @return the loader if the restrictions are set and the resource exists; null otherwise.
*/
- private AutoInstallsLayout createWorkspaceLoaderFromAppRestriction(AppWidgetHost widgetHost) {
+ private AutoInstallsLayout createWorkspaceLoaderFromAppRestriction(
+ LauncherWidgetHolder widgetHolder) {
Context ctx = getContext();
final String authority;
if (!TextUtils.isEmpty(mProviderAuthority)) {
@@ -601,7 +608,7 @@
parser.setInput(new StringReader(layout));
Log.d(TAG, "Loading layout from " + authority);
- return new AutoInstallsLayout(ctx, widgetHost, mOpenHelper,
+ return new AutoInstallsLayout(ctx, widgetHolder, mOpenHelper,
ctx.getPackageManager().getResourcesForApplication(pi.applicationInfo),
() -> parser, AutoInstallsLayout.TAG_WORKSPACE);
} catch (Exception e) {
@@ -620,7 +627,7 @@
.build();
}
- private DefaultLayoutParser getDefaultLayoutParser(AppWidgetHost widgetHost) {
+ private DefaultLayoutParser getDefaultLayoutParser(LauncherWidgetHolder widgetHolder) {
InvariantDeviceProfile idp = LauncherAppState.getIDP(getContext());
int defaultLayout = mUseTestWorkspaceLayout
? TEST_WORKSPACE_LAYOUT_RES_XML : idp.defaultLayoutId;
@@ -630,7 +637,7 @@
defaultLayout = idp.demoModeLayoutId;
}
- return new DefaultLayoutParser(getContext(), widgetHost,
+ return new DefaultLayoutParser(getContext(), widgetHolder,
mOpenHelper, getContext().getResources(), defaultLayout);
}
@@ -931,28 +938,46 @@
*/
public void removeGhostWidgets(SQLiteDatabase db) {
// Get all existing widget ids.
- final AppWidgetHost host = newLauncherWidgetHost();
- final int[] allWidgets;
+ final LauncherWidgetHolder holder = newLauncherWidgetHolder();
try {
- // Although the method was defined in O, it has existed since the beginning of time,
- // so it might work on older platforms as well.
- allWidgets = host.getAppWidgetIds();
- } catch (IncompatibleClassChangeError e) {
- Log.e(TAG, "getAppWidgetIds not supported", e);
- return;
- }
- final IntSet validWidgets = IntSet.wrap(LauncherDbUtils.queryIntArray(false, db,
- Favorites.TABLE_NAME, Favorites.APPWIDGET_ID,
- "itemType=" + Favorites.ITEM_TYPE_APPWIDGET, null, null));
- for (int widgetId : allWidgets) {
- if (!validWidgets.contains(widgetId)) {
- try {
- FileLog.d(TAG, "Deleting invalid widget " + widgetId);
- host.deleteAppWidgetId(widgetId);
- } catch (RuntimeException e) {
- // Ignore
+ final int[] allWidgets;
+ try {
+ // Although the method was defined in O, it has existed since the beginning of
+ // time, so it might work on older platforms as well.
+ allWidgets = holder.getAppWidgetIds();
+ } catch (IncompatibleClassChangeError e) {
+ Log.e(TAG, "getAppWidgetIds not supported", e);
+ // Necessary to destroy the holder to free up possible activity context
+ holder.destroy();
+ return;
+ }
+ final IntSet validWidgets = IntSet.wrap(LauncherDbUtils.queryIntArray(false, db,
+ Favorites.TABLE_NAME, Favorites.APPWIDGET_ID,
+ "itemType=" + Favorites.ITEM_TYPE_APPWIDGET, null, null));
+ boolean isAnyWidgetRemoved = false;
+ for (int widgetId : allWidgets) {
+ if (!validWidgets.contains(widgetId)) {
+ try {
+ FileLog.d(TAG, "Deleting invalid widget " + widgetId);
+ holder.deleteAppWidgetId(widgetId);
+ isAnyWidgetRemoved = true;
+ } catch (RuntimeException e) {
+ // Ignore
+ }
}
}
+ if (isAnyWidgetRemoved) {
+ final String allWidgetsIds = Arrays.stream(allWidgets).mapToObj(String::valueOf)
+ .collect(Collectors.joining(",", "[", "]"));
+ final String validWidgetsIds = Arrays.stream(
+ validWidgets.getArray().toArray()).mapToObj(String::valueOf)
+ .collect(Collectors.joining(",", "[", "]"));
+ FileLog.d(TAG, "One or more widgets was removed. db_path=" + db.getPath()
+ + " allWidgetsIds=" + allWidgetsIds
+ + ", validWidgetsIds=" + validWidgetsIds);
+ }
+ } finally {
+ holder.destroy();
}
}
@@ -1053,8 +1078,12 @@
return mMaxItemId;
}
- public AppWidgetHost newLauncherWidgetHost() {
- return new LauncherAppWidgetHost(mContext);
+ /**
+ * @return A new {@link LauncherWidgetHolder} based on the current context
+ */
+ @NonNull
+ public LauncherWidgetHolder newLauncherWidgetHolder() {
+ return new LauncherWidgetHolder(mContext);
}
@Override
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index 66195f3..4c8f2d9 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -94,15 +94,11 @@
*/
public static final int ITEM_TYPE_DEEP_SHORTCUT = 6;
- /**
- * The favroite is a search action
- */
- public static final int ITEM_TYPE_SEARCH_ACTION = 7;
+ // *** Below enum values are used for metrics purpose but not used in Favorites DB ***
/**
* Type of the item is recents task.
- * TODO(hyunyoungs): move constants not related to Favorites DB to a better location.
*/
public static final int ITEM_TYPE_TASK = 7;
@@ -112,6 +108,11 @@
public static final int ITEM_TYPE_QSB = 8;
/**
+ * The favorite is a search action
+ */
+ public static final int ITEM_TYPE_SEARCH_ACTION = 9;
+
+ /**
* The icon package name in Intent.ShortcutIconResource
* <P>Type: TEXT</P>
*/
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 4532ed4..31a7d18 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -71,17 +71,14 @@
public static final int FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED = BaseState.getFlag(2);
// Flag to indicate that workspace should draw page background
public static final int FLAG_WORKSPACE_HAS_BACKGROUNDS = BaseState.getFlag(3);
- // True if the back button should be hidden when in this state (assuming no floating views are
- // open, launcher has window focus, etc).
- public static final int FLAG_HIDE_BACK_BUTTON = BaseState.getFlag(4);
// Flag to indicate if the state would have scrim over sysui region: statu sbar and nav bar
- public static final int FLAG_HAS_SYS_UI_SCRIM = BaseState.getFlag(5);
+ public static final int FLAG_HAS_SYS_UI_SCRIM = BaseState.getFlag(4);
// Flag to inticate that all popups should be closed when this state is enabled.
- public static final int FLAG_CLOSE_POPUPS = BaseState.getFlag(6);
- public static final int FLAG_OVERVIEW_UI = BaseState.getFlag(7);
+ public static final int FLAG_CLOSE_POPUPS = BaseState.getFlag(5);
+ public static final int FLAG_OVERVIEW_UI = BaseState.getFlag(6);
// Flag indicating that hotseat and its contents are not accessible.
- public static final int FLAG_HOTSEAT_INACCESSIBLE = BaseState.getFlag(8);
+ public static final int FLAG_HOTSEAT_INACCESSIBLE = BaseState.getFlag(7);
public static final float NO_OFFSET = 0;
@@ -110,8 +107,7 @@
*/
public static final LauncherState NORMAL = new LauncherState(NORMAL_STATE_ORDINAL,
LAUNCHER_STATE_HOME,
- FLAG_DISABLE_RESTORE | FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED | FLAG_HIDE_BACK_BUTTON |
- FLAG_HAS_SYS_UI_SCRIM) {
+ FLAG_DISABLE_RESTORE | FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED | FLAG_HAS_SYS_UI_SCRIM) {
@Override
public int getTransitionDuration(Context context, boolean isToState) {
// Arbitrary duration, when going to NORMAL we use the state we're coming from instead.
@@ -265,7 +261,8 @@
*
* 0 means completely zoomed in, without blurs. 1 is zoomed out, with blurs.
*/
- public final float getDepth(Context context) {
+ public final <DEVICE_PROFILE_CONTEXT extends Context & DeviceProfile.DeviceProfileListenable>
+ float getDepth(DEVICE_PROFILE_CONTEXT context) {
return getDepth(context,
BaseDraggingActivity.fromContext(context).getDeviceProfile().isMultiWindowMode);
}
@@ -275,14 +272,16 @@
*
* @see #getDepth(Context).
*/
- public final float getDepth(Context context, boolean isMultiWindowMode) {
+ public final <DEVICE_PROFILE_CONTEXT extends Context & DeviceProfile.DeviceProfileListenable>
+ float getDepth(DEVICE_PROFILE_CONTEXT context, boolean isMultiWindowMode) {
if (isMultiWindowMode) {
return 0;
}
return getDepthUnchecked(context);
}
- protected float getDepthUnchecked(Context context) {
+ protected <DEVICE_PROFILE_CONTEXT extends Context & DeviceProfile.DeviceProfileListenable>
+ float getDepthUnchecked(DEVICE_PROFILE_CONTEXT context) {
return 0f;
}
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 73be5be..eb68adb 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -259,8 +259,13 @@
abortScrollerAnimation(true);
}
+ protected void onScrollerAnimationAborted() {
+ // No-Op
+ }
+
private void abortScrollerAnimation(boolean resetNextPage) {
mScroller.abortAnimation();
+ onScrollerAnimationAborted();
// We need to clean up the next page here to avoid computeScrollHelper from
// updating current page on the pass.
if (resetNextPage) {
@@ -555,11 +560,11 @@
if (mAllowOverScroll) {
if (newPos < mMinScroll && oldPos >= mMinScroll) {
mEdgeGlowLeft.onAbsorb((int) mScroller.getCurrVelocity());
- mScroller.abortAnimation();
+ abortScrollerAnimation(false);
onEdgeAbsorbingScroll();
} else if (newPos > mMaxScroll && oldPos <= mMaxScroll) {
mEdgeGlowRight.onAbsorb((int) mScroller.getCurrVelocity());
- mScroller.abortAnimation();
+ abortScrollerAnimation(false);
onEdgeAbsorbingScroll();
}
}
@@ -569,7 +574,7 @@
int finalPos = mOrientationHandler.getPrimaryValue(mScroller.getFinalX(),
mScroller.getFinalY());
if (newPos == finalPos && mEdgeGlowLeft.isFinished() && mEdgeGlowRight.isFinished()) {
- mScroller.abortAnimation();
+ abortScrollerAnimation(false);
}
invalidate();
@@ -767,6 +772,13 @@
}
if (mScroller.isFinished() && pageScrollChanged) {
+ // TODO(b/246283207): Remove logging once root cause of flake detected.
+ if (Utilities.IS_RUNNING_IN_TEST_HARNESS && !(this instanceof Workspace)) {
+ Log.d("b/246283207", this.getClass().getSimpleName() + "#onLayout() -> "
+ + "if(mScroller.isFinished() && pageScrollChanged) -> getNextPage(): "
+ + getNextPage() + ", getScrollForPage(getNextPage()): "
+ + getScrollForPage(getNextPage()));
+ }
setCurrentPage(getNextPage());
}
onPageScrollsInitialized();
diff --git a/src/com/android/launcher3/PendingAddItemInfo.java b/src/com/android/launcher3/PendingAddItemInfo.java
index be994ee..b7a22fc 100644
--- a/src/com/android/launcher3/PendingAddItemInfo.java
+++ b/src/com/android/launcher3/PendingAddItemInfo.java
@@ -18,6 +18,7 @@
import android.content.ComponentName;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.launcher3.model.data.ItemInfo;
@@ -43,6 +44,7 @@
/**
* Returns shallow copy of the object.
*/
+ @NonNull
@Override
public ItemInfo makeShallowCopy() {
PendingAddItemInfo itemInfo = new PendingAddItemInfo();
diff --git a/src/com/android/launcher3/SecondaryDropTarget.java b/src/com/android/launcher3/SecondaryDropTarget.java
index f8bc1f4..791cfff 100644
--- a/src/com/android/launcher3/SecondaryDropTarget.java
+++ b/src/com/android/launcher3/SecondaryDropTarget.java
@@ -159,7 +159,7 @@
return RECONFIGURE;
}
return INVALID;
- } else if (FeatureFlags.ENABLE_PREDICTION_DISMISS.get() && info.isPredictedItem()) {
+ } else if (info.isPredictedItem()) {
return DISMISS_PREDICTION;
}
@@ -288,7 +288,7 @@
if (widgetId != INVALID_APPWIDGET_ID) {
mLauncher.setWaitingForResult(
PendingRequestArgs.forWidgetInfo(widgetId, null, info));
- mLauncher.getAppWidgetHost().startConfigActivity(mLauncher, widgetId,
+ mLauncher.getAppWidgetHolder().startConfigActivity(mLauncher, widgetId,
REQUEST_RECONFIGURE_APPWIDGET);
}
return null;
diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
index 486a68f..7a74d7e 100644
--- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java
+++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
@@ -32,6 +32,7 @@
import android.view.ViewGroup;
import com.android.launcher3.CellLayout.ContainerType;
+import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.views.ActivityContext;
@@ -80,7 +81,7 @@
final int count = getChildCount();
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
if ((lp.cellX <= cellX) && (cellX < lp.cellX + lp.cellHSpan)
&& (lp.cellY <= cellY) && (cellY < lp.cellY + lp.cellVSpan)) {
@@ -107,7 +108,7 @@
}
public void setupLp(View child) {
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
if (child instanceof NavigableAppWidgetHostView) {
DeviceProfile profile = mActivity.getDeviceProfile();
((NavigableAppWidgetHostView) child).getWidgetInset(profile, mTempRect);
@@ -131,7 +132,7 @@
}
public void measureChild(View child) {
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
final DeviceProfile dp = mActivity.getDeviceProfile();
if (child instanceof NavigableAppWidgetHostView) {
@@ -151,7 +152,7 @@
// No need to add padding when cell layout border spacing is present.
boolean noPaddingX =
(dp.cellLayoutBorderSpacePx.x > 0 && mContainerType == WORKSPACE)
- || (dp.folderCellLayoutBorderSpacePx.x > 0 && mContainerType == FOLDER)
+ || (dp.folderCellLayoutBorderSpacePx > 0 && mContainerType == FOLDER)
|| (dp.hotseatBorderSpace > 0 && mContainerType == HOTSEAT);
int cellPaddingX = noPaddingX
? 0
@@ -175,7 +176,6 @@
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
layoutChild(child);
}
}
@@ -185,7 +185,7 @@
* Core logic to layout a child for this ViewGroup.
*/
public void layoutChild(View child) {
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
if (child instanceof NavigableAppWidgetHostView) {
NavigableAppWidgetHostView nahv = (NavigableAppWidgetHostView) child;
@@ -255,7 +255,7 @@
@Override
public void drawFolderLeaveBehindForIcon(FolderIcon child) {
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
// While the folder is open, the position of the icon cannot change.
lp.canReorder = false;
if (mContainerType == HOTSEAT) {
@@ -266,7 +266,7 @@
@Override
public void clearFolderLeaveBehind(FolderIcon child) {
- ((CellLayout.LayoutParams) child.getLayoutParams()).canReorder = true;
+ ((CellLayoutLayoutParams) child.getLayoutParams()).canReorder = true;
if (mContainerType == HOTSEAT) {
CellLayout cl = (CellLayout) getParent();
cl.clearFolderLeaveBehind();
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index d0dbaf4..743e3ae 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -26,20 +26,13 @@
import android.app.ActivityManager;
import android.app.Person;
import android.app.WallpaperManager;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.LauncherApps;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.content.pm.ShortcutInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
-import android.database.ContentObserver;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.LightingColorFilter;
@@ -51,7 +44,6 @@
import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
-import android.net.Uri;
import android.os.Build;
import android.os.Build.VERSION_CODES;
import android.os.DeadObjectException;
@@ -71,14 +63,12 @@
import android.view.View;
import android.view.ViewConfiguration;
import android.view.animation.Interpolator;
-import android.widget.LinearLayout;
import androidx.annotation.ChecksSdkIntAtLeast;
import androidx.annotation.NonNull;
import androidx.core.graphics.ColorUtils;
import com.android.launcher3.dragndrop.FolderAdaptiveIcon;
-import com.android.launcher3.graphics.GridCustomizationsProvider;
import com.android.launcher3.graphics.TintedDrawableSpan;
import com.android.launcher3.icons.ShortcutCachingLogic;
import com.android.launcher3.icons.ThemedIconDrawable;
@@ -90,7 +80,6 @@
import com.android.launcher3.shortcuts.ShortcutRequest;
import com.android.launcher3.testing.shared.ResourceUtils;
import com.android.launcher3.util.IntArray;
-import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ActivityContext;
@@ -99,10 +88,8 @@
import java.lang.reflect.Method;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
import java.util.Locale;
-import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -116,8 +103,6 @@
private static final Pattern sTrimPattern =
Pattern.compile("^[\\s|\\p{javaSpaceChar}]*(.*)[\\s|\\p{javaSpaceChar}]*$");
- private static final int[] sLoc0 = new int[2];
- private static final int[] sLoc1 = new int[2];
private static final Matrix sMatrix = new Matrix();
private static final Matrix sInverseMatrix = new Matrix();
@@ -166,14 +151,6 @@
Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0;
}
- // An intent extra to indicate the horizontal scroll of the wallpaper.
- public static final String EXTRA_WALLPAPER_OFFSET = "com.android.launcher3.WALLPAPER_OFFSET";
- public static final String EXTRA_WALLPAPER_FLAVOR = "com.android.launcher3.WALLPAPER_FLAVOR";
-
- // An intent extra to indicate the launch source by launcher.
- public static final String EXTRA_WALLPAPER_LAUNCH_SOURCE =
- "com.android.wallpaper.LAUNCH_SOURCE";
-
public static boolean IS_RUNNING_IN_TEST_HARNESS =
ActivityManager.isRunningInTestHarness();
@@ -185,12 +162,6 @@
return Log.isLoggable(propertyName, Log.VERBOSE);
}
- public static boolean existsStyleWallpapers(Context context) {
- ResolveInfo ri = context.getPackageManager().resolveActivity(
- PackageManagerHelper.getStyleWallpapersIntent(context), 0);
- return ri != null;
- }
-
/**
* Given a coordinate relative to the descendant, find the coordinate in a parent view's
* coordinates.
@@ -304,9 +275,9 @@
* Sets {@param out} to be same as {@param in} by rounding individual values
*/
public static void roundArray(float[] in, int[] out) {
- for (int i = 0; i < in.length; i++) {
- out[i] = Math.round(in[i]);
- }
+ for (int i = 0; i < in.length; i++) {
+ out[i] = Math.round(in[i]);
+ }
}
public static void offsetPoints(float[] points, float offsetX, float offsetY) {
@@ -327,80 +298,8 @@
localY < (v.getHeight() + slop);
}
- public static int[] getCenterDeltaInScreenSpace(View v0, View v1) {
- v0.getLocationInWindow(sLoc0);
- v1.getLocationInWindow(sLoc1);
-
- sLoc0[0] += (v0.getMeasuredWidth() * v0.getScaleX()) / 2;
- sLoc0[1] += (v0.getMeasuredHeight() * v0.getScaleY()) / 2;
- sLoc1[0] += (v1.getMeasuredWidth() * v1.getScaleX()) / 2;
- sLoc1[1] += (v1.getMeasuredHeight() * v1.getScaleY()) / 2;
- return new int[] {sLoc1[0] - sLoc0[0], sLoc1[1] - sLoc0[1]};
- }
-
- /**
- * Helper method to set rectOut with rectFSrc.
- */
- public static void setRect(RectF rectFSrc, Rect rectOut) {
- rectOut.left = (int) rectFSrc.left;
- rectOut.top = (int) rectFSrc.top;
- rectOut.right = (int) rectFSrc.right;
- rectOut.bottom = (int) rectFSrc.bottom;
- }
-
public static void scaleRectFAboutCenter(RectF r, float scale) {
- scaleRectFAboutPivot(r, scale, r.centerX(), r.centerY());
- }
-
- public static void scaleRectFAboutPivot(RectF r, float scale, float px, float py) {
- if (scale != 1.0f) {
- r.offset(-px, -py);
- r.left = r.left * scale;
- r.top = r.top * scale ;
- r.right = r.right * scale;
- r.bottom = r.bottom * scale;
- r.offset(px, py);
- }
- }
-
- public static void scaleRectAboutCenter(Rect r, float scale) {
- if (scale != 1.0f) {
- int cx = r.centerX();
- int cy = r.centerY();
- r.offset(-cx, -cy);
- scaleRect(r, scale);
- r.offset(cx, cy);
- }
- }
-
- public static void scaleRect(Rect r, float scale) {
- if (scale != 1.0f) {
- r.left = (int) (r.left * scale + 0.5f);
- r.top = (int) (r.top * scale + 0.5f);
- r.right = (int) (r.right * scale + 0.5f);
- r.bottom = (int) (r.bottom * scale + 0.5f);
- }
- }
-
- public static void insetRect(Rect r, Rect insets) {
- r.left = Math.min(r.right, r.left + insets.left);
- r.top = Math.min(r.bottom, r.top + insets.top);
- r.right = Math.max(r.left, r.right - insets.right);
- r.bottom = Math.max(r.top, r.bottom - insets.bottom);
- }
-
- public static float shrinkRect(Rect r, float scaleX, float scaleY) {
- float scale = Math.min(Math.min(scaleX, scaleY), 1.0f);
- if (scale < 1.0f) {
- int deltaX = (int) (r.width() * (scaleX - scale) * 0.5f);
- r.left += deltaX;
- r.right -= deltaX;
-
- int deltaY = (int) (r.height() * (scaleY - scale) * 0.5f);
- r.top += deltaY;
- r.bottom -= deltaY;
- }
- return scale;
+ scaleRectFAboutCenter(r, scale, scale);
}
/**
@@ -418,6 +317,33 @@
r.offset(px, py);
}
+ public static void scaleRectAboutCenter(Rect r, float scale) {
+ if (scale != 1.0f) {
+ int cx = r.centerX();
+ int cy = r.centerY();
+ r.offset(-cx, -cy);
+ r.left = (int) (r.left * scale + 0.5f);
+ r.top = (int) (r.top * scale + 0.5f);
+ r.right = (int) (r.right * scale + 0.5f);
+ r.bottom = (int) (r.bottom * scale + 0.5f);
+ r.offset(cx, cy);
+ }
+ }
+
+ public static float shrinkRect(Rect r, float scaleX, float scaleY) {
+ float scale = Math.min(Math.min(scaleX, scaleY), 1.0f);
+ if (scale < 1.0f) {
+ int deltaX = (int) (r.width() * (scaleX - scale) * 0.5f);
+ r.left += deltaX;
+ r.right -= deltaX;
+
+ int deltaY = (int) (r.height() * (scaleY - scale) * 0.5f);
+ r.top += deltaY;
+ r.bottom -= deltaY;
+ }
+ return scale;
+ }
+
/**
* Maps t from one range to another range.
* @param t The value to map.
@@ -453,30 +379,6 @@
}
/**
- * Bounds parameter to the range [0, 1]
- */
- public static float saturate(float a) {
- return boundToRange(a, 0, 1.0f);
- }
-
- /**
- * Returns the compliment (1 - a) of the parameter.
- */
- public static float comp(float a) {
- return 1 - a;
- }
-
- /**
- * Returns the "probabilistic or" of a and b. (a + b - ab).
- * Useful beyond probability, can be used to combine two unit progresses for example.
- */
- public static float or(float a, float b) {
- float satA = saturate(a);
- float satB = saturate(b);
- return satA + satB - (satA * satB);
- }
-
- /**
* Trims the string, removing all whitespace at the beginning and end of the string.
* Non-breaking whitespaces are also removed.
*/
@@ -520,6 +422,11 @@
return (int) (dp * Resources.getSystem().getDisplayMetrics().density);
}
+ /** Converts a dp value to pixels for a certain density. */
+ public static int dpToPx(float dp, int densityDpi) {
+ float densityRatio = (float) densityDpi / DisplayMetrics.DENSITY_DEFAULT;
+ return (int) (dp * densityRatio);
+ }
public static int pxFromSp(float size, DisplayMetrics metrics) {
return pxFromSp(size, metrics, 1f);
@@ -530,7 +437,6 @@
return ResourceUtils.roundPxValueFromFloat(value);
}
-
public static String createDbSelectionQuery(String columnName, IntArray values) {
return String.format(Locale.ENGLISH, "%s IN (%s)", columnName, values.toConcatString());
}
@@ -554,18 +460,6 @@
}
/**
- * Using the view's bounds and icon size, calculate where the icon bounds will
- * be if it was positioned at the center of the view.
- */
- public static void setRectToViewCenter(View iconView, int iconSize, Rect outBounds) {
- int top = (iconView.getHeight() - iconSize) / 2;
- int left = (iconView.getWidth() - iconSize) / 2;
- int right = left + iconSize;
- int bottom = top + iconSize;
- outBounds.set(left, top, right, bottom);
- }
-
- /**
* Ensures that a value is within given bounds. Specifically:
* If value is less than lowerBound, return lowerBound; else if value is greater than upperBound,
* return upperBound; else return value unchanged.
@@ -640,42 +534,6 @@
|| e.getCause() instanceof DeadObjectException;
}
- public static boolean isGridOptionsEnabled(Context context) {
- return isComponentEnabled(context.getPackageManager(),
- context.getPackageName(),
- GridCustomizationsProvider.class.getName());
- }
-
- private static boolean isComponentEnabled(PackageManager pm, String pkgName, String clsName) {
- ComponentName componentName = new ComponentName(pkgName, clsName);
- int componentEnabledSetting = pm.getComponentEnabledSetting(componentName);
-
- switch (componentEnabledSetting) {
- case PackageManager.COMPONENT_ENABLED_STATE_DISABLED:
- return false;
- case PackageManager.COMPONENT_ENABLED_STATE_ENABLED:
- return true;
- case PackageManager.COMPONENT_ENABLED_STATE_DEFAULT:
- default:
- // We need to get the application info to get the component's default state
- try {
- PackageInfo packageInfo = pm.getPackageInfo(pkgName,
- PackageManager.GET_PROVIDERS | PackageManager.GET_DISABLED_COMPONENTS);
-
- if (packageInfo.providers != null) {
- return Arrays.stream(packageInfo.providers).anyMatch(
- pi -> pi.name.equals(clsName) && pi.isEnabled());
- }
-
- // the component is not declared in the AndroidManifest
- return false;
- } catch (PackageManager.NameNotFoundException e) {
- // the package isn't installed on the device
- return false;
- }
- }
- }
-
/**
* Utility method to post a runnable on the handler, skipping the synchronization barriers.
*/
@@ -685,12 +543,6 @@
handler.sendMessage(msg);
}
- public static void unregisterReceiverSafely(Context context, BroadcastReceiver receiver) {
- try {
- context.unregisterReceiver(receiver);
- } catch (IllegalArgumentException e) {}
- }
-
/**
* Returns the full drawable for info without any flattening or pre-processing.
*
@@ -788,14 +640,6 @@
}
}
- /**
- * @return true is the extra is either null or is of type {@param type}
- */
- public static boolean isValidExtraType(Intent intent, String key, Class type) {
- Object extra = intent.getParcelableExtra(key);
- return extra == null || type.isInstance(extra);
- }
-
public static float squaredHypot(float x, float y) {
return x * x + y * y;
}
@@ -806,28 +650,6 @@
}
/**
- * Helper method to create a content provider
- */
- public static ContentObserver newContentObserver(Handler handler, Consumer<Uri> command) {
- return new ContentObserver(handler) {
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- command.accept(uri);
- }
- };
- }
-
- /**
- * Compares the ratio of two quantities and returns whether that ratio is greater than the
- * provided bound. Order of quantities does not matter. Bound should be a decimal representation
- * of a percentage.
- */
- public static boolean isRelativePercentDifferenceGreaterThan(float first, float second,
- float bound) {
- return (Math.abs(first - second) / Math.abs((first + second) / 2.0f)) > bound;
- }
-
- /**
* Rotates `inOutBounds` by `delta` 90-degree increments. Rotation is visually CCW. Parent
* sizes represent the "space" that will rotate carrying inOutBounds along with it to determine
* the final bounds.
@@ -875,16 +697,6 @@
ColorUtils.blendARGB(0, color, tintAmount));
}
- /**
- * Sets start margin on the provided {@param view} to be {@param margin}.
- * Assumes {@param view} is a child of {@link LinearLayout}
- */
- public static void setStartMarginForView(View view, int margin) {
- LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) view.getLayoutParams();
- lp.setMarginStart(margin);
- view.setLayoutParams(lp);
- }
-
public static Rect getViewBounds(@NonNull View v) {
int[] pos = new int[2];
v.getLocationOnScreen(pos);
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index b716912..517bbf8 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -26,7 +26,6 @@
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.SPRING_LOADED;
import static com.android.launcher3.anim.AnimatorListeners.forSuccessCallback;
-import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_OVERLAY;
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;
@@ -53,12 +52,13 @@
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
+import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.FrameLayout;
import android.widget.Toast;
import androidx.annotation.Nullable;
@@ -67,6 +67,7 @@
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.config.FeatureFlags;
import com.android.launcher3.dot.FolderDotInfo;
import com.android.launcher3.dragndrop.DragController;
@@ -106,9 +107,9 @@
import com.android.launcher3.util.RunnableList;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.util.WallpaperOffsetInterpolator;
-import com.android.launcher3.widget.LauncherAppWidgetHost;
-import com.android.launcher3.widget.LauncherAppWidgetHost.ProviderChangedListener;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
+import com.android.launcher3.widget.LauncherWidgetHolder;
+import com.android.launcher3.widget.LauncherWidgetHolder.ProviderChangedListener;
import com.android.launcher3.widget.NavigableAppWidgetHostView;
import com.android.launcher3.widget.PendingAddShortcutInfo;
import com.android.launcher3.widget.PendingAddWidgetInfo;
@@ -117,6 +118,7 @@
import com.android.launcher3.widget.dragndrop.AppWidgetHostViewDragListener;
import com.android.launcher3.widget.util.WidgetSizes;
import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlay;
+import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlayCallbacks;
import java.util.ArrayList;
import java.util.Iterator;
@@ -134,7 +136,7 @@
public class Workspace<T extends View & PageIndicator> extends PagedView<T>
implements DropTarget, DragSource, View.OnTouchListener,
DragController.DragListener, Insettable, StateHandler<LauncherState>,
- WorkspaceLayoutManager, LauncherBindableItemsContainer {
+ WorkspaceLayoutManager, LauncherBindableItemsContainer, LauncherOverlayCallbacks {
/** The value that {@link #mTransitionProgress} must be greater than for
* {@link #transitionStateShouldAllowDrop()} to return true. */
@@ -152,9 +154,7 @@
public static final int DEFAULT_PAGE = 0;
- private static final int DEFAULT_SMARTSPACE_HEIGHT = 1;
-
- private static final int EXPANDED_SMARTSPACE_HEIGHT = 2;
+ private final int mAllAppsIconSize;
private LayoutTransition mLayoutTransition;
@Thunk final WallpaperManager mWallpaperManager;
@@ -223,8 +223,8 @@
// Variables relating to touch disambiguation (scrolling workspace vs. scrolling a widget)
private float mXDown;
private float mYDown;
- private View mQsb;
- private boolean mIsEventOverQsb;
+ private View mFirstPagePinnedItem;
+ private boolean mIsEventOverFirstPagePinnedItem;
final static float START_DAMPING_TOUCH_SLOP_ANGLE = (float) Math.PI / 6;
final static float MAX_SWIPE_ANGLE = (float) Math.PI / 3;
@@ -254,14 +254,12 @@
// State related to Launcher Overlay
private OverlayEdgeEffect mOverlayEdgeEffect;
- boolean mOverlayShown = false;
- private Runnable mOnOverlayHiddenCallback;
+ private boolean mOverlayShown = false;
+ private float mOverlayProgress; // 1 -> overlay completely visible, 0 -> home visible
+ private final List<LauncherOverlayCallbacks> mOverlayCallbacks = new ArrayList<>();
private boolean mForceDrawAdjacentPages = false;
- // Total over scrollX in the overlay direction.
- private float mOverlayTranslation;
-
// Handles workspace state transitions
private final WorkspaceStateTransitionAnimation mStateTransitionAnimation;
@@ -290,7 +288,7 @@
mLauncher = Launcher.getLauncher(context);
mStateTransitionAnimation = new WorkspaceStateTransitionAnimation(mLauncher, this);
mWallpaperManager = WallpaperManager.getInstance(context);
-
+ mAllAppsIconSize = mLauncher.getDeviceProfile().allAppsIconSizePx;
mWallpaperOffset = new WallpaperOffsetInterpolator(this);
setHapticFeedbackEnabled(false);
@@ -326,6 +324,26 @@
updateCellLayoutPadding();
updateWorkspaceWidgetsSizes();
+ setPageIndicatorInset();
+ }
+
+ private void setPageIndicatorInset() {
+ DeviceProfile grid = mLauncher.getDeviceProfile();
+
+ FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mPageIndicator.getLayoutParams();
+
+ // Set insets for page indicator
+ Rect padding = grid.workspacePadding;
+ if (grid.isVerticalBarLayout()) {
+ lp.leftMargin = padding.left + grid.workspaceCellPaddingXPx;
+ lp.rightMargin = padding.right + grid.workspaceCellPaddingXPx;
+ lp.bottomMargin = padding.bottom;
+ } else {
+ lp.leftMargin = lp.rightMargin = 0;
+ lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
+ lp.bottomMargin = grid.hotseatBarSizePx;
+ }
+ mPageIndicator.setLayoutParams(lp);
}
private void updateCellLayoutPadding() {
@@ -554,22 +572,22 @@
// Add the first page
CellLayout firstPage = insertNewWorkspaceScreen(Workspace.FIRST_SCREEN_ID, getChildCount());
- // Always add a QSB on the first screen.
- if (mQsb == null) {
- // In transposed layout, we add the QSB in the Grid. As workspace does not touch the
- // edges, we do not need a full width QSB.
- mQsb = LayoutInflater.from(getContext())
+ // Always add a first page pinned widget on the first screen.
+ if (mFirstPagePinnedItem == null) {
+ // In transposed layout, we add the first page pinned widget in the Grid.
+ // As workspace does not touch the edges, we do not need a full
+ // width first page pinned widget.
+ mFirstPagePinnedItem = LayoutInflater.from(getContext())
.inflate(R.layout.search_container_workspace, firstPage, false);
}
- int cellVSpan = FeatureFlags.EXPANDED_SMARTSPACE.get()
- ? EXPANDED_SMARTSPACE_HEIGHT : DEFAULT_SMARTSPACE_HEIGHT;
int cellHSpan = mLauncher.getDeviceProfile().inv.numSearchContainerColumns;
- CellLayout.LayoutParams lp = new CellLayout.LayoutParams(0, 0, cellHSpan, cellVSpan);
+ CellLayoutLayoutParams lp = new CellLayoutLayoutParams(0, 0, cellHSpan, 1);
lp.canReorder = false;
- if (!firstPage.addViewToCellLayout(mQsb, 0, R.id.search_container_workspace, lp, true)) {
+ if (!firstPage.addViewToCellLayout(
+ mFirstPagePinnedItem, 0, R.id.search_container_workspace, lp, true)) {
Log.e(TAG, "Failed to add to item at (0, 0) to CellLayout");
- mQsb = null;
+ mFirstPagePinnedItem = null;
}
}
@@ -578,9 +596,9 @@
// transition animations competing with us changing the scroll when we add pages
disableLayoutTransitions();
- // Recycle the QSB widget
- if (mQsb != null) {
- ((ViewGroup) mQsb.getParent()).removeView(mQsb);
+ // Recycle the first page pinned widget
+ if (mFirstPagePinnedItem != null) {
+ ((ViewGroup) mFirstPagePinnedItem.getParent()).removeView(mFirstPagePinnedItem);
}
// Remove the pages and clear the screen models
@@ -900,6 +918,10 @@
return mScreenOrder;
}
+ protected View getFirstPagePinnedItem() {
+ return mFirstPagePinnedItem;
+ }
+
/**
* Returns the screen ID of a page that is shown together with the given page screen ID when the
* two panel UI is enabled.
@@ -1051,20 +1073,22 @@
mXDown = ev.getX();
mYDown = ev.getY();
- if (mQsb != null) {
+ if (mFirstPagePinnedItem != null) {
mTempFXY[0] = mXDown + getScrollX();
mTempFXY[1] = mYDown + getScrollY();
- Utilities.mapCoordInSelfToDescendant(mQsb, this, mTempFXY);
- mIsEventOverQsb = mQsb.getLeft() <= mTempFXY[0] && mQsb.getRight() >= mTempFXY[0]
- && mQsb.getTop() <= mTempFXY[1] && mQsb.getBottom() >= mTempFXY[1];
+ Utilities.mapCoordInSelfToDescendant(mFirstPagePinnedItem, this, mTempFXY);
+ mIsEventOverFirstPagePinnedItem = mFirstPagePinnedItem.getLeft() <= mTempFXY[0]
+ && mFirstPagePinnedItem.getRight() >= mTempFXY[0]
+ && mFirstPagePinnedItem.getTop() <= mTempFXY[1]
+ && mFirstPagePinnedItem.getBottom() >= mTempFXY[1];
} else {
- mIsEventOverQsb = false;
+ mIsEventOverFirstPagePinnedItem = false;
}
}
@Override
protected void determineScrollingStart(MotionEvent ev) {
- if (!isFinishedSwitchingState() || mIsEventOverQsb) return;
+ if (!isFinishedSwitchingState() || mIsEventOverFirstPagePinnedItem) return;
float deltaX = ev.getX() - mXDown;
float absDeltaX = Math.abs(deltaX);
@@ -1125,9 +1149,15 @@
}
public void setLauncherOverlay(LauncherOverlay overlay) {
- mOverlayEdgeEffect = overlay == null ? null : new OverlayEdgeEffect(getContext(), overlay);
- EdgeEffectCompat newEffect = overlay == null
- ? new EdgeEffectCompat(getContext()) : mOverlayEdgeEffect;
+ final EdgeEffectCompat newEffect;
+ if (overlay == null) {
+ newEffect = new EdgeEffectCompat(getContext());
+ mOverlayEdgeEffect = null;
+ } else {
+ newEffect = mOverlayEdgeEffect = new OverlayEdgeEffect(getContext(), overlay);
+ overlay.setOverlayCallbacks(this);
+ }
+
if (mIsRtl) {
mEdgeGlowRight = newEffect;
} else {
@@ -1177,132 +1207,46 @@
@Override
protected boolean shouldFlingForVelocity(int velocityX) {
// When the overlay is moving, the fling or settle transition is controlled by the overlay.
- return Float.compare(Math.abs(mOverlayTranslation), 0) == 0 &&
- super.shouldFlingForVelocity(velocityX);
+ return Float.compare(Math.abs(mOverlayProgress), 0) == 0
+ && super.shouldFlingForVelocity(velocityX);
}
/**
* The overlay scroll is being controlled locally, just update our overlay effect
*/
+ @Override
public void onOverlayScrollChanged(float scroll) {
- if (Float.compare(scroll, 1f) == 0) {
+ mOverlayProgress = Utilities.boundToRange(scroll, 0, 1);
+ if (Float.compare(mOverlayProgress, 1f) == 0) {
if (!mOverlayShown) {
- mLauncher.getStatsLogManager().logger()
- .withSrcState(LAUNCHER_STATE_HOME)
- .withDstState(LAUNCHER_STATE_HOME)
- .withContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
- .setWorkspace(
- LauncherAtom.WorkspaceContainer.newBuilder()
- .setPageIndex(0))
- .build())
- .log(LAUNCHER_SWIPELEFT);
+ mOverlayShown = true;
+ mLauncher.onOverlayVisibilityChanged(true);
}
- mOverlayShown = true;
-
- // Let the Launcher activity know that the overlay is now visible.
- mLauncher.onOverlayVisibilityChanged(mOverlayShown);
-
- // Not announcing the overlay page for accessibility since it announces itself.
- } else if (Float.compare(scroll, 0f) == 0) {
+ } else if (Float.compare(mOverlayProgress, 0f) == 0) {
if (mOverlayShown) {
- // TODO: this is logged unnecessarily on home gesture.
- mLauncher.getStatsLogManager().logger()
- .withSrcState(LAUNCHER_STATE_HOME)
- .withDstState(LAUNCHER_STATE_HOME)
- .withContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
- .setWorkspace(
- LauncherAtom.WorkspaceContainer.newBuilder()
- .setPageIndex(-1))
- .build())
- .log(LAUNCHER_SWIPERIGHT);
- } else if (Float.compare(mOverlayTranslation, 0f) != 0) {
- // When arriving to 0 overscroll from non-zero overscroll, announce page for
- // accessibility since default announcements were disabled while in overscroll
- // state.
- // Not doing this if mOverlayShown because in that case the accessibility service
- // will announce the launcher window description upon regaining focus after
- // switching from the overlay screen.
- announcePageForAccessibility();
+ mOverlayShown = false;
+ mLauncher.onOverlayVisibilityChanged(false);
}
- mOverlayShown = false;
-
- // Let the Launcher activity know that the overlay is no longer visible.
- mLauncher.onOverlayVisibilityChanged(mOverlayShown);
-
- tryRunOverlayCallback();
}
-
- float offset = 0f;
-
- scroll = Math.max(scroll - offset, 0);
- scroll = Math.min(1, scroll / (1 - offset));
-
- float alpha = 1 - Interpolators.DEACCEL_3.getInterpolation(scroll);
- float transX = mLauncher.getDragLayer().getMeasuredWidth() * scroll;
-
- if (mIsRtl) {
- transX = -transX;
+ int count = mOverlayCallbacks.size();
+ for (int i = 0; i < count; i++) {
+ mOverlayCallbacks.get(i).onOverlayScrollChanged(mOverlayProgress);
}
- mOverlayTranslation = transX;
-
- // TODO(adamcohen): figure out a final effect here. We may need to recommend
- // different effects based on device performance. On at least one relatively high-end
- // device I've tried, translating the launcher causes things to get quite laggy.
- mLauncher.getDragLayer().setTranslationX(transX);
- mLauncher.getDragLayer().getAlphaProperty(ALPHA_INDEX_OVERLAY).setValue(alpha);
}
/**
- * @return false if the callback is still pending
+ * Adds a callback for receiving overlay progress
*/
- private boolean tryRunOverlayCallback() {
- if (mOnOverlayHiddenCallback == null) {
- // Return true as no callback is pending. This is used by OnWindowFocusChangeListener
- // to remove itself if multiple focus handles were added.
- return true;
- }
- if (mOverlayShown || !hasWindowFocus()) {
- return false;
- }
-
- mOnOverlayHiddenCallback.run();
- mOnOverlayHiddenCallback = null;
- return true;
+ public void addOverlayCallback(LauncherOverlayCallbacks callback) {
+ mOverlayCallbacks.add(callback);
+ callback.onOverlayScrollChanged(mOverlayProgress);
}
/**
- * Runs the given callback when the minus one overlay is hidden. Specifically, it is run
- * when launcher's window has focus and the overlay is no longer being shown. If a callback
- * is already present, the new callback will chain off it so both are run.
- *
- * @return Whether the callback was deferred.
+ * Removes a previously added overlay progress callback
*/
- public boolean runOnOverlayHidden(Runnable callback) {
- if (mOnOverlayHiddenCallback == null) {
- mOnOverlayHiddenCallback = callback;
- } else {
- // Chain the new callback onto the previous callback(s).
- Runnable oldCallback = mOnOverlayHiddenCallback;
- mOnOverlayHiddenCallback = () -> {
- oldCallback.run();
- callback.run();
- };
- }
- if (!tryRunOverlayCallback()) {
- ViewTreeObserver observer = getViewTreeObserver();
- if (observer != null && observer.isAlive()) {
- observer.addOnWindowFocusChangeListener(
- new ViewTreeObserver.OnWindowFocusChangeListener() {
- @Override
- public void onWindowFocusChanged(boolean hasFocus) {
- if (tryRunOverlayCallback() && observer.isAlive()) {
- observer.removeOnWindowFocusChangeListener(this);
- }
- }});
- }
- return true;
- }
- return false;
+ public void removeOverlayCallback(LauncherOverlayCallbacks callback) {
+ mOverlayCallbacks.remove(callback);
}
@Override
@@ -1677,8 +1621,14 @@
mDragSourceInternal = (ShortcutAndWidgetContainer) child.getParent();
}
- if (child instanceof BubbleTextView && !dragOptions.isAccessibleDrag) {
- dragOptions.preDragCondition = ((BubbleTextView) child).startLongPressAction();
+ if (child instanceof BubbleTextView) {
+ BubbleTextView btv = (BubbleTextView) child;
+ if (!dragOptions.isAccessibleDrag) {
+ dragOptions.preDragCondition = btv.startLongPressAction();
+ }
+ if (btv.isDisplaySearchResult()) {
+ dragOptions.preDragEndScale = (float) mAllAppsIconSize / btv.getIconSize();
+ }
}
final DragView dv;
@@ -1802,7 +1752,7 @@
boolean willCreateUserFolder(ItemInfo info, View dropOverView, boolean considerTimeout) {
if (dropOverView != null) {
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) dropOverView.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) dropOverView.getLayoutParams();
if (lp.useTmpCoords && (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.cellY)) {
return false;
}
@@ -1837,7 +1787,7 @@
}
boolean willAddToExistingUserFolder(ItemInfo dragInfo, View dropOverView) {
if (dropOverView != null) {
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) dropOverView.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) dropOverView.getLayoutParams();
if (lp.useTmpCoords && (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.cellY)) {
return false;
}
@@ -2054,7 +2004,7 @@
}
// update the item's position after drop
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) cell.getLayoutParams();
lp.cellX = lp.tmpCellX = mTargetCell[0];
lp.cellY = lp.tmpCellY = mTargetCell[1];
lp.cellHSpan = item.spanX;
@@ -2063,20 +2013,11 @@
if (container != LauncherSettings.Favorites.CONTAINER_HOTSEAT &&
cell instanceof LauncherAppWidgetHostView) {
- final CellLayout cellLayout = dropTargetLayout;
+
// We post this call so that the widget has a chance to be placed
// in its final location
-
- final LauncherAppWidgetHostView hostView = (LauncherAppWidgetHostView) cell;
- AppWidgetProviderInfo pInfo = hostView.getAppWidgetInfo();
- if (pInfo != null && pInfo.resizeMode != AppWidgetProviderInfo.RESIZE_NONE
- && !options.isAccessibleDrag) {
- onCompleteRunnable = () -> {
- if (!isPageInTransition()) {
- AppWidgetResizeFrame.showForWidget(hostView, cellLayout);
- }
- };
- }
+ onCompleteRunnable = getWidgetResizeFrameRunnable(options,
+ (LauncherAppWidgetHostView) cell, dropTargetLayout);
}
mLauncher.getModelWriter().modifyItemInDatabase(info, container, screenId,
lp.cellX, lp.cellY, item.spanX, item.spanY);
@@ -2089,7 +2030,7 @@
}
// If we can't find a drop location, we return the item to its original position
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) cell.getLayoutParams();
mTargetCell[0] = lp.cellX;
mTargetCell[1] = lp.cellY;
CellLayout layout = (CellLayout) cell.getParent().getParent();
@@ -2097,8 +2038,16 @@
}
} else {
// When drag is cancelled, reattach content view back to its original parent.
- if (mDragInfo.cell instanceof LauncherAppWidgetHostView) {
+ if (cell instanceof LauncherAppWidgetHostView) {
d.dragView.detachContentView(/* reattachToPreviousParent= */ true);
+
+ final CellLayout cellLayout = getParentCellLayoutForView(cell);
+ boolean pageIsVisible = isVisible(cellLayout);
+
+ if (pageIsVisible) {
+ onCompleteRunnable = getWidgetResizeFrameRunnable(options,
+ (LauncherAppWidgetHostView) cell, cellLayout);
+ }
}
}
@@ -2151,6 +2100,21 @@
}
}
+ @Nullable
+ private Runnable getWidgetResizeFrameRunnable(DragOptions options,
+ LauncherAppWidgetHostView hostView, CellLayout cellLayout) {
+ AppWidgetProviderInfo pInfo = hostView.getAppWidgetInfo();
+ if (pInfo != null && pInfo.resizeMode != AppWidgetProviderInfo.RESIZE_NONE
+ && !options.isAccessibleDrag) {
+ return () -> {
+ if (!isPageInTransition()) {
+ AppWidgetResizeFrame.showForWidget(hostView, cellLayout);
+ }
+ };
+ }
+ return null;
+ }
+
public void onNoCellFound(
View dropTargetLayout, ItemInfo itemInfo, @Nullable InstanceId logInstanceId) {
int strId = mLauncher.isHotseatLayout(dropTargetLayout)
@@ -2387,7 +2351,7 @@
}
mTargetCell = findNearestArea((int) mDragViewVisualCenter[0],
- (int) mDragViewVisualCenter[1], minSpanX, minSpanY,
+ (int) mDragViewVisualCenter[1], item.spanX, item.spanY,
mDragTargetLayout, mTargetCell);
int reorderX = mTargetCell[0];
int reorderY = mTargetCell[1];
@@ -2403,7 +2367,8 @@
mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1], item.spanX,
item.spanY, child, mTargetCell);
- manageReorderOnDragOver(d, targetCellDistance, nearestDropOccupied, minSpanX, minSpanY);
+ manageReorderOnDragOver(d, targetCellDistance, nearestDropOccupied, minSpanX, minSpanY,
+ reorderX, reorderY);
if (mDragMode == DRAG_MODE_CREATE_FOLDER || mDragMode == DRAG_MODE_ADD_TO_FOLDER ||
!nearestDropOccupied) {
@@ -2415,12 +2380,10 @@
}
protected void manageReorderOnDragOver(DragObject d, float targetCellDistance,
- boolean nearestDropOccupied, int minSpanX, int minSpanY) {
+ boolean nearestDropOccupied, int minSpanX, int minSpanY, int reorderX, int reorderY) {
ItemInfo item = d.dragInfo;
final View child = (mDragInfo == null) ? null : mDragInfo.cell;
- int reorderX = mTargetCell[0];
- int reorderY = mTargetCell[1];
if (!nearestDropOccupied) {
mDragTargetLayout.visualizeDropLocation(mTargetCell[0], mTargetCell[1],
item.spanX, item.spanY, d);
@@ -2429,12 +2392,11 @@
&& (mLastReorderX != reorderX || mLastReorderY != reorderY)
&& targetCellDistance < mDragTargetLayout.getReorderRadius(mTargetCell, item.spanX,
item.spanY)) {
-
- int[] resultSpan = new int[2];
+ mLastReorderX = reorderX;
+ mLastReorderY = reorderY;
mDragTargetLayout.performReorder((int) mDragViewVisualCenter[0],
(int) mDragViewVisualCenter[1], minSpanX, minSpanY, item.spanX, item.spanY,
- child, mTargetCell, resultSpan, CellLayout.MODE_SHOW_REORDER_HINT);
-
+ child, mTargetCell, new int[2], CellLayout.MODE_SHOW_REORDER_HINT);
// Otherwise, if we aren't adding to or creating a folder and there's no pending
// reorder, then we schedule a reorder
ReorderAlarmListener listener = new ReorderAlarmListener(mDragViewVisualCenter,
@@ -2495,10 +2457,10 @@
}
private boolean isDragObjectOverSmartSpace(DragObject dragObject) {
- if (mQsb == null) {
+ if (mFirstPagePinnedItem == null) {
return false;
}
- getViewBoundsRelativeToWorkspace(mQsb, mTempRect);
+ getViewBoundsRelativeToWorkspace(mFirstPagePinnedItem, mTempRect);
return mTempRect.contains(dragObject.x, dragObject.y);
}
@@ -2639,8 +2601,6 @@
mTargetCell = findNearestArea((int) mDragViewVisualCenter[0],
(int) mDragViewVisualCenter[1], minSpanX, minSpanY, mDragTargetLayout,
mTargetCell);
- mLastReorderX = mTargetCell[0];
- mLastReorderY = mTargetCell[1];
mTargetCell = mDragTargetLayout.performReorder((int) mDragViewVisualCenter[0],
(int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY,
@@ -3344,7 +3304,7 @@
public void widgetsRestored(final ArrayList<LauncherAppWidgetInfo> changedInfo) {
if (!changedInfo.isEmpty()) {
DeferredWidgetRefresh widgetRefresh = new DeferredWidgetRefresh(changedInfo,
- mLauncher.getAppWidgetHost());
+ mLauncher.getAppWidgetHolder());
LauncherAppWidgetInfo item = changedInfo.get(0);
final AppWidgetProviderInfo widgetInfo;
@@ -3421,7 +3381,7 @@
protected boolean canAnnouncePageDescription() {
// Disable announcements while overscrolling potentially to overlay screen because if we end
// up on the overlay screen, it will take care of announcing itself.
- return Float.compare(mOverlayTranslation, 0f) == 0;
+ return Float.compare(mOverlayProgress, 0f) == 0;
}
@Override
@@ -3470,19 +3430,19 @@
*/
private class DeferredWidgetRefresh implements Runnable, ProviderChangedListener {
private final ArrayList<LauncherAppWidgetInfo> mInfos;
- private final LauncherAppWidgetHost mHost;
+ private final LauncherWidgetHolder mWidgetHolder;
private final Handler mHandler;
private boolean mRefreshPending;
DeferredWidgetRefresh(ArrayList<LauncherAppWidgetInfo> infos,
- LauncherAppWidgetHost host) {
+ LauncherWidgetHolder holder) {
mInfos = infos;
- mHost = host;
+ mWidgetHolder = holder;
mHandler = mLauncher.mHandler;
mRefreshPending = true;
- mHost.addProviderChangeListener(this);
+ mWidgetHolder.addProviderChangeListener(this);
// Force refresh after 10 seconds, if we don't get the provider changed event.
// This could happen when the provider is no longer available in the app.
Message msg = Message.obtain(mHandler, this);
@@ -3492,7 +3452,7 @@
@Override
public void run() {
- mHost.removeProviderChangeListener(this);
+ mWidgetHolder.removeProviderChangeListener(this);
mHandler.removeCallbacks(this);
if (!mRefreshPending) {
diff --git a/src/com/android/launcher3/WorkspaceLayoutManager.java b/src/com/android/launcher3/WorkspaceLayoutManager.java
index 7e6e1b6..0b3a62f 100644
--- a/src/com/android/launcher3/WorkspaceLayoutManager.java
+++ b/src/com/android/launcher3/WorkspaceLayoutManager.java
@@ -19,6 +19,7 @@
import android.view.View;
import android.view.ViewGroup;
+import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.model.data.ItemInfo;
@@ -111,11 +112,11 @@
}
ViewGroup.LayoutParams genericLp = child.getLayoutParams();
- CellLayout.LayoutParams lp;
- if (genericLp == null || !(genericLp instanceof CellLayout.LayoutParams)) {
- lp = new CellLayout.LayoutParams(x, y, spanX, spanY);
+ CellLayoutLayoutParams lp;
+ if (genericLp == null || !(genericLp instanceof CellLayoutLayoutParams)) {
+ lp = new CellLayoutLayoutParams(x, y, spanX, spanY);
} else {
- lp = (CellLayout.LayoutParams) genericLp;
+ lp = (CellLayoutLayoutParams) genericLp;
lp.cellX = x;
lp.cellY = y;
lp.cellHSpan = spanX;
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index a991c2f..62e7ef3 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -29,11 +29,14 @@
import static com.android.launcher3.LauncherState.HINT_STATE;
import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.LauncherState.SPRING_LOADED;
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.config.FeatureFlags.HOME_GARDENING_WORKSPACE_BUTTONS;
+import static com.android.launcher3.config.FeatureFlags.SHOW_HOME_GARDENING;
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;
@@ -69,6 +72,8 @@
*/
public class WorkspaceStateTransitionAnimation {
+ private static final float FIRST_PAGE_PINNED_WIDGET_DISABLED_ALPHA = 0.3f;
+
private static final FloatProperty<Workspace<?>> WORKSPACE_SCALE_PROPERTY =
WORKSPACE_SCALE_PROPERTY_FACTORY.get(SCALE_INDEX_WORKSPACE_STATE);
@@ -155,6 +160,30 @@
float hotseatIconsAlpha = (elements & HOTSEAT_ICONS) != 0 ? 1 : 0;
propertySetter.setViewAlpha(hotseat, hotseatIconsAlpha, hotseatFadeInterpolator);
+ if (SHOW_HOME_GARDENING.get()) {
+ propertySetter.setViewAlpha(
+ mWorkspace.getFirstPagePinnedItem(),
+ state == SPRING_LOADED ? FIRST_PAGE_PINNED_WIDGET_DISABLED_ALPHA : 1,
+ workspaceFadeInterpolator);
+ propertySetter.addEndListener(success -> {
+ if (success) {
+ mWorkspace.getFirstPagePinnedItem().setClickable(state != SPRING_LOADED);
+ }
+ });
+ }
+
+ if (HOME_GARDENING_WORKSPACE_BUTTONS.get()) {
+ propertySetter.setViewAlpha(
+ mLauncher.getHotseat().getQsb(),
+ state == SPRING_LOADED ? 0 : 1,
+ workspaceFadeInterpolator);
+ propertySetter.addEndListener(success -> {
+ if (success) {
+ mLauncher.getHotseat().getQsb().setClickable(state != SPRING_LOADED);
+ }
+ });
+ }
+
// Update the accessibility flags for hotseat based on launcher state.
hotseat.setImportantForAccessibility(
state.hasFlag(FLAG_HOTSEAT_INACCESSIBLE)
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 79214e8..063b82e 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -1,5 +1,7 @@
package com.android.launcher3.accessibility;
+import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED;
+import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK;
import static com.android.launcher3.LauncherState.NORMAL;
@@ -23,6 +25,7 @@
import com.android.launcher3.PendingAddItemInfo;
import com.android.launcher3.R;
import com.android.launcher3.Workspace;
+import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.dragndrop.DragOptions.PreDragCondition;
import com.android.launcher3.dragndrop.DragView;
@@ -171,7 +174,11 @@
mContext.getDragLayer().getDescendantRectRelativeToSelf(host, pos);
ArrowPopup popup = OptionsPopupView.show(mContext, new RectF(pos), actions, false);
popup.requestFocus();
- popup.setOnCloseCallback(host::requestFocus);
+ popup.setOnCloseCallback(() -> {
+ host.requestFocus();
+ host.sendAccessibilityEvent(TYPE_VIEW_FOCUSED);
+ host.performAccessibilityAction(ACTION_ACCESSIBILITY_FOCUS, null);
+ });
return true;
} else if (action == DEEP_SHORTCUTS || action == SHORTCUTS_AND_NOTIFICATIONS) {
BubbleTextView btv = host instanceof BubbleTextView ? (BubbleTextView) host
@@ -244,7 +251,7 @@
}
private boolean performResizeAction(int action, View host, LauncherAppWidgetInfo info) {
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) host.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) host.getLayoutParams();
CellLayout layout = (CellLayout) host.getParent().getParent();
layout.markCellsAsUnoccupiedForView(host);
diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
index e33e44e..c86f08d 100644
--- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.allapps;
+import static com.android.launcher3.allapps.BaseAllAppsContainerView.AdapterHolder.SEARCH;
+
import android.content.Context;
import android.util.AttributeSet;
import android.view.KeyEvent;
@@ -34,7 +36,6 @@
import com.android.launcher3.views.AppLauncher;
import java.util.ArrayList;
-import java.util.Objects;
/**
* All apps container view with search support for use in a dragging activity.
@@ -44,6 +45,11 @@
public class ActivityAllAppsContainerView<T extends Context & AppLauncher
& DeviceProfileListenable> extends BaseAllAppsContainerView<T> {
+ private static final long DEFAULT_SEARCH_TRANSITION_DURATION_MS = 300;
+
+ // Used to animate Search results out and A-Z apps in, or vice-versa.
+ private final SearchTransitionController mSearchTransitionController;
+
protected SearchUiManager mSearchUiManager;
/**
* View that defines the search box. Result is rendered inside the recycler view defined in the
@@ -52,6 +58,7 @@
private View mSearchContainer;
/** {@code true} when rendered view is in search state instead of the scroll state. */
private boolean mIsSearching;
+ private boolean mRebindAdaptersAfterSearchAnimation;
public ActivityAllAppsContainerView(Context context) {
this(context, null);
@@ -63,6 +70,8 @@
public ActivityAllAppsContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
+
+ mSearchTransitionController = new SearchTransitionController(this);
}
public SearchUiManager getSearchUiManager() {
@@ -73,32 +82,65 @@
return mSearchContainer;
}
- /** Updates all apps container with the latest search query. */
- public void setLastSearchQuery(String query) {
- mIsSearching = true;
- rebindAdapters();
- mHeader.setCollapsed(true);
- }
-
/** Invoke when the current search session is finished. */
public void onClearSearchResult() {
- mIsSearching = false;
- mHeader.setCollapsed(false);
+ getMainAdapterProvider().clearHighlightedItem();
+ animateToSearchState(false);
rebindAdapters();
- mHeader.reset(false);
}
/**
* Sets results list for search
*/
public void setSearchResults(ArrayList<AdapterItem> results) {
+ getMainAdapterProvider().clearHighlightedItem();
if (getSearchResultList().setSearchResults(results)) {
- for (int i = 0; i < mAH.size(); i++) {
- if (mAH.get(i).mRecyclerView != null) {
- mAH.get(i).mRecyclerView.onSearchResultsChanged();
- }
- }
+ getSearchRecyclerView().onSearchResultsChanged();
}
+ if (results != null) {
+ animateToSearchState(true);
+ }
+ }
+
+ /**
+ * Sets results list for search.
+ *
+ * @param searchResultCode indicates if the result is final or intermediate for a given query
+ * since we can get search results from multiple sources.
+ */
+ public void setSearchResults(ArrayList<AdapterItem> results, int searchResultCode) {
+ setSearchResults(results);
+ }
+
+ private void animateToSearchState(boolean goingToSearch) {
+ animateToSearchState(goingToSearch, DEFAULT_SEARCH_TRANSITION_DURATION_MS);
+ }
+
+ private void animateToSearchState(boolean goingToSearch, long durationMs) {
+ if (!mSearchTransitionController.isRunning() && goingToSearch == isSearching()) {
+ return;
+ }
+ if (goingToSearch) {
+ // Fade out the button to pause work apps.
+ mWorkManager.onActivePageChanged(SEARCH);
+ }
+ mSearchTransitionController.animateToSearchState(goingToSearch, durationMs,
+ /* onEndRunnable = */ () -> {
+ mIsSearching = goingToSearch;
+ updateSearchResultsVisibility();
+ int previousPage = getCurrentPage();
+ if (mRebindAdaptersAfterSearchAnimation) {
+ rebindAdapters(false);
+ mRebindAdaptersAfterSearchAnimation = false;
+ }
+ if (!goingToSearch) {
+ setSearchResults(null);
+ if (mViewPager != null) {
+ mViewPager.setCurrentPage(previousPage);
+ }
+ onActivePageChanged(previousPage);
+ }
+ });
}
@Override
@@ -121,6 +163,8 @@
super.reset(animate);
// Reset the search bar after transitioning home.
mSearchUiManager.resetSearch();
+ // Animate to A-Z with 0 time to reset the animation with proper state management.
+ animateToSearchState(false, 0);
}
@Override
@@ -147,31 +191,35 @@
}
@Override
- protected boolean shouldShowTabs() {
- return super.shouldShowTabs() && !isSearching();
- }
-
- @Override
public boolean isSearching() {
return mIsSearching;
}
@Override
+ public void onActivePageChanged(int currentActivePage) {
+ if (mSearchTransitionController.isRunning()) {
+ // Will be called at the end of the animation.
+ return;
+ }
+ super.onActivePageChanged(currentActivePage);
+ }
+
+ @Override
protected void rebindAdapters(boolean force) {
+ if (mSearchTransitionController.isRunning()) {
+ mRebindAdaptersAfterSearchAnimation = true;
+ return;
+ }
super.rebindAdapters(force);
if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()
- || getMainAdapterProvider().getDecorator() == null) {
+ || getMainAdapterProvider().getDecorator() == null
+ || getSearchRecyclerView() == null) {
return;
}
RecyclerView.ItemDecoration decoration = getMainAdapterProvider().getDecorator();
- mAH.stream()
- .map(adapterHolder -> adapterHolder.mRecyclerView)
- .filter(Objects::nonNull)
- .forEach(v -> {
- v.removeItemDecoration(decoration); // Remove in case it is already added.
- v.addItemDecoration(decoration);
- });
+ getSearchRecyclerView().removeItemDecoration(decoration); // In case it is already added.
+ getSearchRecyclerView().addItemDecoration(decoration);
}
@Override
@@ -273,7 +321,7 @@
layoutParams.topMargin =
includeTabsMargin
? getContext().getResources().getDimensionPixelSize(
- R.dimen.all_apps_header_pill_height)
+ R.dimen.all_apps_header_pill_height)
: 0;
}
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index 33d2f2b..e34d4c8 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -26,9 +26,12 @@
import androidx.core.view.accessibility.AccessibilityRecordCompat;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.RecyclerView.Adapter;
+import com.android.launcher3.util.ScrollableLayoutManager;
import com.android.launcher3.views.ActivityContext;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -40,32 +43,61 @@
BaseAllAppsAdapter<T> {
public static final String TAG = "AppsGridAdapter";
- private final GridLayoutManager mGridLayoutMgr;
- private final GridSpanSizer mGridSizer;
+ private final AppsGridLayoutManager mGridLayoutMgr;
+ private final List<OnLayoutCompletedListener> mOnLayoutCompletedListeners = new ArrayList<>();
+
+ /**
+ * Listener for {@link RecyclerView.LayoutManager#onLayoutCompleted(RecyclerView.State)}
+ */
+ public interface OnLayoutCompletedListener {
+ void onLayoutCompleted();
+ }
+
+ /**
+ * Adds a {@link OnLayoutCompletedListener} to receive a callback when {@link
+ * RecyclerView.LayoutManager#onLayoutCompleted(RecyclerView.State)} is called
+ */
+ public void addOnLayoutCompletedListener(OnLayoutCompletedListener listener) {
+ mOnLayoutCompletedListeners.add(listener);
+ }
+
+ /**
+ * Removes a {@link OnLayoutCompletedListener} to not receive a callback when {@link
+ * RecyclerView.LayoutManager#onLayoutCompleted(RecyclerView.State)} is called
+ */
+ public void removeOnLayoutCompletedListener(OnLayoutCompletedListener listener) {
+ mOnLayoutCompletedListeners.remove(listener);
+ }
+
public AllAppsGridAdapter(T activityContext, LayoutInflater inflater,
AlphabeticalAppsList apps, BaseAdapterProvider[] adapterProviders) {
super(activityContext, inflater, apps, adapterProviders);
- mGridSizer = new GridSpanSizer();
mGridLayoutMgr = new AppsGridLayoutManager(mActivityContext);
- mGridLayoutMgr.setSpanSizeLookup(mGridSizer);
+ mGridLayoutMgr.setSpanSizeLookup(new GridSpanSizer());
setAppsPerRow(activityContext.getDeviceProfile().numShownAllAppsColumns);
}
/**
* Returns the grid layout manager.
*/
- public RecyclerView.LayoutManager getLayoutManager() {
+ public AppsGridLayoutManager getLayoutManager() {
return mGridLayoutMgr;
}
+ /** @return the column index that the given adapter index falls. */
+ public int getSpanIndex(int adapterIndex) {
+ AppsGridLayoutManager lm = getLayoutManager();
+ return lm.getSpanSizeLookup().getSpanIndex(adapterIndex, lm.getSpanCount());
+ }
+
/**
* A subclass of GridLayoutManager that overrides accessibility values during app search.
*/
- public class AppsGridLayoutManager extends GridLayoutManager {
+ public class AppsGridLayoutManager extends ScrollableLayoutManager {
public AppsGridLayoutManager(Context context) {
- super(context, 1, GridLayoutManager.VERTICAL, false);
+ super(context);
}
@Override
@@ -125,6 +157,23 @@
}
return extraRows;
}
+
+ @Override
+ public void onLayoutCompleted(RecyclerView.State state) {
+ super.onLayoutCompleted(state);
+ for (OnLayoutCompletedListener listener : mOnLayoutCompletedListeners) {
+ listener.onLayoutCompleted();
+ }
+ }
+
+ @Override
+ protected int incrementTotalHeight(Adapter adapter, int position, int heightUntilLastPos) {
+ AllAppsGridAdapter.AdapterItem item = mApps.getAdapterItems().get(position);
+ // only account for the first icon in the row since they are the same size within a row
+ return (isIconViewType(item.viewType) && item.rowAppIndex != 0)
+ ? heightUntilLastPos
+ : (heightUntilLastPos + mCachedSizes.get(item.viewType));
+ }
}
@Override
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index 31e3890..ac10892 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -15,23 +15,24 @@
*/
package com.android.launcher3.allapps;
-import static android.view.View.MeasureSpec.UNSPECIFIED;
-
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_SCROLLED;
+import static com.android.launcher3.logger.LauncherAtom.ContainerInfo;
+import static com.android.launcher3.logger.LauncherAtom.SearchResultContainer;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_SCROLLED_DOWN;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_SCROLLED_UNKNOWN_DIRECTION;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_SCROLLED_UP;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_VERTICAL_SWIPE_BEGIN;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_VERTICAL_SWIPE_END;
import static com.android.launcher3.util.LogConfig.SEARCH_LOGGING;
-import static com.android.launcher3.util.UiThreadHelper.hideKeyboardAsync;
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.util.Log;
-import android.util.SparseIntArray;
import androidx.recyclerview.widget.RecyclerView;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.ExtendedEditText;
import com.android.launcher3.FastScrollRecyclerView;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
@@ -50,40 +51,11 @@
private static final boolean DEBUG = false;
private static final boolean DEBUG_LATENCY = Utilities.isPropertyEnabled(SEARCH_LOGGING);
- protected AlphabeticalAppsList<?> mApps;
protected final int mNumAppsPerRow;
-
- // The specific view heights that we use to calculate scroll
- private final SparseIntArray mViewHeights = new SparseIntArray();
- private final SparseIntArray mCachedScrollPositions = new SparseIntArray();
private final AllAppsFastScrollHelper mFastScrollHelper;
+ private int mCumulativeVerticalScroll;
-
- private final AdapterDataObserver mObserver = new RecyclerView.AdapterDataObserver() {
- public void onChanged() {
- mCachedScrollPositions.clear();
- }
-
- @Override
- public void onItemRangeChanged(int positionStart, int itemCount) {
- onChanged();
- }
-
- @Override
- public void onItemRangeInserted(int positionStart, int itemCount) {
- onChanged();
- }
-
- @Override
- public void onItemRangeRemoved(int positionStart, int itemCount) {
- onChanged();
- }
-
- @Override
- public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
- onChanged();
- }
- };
+ protected AlphabeticalAppsList<?> mApps;
public AllAppsRecyclerView(Context context) {
this(context, null);
@@ -123,12 +95,8 @@
pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_ALL_APPS_DIVIDER, 1);
pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_ICON, approxRows
* (mNumAppsPerRow + 1));
-
- mViewHeights.clear();
- mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_ICON, grid.allAppsCellHeightPx);
}
-
@Override
public void onDraw(Canvas c) {
if (DEBUG) {
@@ -158,20 +126,26 @@
StatsLogManager mgr = ActivityContext.lookupContext(getContext()).getStatsLogManager();
switch (state) {
case SCROLL_STATE_DRAGGING:
- mgr.logger().log(LAUNCHER_ALLAPPS_SCROLLED);
+ mCumulativeVerticalScroll = 0;
requestFocus();
mgr.logger().sendToInteractionJankMonitor(
LAUNCHER_ALLAPPS_VERTICAL_SWIPE_BEGIN, this);
- hideKeyboardAsync(ActivityContext.lookupContext(getContext()),
- getApplicationWindowToken());
+ ActivityContext.lookupContext(getContext()).hideKeyboard();
break;
case SCROLL_STATE_IDLE:
mgr.logger().sendToInteractionJankMonitor(
LAUNCHER_ALLAPPS_VERTICAL_SWIPE_END, this);
+ logCumulativeVerticalScroll();
break;
}
}
+ @Override
+ public void onScrolled(int dx, int dy) {
+ super.onScrolled(dx, dy);
+ mCumulativeVerticalScroll += dy;
+ }
+
/**
* Maps the touch (from 0..1) to the adapter position that should be visible.
*/
@@ -202,17 +176,6 @@
}
@Override
- public void setAdapter(Adapter adapter) {
- if (getAdapter() != null) {
- getAdapter().unregisterAdapterDataObserver(mObserver);
- }
- super.setAdapter(adapter);
- if (adapter != null) {
- adapter.registerAdapterDataObserver(mObserver);
- }
- }
-
- @Override
protected boolean isPaddingOffsetRequired() {
return true;
}
@@ -233,13 +196,13 @@
List<AllAppsGridAdapter.AdapterItem> items = mApps.getAdapterItems();
// Skip early if there are no items or we haven't been measured
- if (items.isEmpty() || mNumAppsPerRow == 0) {
+ if (items.isEmpty() || mNumAppsPerRow == 0 || getChildCount() == 0) {
mScrollbar.setThumbOffsetY(-1);
return;
}
// Skip early if, there no child laid out in the container.
- int scrollY = getCurrentScrollY();
+ int scrollY = computeVerticalScrollOffset();
if (scrollY < 0) {
mScrollbar.setThumbOffsetY(-1);
return;
@@ -294,51 +257,6 @@
}
}
- @Override
- protected int getItemsHeight(int position) {
- List<AllAppsGridAdapter.AdapterItem> items = mApps.getAdapterItems();
- AllAppsGridAdapter.AdapterItem posItem = position < items.size()
- ? items.get(position) : null;
- int y = mCachedScrollPositions.get(position, -1);
- if (y < 0) {
- y = 0;
- for (int i = 0; i < position; i++) {
- AllAppsGridAdapter.AdapterItem item = items.get(i);
- if (AllAppsGridAdapter.isIconViewType(item.viewType)) {
- // Break once we reach the desired row
- if (posItem != null && posItem.viewType == item.viewType &&
- posItem.rowIndex == item.rowIndex) {
- break;
- }
- // Otherwise, only account for the first icon in the row since they are the same
- // size within a row
- if (item.rowAppIndex == 0) {
- y += mViewHeights.get(item.viewType, 0);
- }
- } else {
- // Rest of the views span the full width
- int elHeight = mViewHeights.get(item.viewType);
- if (elHeight == 0) {
- ViewHolder holder = findViewHolderForAdapterPosition(i);
- if (holder == null) {
- holder = getAdapter().createViewHolder(this, item.viewType);
- getAdapter().onBindViewHolder(holder, i);
- holder.itemView.measure(UNSPECIFIED, UNSPECIFIED);
- elHeight = holder.itemView.getMeasuredHeight();
-
- getRecycledViewPool().putRecycledView(holder);
- } else {
- elHeight = holder.itemView.getMeasuredHeight();
- }
- }
- y += elHeight;
- }
- }
- mCachedScrollPositions.put(position, y);
- }
- return y;
- }
-
public int getScrollBarTop() {
return getResources().getDimensionPixelOffset(R.dimen.all_apps_header_top_padding);
}
@@ -351,4 +269,21 @@
public boolean hasOverlappingRendering() {
return false;
}
+
+ private void logCumulativeVerticalScroll() {
+ ActivityContext context = ActivityContext.lookupContext(getContext());
+ StatsLogManager mgr = context.getStatsLogManager();
+ ExtendedEditText editText = context.getAppsView().getSearchUiManager().getEditText();
+ ContainerInfo containerInfo = ContainerInfo.newBuilder().setSearchResultContainer(
+ SearchResultContainer
+ .newBuilder()
+ .setQueryLength((editText == null) ? -1 : editText.length())).build();
+
+ // mCumulativeVerticalScroll == 0 when user comes back to original position, we don't
+ // know the direction of scrolling.
+ mgr.logger().withContainerInfo(containerInfo).log(
+ mCumulativeVerticalScroll == 0 ? LAUNCHER_ALLAPPS_SCROLLED_UNKNOWN_DIRECTION
+ : (mCumulativeVerticalScroll > 0) ? LAUNCHER_ALLAPPS_SCROLLED_DOWN
+ : LAUNCHER_ALLAPPS_SCROLLED_UP);
+ }
}
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index ecc9d7e..9930abe 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -15,6 +15,7 @@
*/
package com.android.launcher3.allapps;
+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.NORMAL;
@@ -39,10 +40,10 @@
import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.PropertySetter;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.statemanager.StateManager.StateHandler;
import com.android.launcher3.states.StateAnimationConfig;
-import com.android.launcher3.util.MultiAdditivePropertyFactory;
+import com.android.launcher3.util.MultiPropertyFactory;
+import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.launcher3.views.ScrimView;
@@ -75,6 +76,8 @@
}
};
+ private static final float ALL_APPS_PULL_BACK_TRANSLATION_DEFAULT = 0f;
+
public static final FloatProperty<AllAppsTransitionController> ALL_APPS_PULL_BACK_TRANSLATION =
new FloatProperty<AllAppsTransitionController>("allAppsPullBackTranslation") {
@@ -83,8 +86,7 @@
if (controller.mIsTablet) {
return controller.mAppsView.getActiveRecyclerView().getTranslationY();
} else {
- return controller.getAppsViewPullbackTranslationY().get(
- controller.mAppsView);
+ return controller.getAppsViewPullbackTranslationY().getValue();
}
}
@@ -92,13 +94,18 @@
public void setValue(AllAppsTransitionController controller, float translation) {
if (controller.mIsTablet) {
controller.mAppsView.getActiveRecyclerView().setTranslationY(translation);
+ controller.getAppsViewPullbackTranslationY().setValue(
+ ALL_APPS_PULL_BACK_TRANSLATION_DEFAULT);
} else {
- controller.getAppsViewPullbackTranslationY().set(controller.mAppsView,
- translation);
+ controller.getAppsViewPullbackTranslationY().setValue(translation);
+ controller.mAppsView.getActiveRecyclerView().setTranslationY(
+ ALL_APPS_PULL_BACK_TRANSLATION_DEFAULT);
}
}
};
+ private static final float ALL_APPS_PULL_BACK_ALPHA_DEFAULT = 1f;
+
public static final FloatProperty<AllAppsTransitionController> ALL_APPS_PULL_BACK_ALPHA =
new FloatProperty<AllAppsTransitionController>("allAppsPullBackAlpha") {
@@ -115,8 +122,12 @@
public void setValue(AllAppsTransitionController controller, float alpha) {
if (controller.mIsTablet) {
controller.mAppsView.getActiveRecyclerView().setAlpha(alpha);
+ controller.getAppsViewPullbackAlpha().setValue(
+ ALL_APPS_PULL_BACK_ALPHA_DEFAULT);
} else {
controller.getAppsViewPullbackAlpha().setValue(alpha);
+ controller.mAppsView.getActiveRecyclerView().setAlpha(
+ ALL_APPS_PULL_BACK_ALPHA_DEFAULT);
}
}
};
@@ -130,6 +141,9 @@
private final Launcher mLauncher;
private boolean mIsVerticalLayout;
+ // Whether this class should take care of closing the keyboard.
+ private boolean mShouldControlKeyboard;
+
// Animation in this class is controlled by a single variable {@link mProgress}.
// Visually, it represents top y coordinate of the all apps container if multiplied with
// {@link mShiftRange}.
@@ -141,10 +155,8 @@
private ScrimView mScrimView;
- private final MultiAdditivePropertyFactory<View>
- mAppsViewTranslationYPropertyFactory = new MultiAdditivePropertyFactory<>(
- "appsViewTranslationY", View.TRANSLATION_Y);
private MultiValueAlpha mAppsViewAlpha;
+ private MultiPropertyFactory<View> mAppsViewTranslationY;
private boolean mIsTablet;
@@ -185,7 +197,7 @@
*/
public void setProgress(float progress) {
mProgress = progress;
- getAppsViewProgressTranslationY().set(mAppsView, mProgress * mShiftRange);
+ getAppsViewProgressTranslationY().setValue(mProgress * mShiftRange);
mLauncher.onAllAppsTransition(1 - progress);
}
@@ -193,20 +205,20 @@
return mProgress;
}
- private FloatProperty<View> getAppsViewProgressTranslationY() {
- return mAppsViewTranslationYPropertyFactory.get(INDEX_APPS_VIEW_PROGRESS);
+ private MultiProperty getAppsViewProgressTranslationY() {
+ return mAppsViewTranslationY.get(INDEX_APPS_VIEW_PROGRESS);
}
- private FloatProperty<View> getAppsViewPullbackTranslationY() {
- return mAppsViewTranslationYPropertyFactory.get(INDEX_APPS_VIEW_PULLBACK);
+ private MultiProperty getAppsViewPullbackTranslationY() {
+ return mAppsViewTranslationY.get(INDEX_APPS_VIEW_PULLBACK);
}
- private MultiValueAlpha.AlphaProperty getAppsViewProgressAlpha() {
- return mAppsViewAlpha.getProperty(INDEX_APPS_VIEW_PROGRESS);
+ private MultiProperty getAppsViewProgressAlpha() {
+ return mAppsViewAlpha.get(INDEX_APPS_VIEW_PROGRESS);
}
- private MultiValueAlpha.AlphaProperty getAppsViewPullbackAlpha() {
- return mAppsViewAlpha.getProperty(INDEX_APPS_VIEW_PULLBACK);
+ private MultiProperty getAppsViewPullbackAlpha() {
+ return mAppsViewAlpha.get(INDEX_APPS_VIEW_PULLBACK);
}
/**
@@ -227,18 +239,23 @@
@Override
public void setStateWithAnimation(LauncherState toState,
StateAnimationConfig config, PendingAnimation builder) {
- if (NORMAL.equals(toState) && mLauncher.isInState(ALL_APPS)) {
+ if (mLauncher.isInState(ALL_APPS) && !ALL_APPS.equals(toState)) {
+ // For atomic animations, we close the keyboard immediately.
+ if (!config.userControlled && mShouldControlKeyboard) {
+ mLauncher.getAppsView().getSearchUiManager().getEditText().hideKeyboard();
+ }
+
builder.addEndListener(success -> {
// Reset pull back progress and alpha after switching states.
- ALL_APPS_PULL_BACK_TRANSLATION.set(this, 0f);
- ALL_APPS_PULL_BACK_ALPHA.set(this, 1f);
+ ALL_APPS_PULL_BACK_TRANSLATION.set(this, ALL_APPS_PULL_BACK_TRANSLATION_DEFAULT);
+ ALL_APPS_PULL_BACK_ALPHA.set(this, ALL_APPS_PULL_BACK_ALPHA_DEFAULT);
// We only want to close the keyboard if the animation has completed successfully.
// The reason is that with keyboard sync, if the user swipes down from All Apps with
// the keyboard open and then changes their mind and swipes back up, we want the
// keyboard to remain open. However an onCancel signal is sent to the listeners
// (success = false), so we need to check for that.
- if (success) {
+ if (config.userControlled && success && mShouldControlKeyboard) {
mLauncher.getAppsView().getSearchUiManager().getEditText().hideKeyboard();
}
});
@@ -279,7 +296,7 @@
boolean hasAllAppsContent = (visibleElements & ALL_APPS_CONTENT) != 0;
Interpolator allAppsFade = config.getInterpolator(ANIM_ALL_APPS_FADE, LINEAR);
- setter.setFloat(getAppsViewProgressAlpha(), MultiValueAlpha.VALUE,
+ setter.setFloat(getAppsViewProgressAlpha(), MultiPropertyFactory.MULTI_PROPERTY_VALUE,
hasAllAppsContent ? 1 : 0, allAppsFade);
boolean shouldProtectHeader =
@@ -298,8 +315,13 @@
mScrimView = scrimView;
mAppsView = appsView;
mAppsView.setScrimView(scrimView);
+
mAppsViewAlpha = new MultiValueAlpha(mAppsView, APPS_VIEW_INDEX_COUNT);
mAppsViewAlpha.setUpdateVisibility(true);
+ mAppsViewTranslationY = new MultiPropertyFactory<>(
+ mAppsView, VIEW_TRANSLATE_Y, APPS_VIEW_INDEX_COUNT, Float::sum);
+
+ mShouldControlKeyboard = !mLauncher.getSearchConfig().isKeyboardSyncEnabled();
}
/**
@@ -314,10 +336,11 @@
* TODO: This logic should go in {@link LauncherState}
*/
private void onProgressAnimationEnd() {
- if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) return;
if (Float.compare(mProgress, 1f) == 0) {
mAppsView.reset(false /* animate */);
- mLauncher.getAppsView().getSearchUiManager().getEditText().hideKeyboard();
+ if (mShouldControlKeyboard) {
+ mLauncher.getAppsView().getSearchUiManager().getEditText().hideKeyboard();
+ }
}
}
}
diff --git a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
index 6990e57..42f8b0c 100644
--- a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
+++ b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
@@ -127,6 +127,11 @@
public boolean isContentSame(AdapterItem other) {
return itemInfo == null && other.itemInfo == null;
}
+
+ /** Sets the alpha of the decorator for this item. Returns true if successful. */
+ public boolean setDecorationFillAlpha(int alpha) {
+ return false;
+ }
}
protected final T mActivityContext;
diff --git a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
index f3c5dd6..da86d98 100644
--- a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
@@ -15,9 +15,9 @@
*/
package com.android.launcher3.allapps;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_COUNT;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_TAP_ON_PERSONAL_TAB;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_TAP_ON_WORK_TAB;
-import static com.android.launcher3.util.UiThreadHelper.hideKeyboardAsync;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -89,6 +89,9 @@
public static final float PULL_MULTIPLIER = .02f;
public static final float FLING_VELOCITY_MULTIPLIER = 1200f;
+ // Render the header protection at all times to debug clipping issues.
+ private static final boolean DEBUG_HEADER_PROTECTION = false;
+
private final Paint mHeaderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final Rect mInsets = new Rect();
@@ -104,15 +107,16 @@
new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
- updateHeaderScroll(((AllAppsRecyclerView) recyclerView).getCurrentScrollY());
+ updateHeaderScroll(recyclerView.computeVerticalScrollOffset());
}
};
- private final WorkProfileManager mWorkManager;
+
+ protected final WorkProfileManager mWorkManager;
private final Paint mNavBarScrimPaint;
private int mNavBarScrimHeight = 0;
- private AllAppsPagedView mViewPager;
+ protected AllAppsPagedView mViewPager;
private SearchRecyclerView mSearchRecyclerView;
protected FloatingHeaderView mHeader;
@@ -128,7 +132,6 @@
private final int mScrimColor;
private final int mHeaderProtectionColor;
protected final float mHeaderThreshold;
- private int mHeaderBottomAdjustment;
private ScrimView mScrimView;
private int mHeaderColor;
private int mTabsProtectionAlpha;
@@ -141,8 +144,6 @@
mScrimColor = Themes.getAttrColor(context, R.attr.allAppsScrimColor);
mHeaderThreshold = getResources().getDimensionPixelSize(
R.dimen.dynamic_grid_cell_border_spacing);
- mHeaderBottomAdjustment = getResources().getDimensionPixelSize(
- R.dimen.all_apps_header_bottom_adjustment);
mHeaderProtectionColor = Themes.getAttrColor(context, R.attr.allappsHeaderProtectionColor);
mWorkManager = new WorkProfileManager(
@@ -190,7 +191,6 @@
reset(true);
}
}
-
}
@Override
@@ -244,6 +244,10 @@
mWorkManager.reset();
}
}
+
+ mActivityContext.getStatsLogManager().logger()
+ .withCardinality(mAllAppsStore.getApps().length)
+ .log(LAUNCHER_ALLAPPS_COUNT);
}
/**
@@ -349,7 +353,7 @@
* The container for A-Z apps (the ViewPager for main+work tabs, or main RV). This is currently
* hidden while searching.
**/
- private View getAppsRecyclerViewContainer() {
+ protected View getAppsRecyclerViewContainer() {
return mViewPager != null ? mViewPager : findViewById(R.id.apps_list_view);
}
@@ -491,6 +495,7 @@
mAllAppsStore.unregisterIconContainer(mAH.get(AdapterHolder.MAIN).mRecyclerView);
mAllAppsStore.unregisterIconContainer(mAH.get(AdapterHolder.WORK).mRecyclerView);
+ mAllAppsStore.unregisterIconContainer(mAH.get(AdapterHolder.SEARCH).mRecyclerView);
if (mUsingTabs) {
mAH.get(AdapterHolder.MAIN).setup(mViewPager.getChildAt(0), mPersonalMatcher);
@@ -503,8 +508,7 @@
mActivityContext.getStatsLogManager().logger()
.log(LAUNCHER_ALLAPPS_TAP_ON_PERSONAL_TAB);
}
- hideKeyboardAsync(ActivityContext.lookupContext(getContext()),
- getApplicationWindowToken());
+ mActivityContext.hideKeyboard();
});
findViewById(R.id.tab_work)
.setOnClickListener((View view) -> {
@@ -512,8 +516,7 @@
mActivityContext.getStatsLogManager().logger()
.log(LAUNCHER_ALLAPPS_TAP_ON_WORK_TAB);
}
- hideKeyboardAsync(ActivityContext.lookupContext(getContext()),
- getApplicationWindowToken());
+ mActivityContext.hideKeyboard();
});
setDeviceManagementResources();
onActivePageChanged(mViewPager.getNextPage());
@@ -525,15 +528,18 @@
mAllAppsStore.registerIconContainer(mAH.get(AdapterHolder.MAIN).mRecyclerView);
mAllAppsStore.registerIconContainer(mAH.get(AdapterHolder.WORK).mRecyclerView);
+ mAllAppsStore.registerIconContainer(mAH.get(AdapterHolder.SEARCH).mRecyclerView);
}
- private void updateSearchResultsVisibility() {
+ protected void updateSearchResultsVisibility() {
if (isSearching()) {
getSearchRecyclerView().setVisibility(VISIBLE);
getAppsRecyclerViewContainer().setVisibility(GONE);
+ mHeader.setVisibility(GONE);
} else {
getSearchRecyclerView().setVisibility(GONE);
getAppsRecyclerViewContainer().setVisibility(VISIBLE);
+ mHeader.setVisibility(VISIBLE);
}
if (mHeader.isSetUp()) {
mHeader.setActiveRV(getCurrentPage());
@@ -727,17 +733,29 @@
if (!mHeader.isHeaderProtectionSupported()) {
return;
}
- mHeaderPaint.setColor(mHeaderColor);
- mHeaderPaint.setAlpha((int) (getAlpha() * Color.alpha(mHeaderColor)));
+ if (DEBUG_HEADER_PROTECTION) {
+ mHeaderPaint.setColor(Color.MAGENTA);
+ mHeaderPaint.setAlpha(255);
+ } else {
+ mHeaderPaint.setColor(mHeaderColor);
+ mHeaderPaint.setAlpha((int) (getAlpha() * Color.alpha(mHeaderColor)));
+ }
if (mHeaderPaint.getColor() != mScrimColor && mHeaderPaint.getColor() != 0) {
int bottom = getHeaderBottom();
+ FloatingHeaderView headerView = getFloatingHeaderView();
if (!mUsingTabs) {
- bottom += getFloatingHeaderView().getPaddingBottom() - mHeaderBottomAdjustment;
+ // Add protection which is otherwise added when tabs scroll up.
+ bottom += headerView.getTabsAdditionalPaddingTop();
}
canvas.drawRect(0, 0, canvas.getWidth(), bottom, mHeaderPaint);
- int tabsHeight = getFloatingHeaderView().getPeripheralProtectionHeight();
+ int tabsHeight = headerView.getPeripheralProtectionHeight();
if (mTabsProtectionAlpha > 0 && tabsHeight != 0) {
- mHeaderPaint.setAlpha((int) (getAlpha() * mTabsProtectionAlpha));
+ if (DEBUG_HEADER_PROTECTION) {
+ mHeaderPaint.setColor(Color.BLUE);
+ mHeaderPaint.setAlpha(255);
+ } else {
+ mHeaderPaint.setAlpha((int) (getAlpha() * mTabsProtectionAlpha));
+ }
canvas.drawRect(0, bottom, canvas.getWidth(), bottom + tabsHeight, mHeaderPaint);
}
}
@@ -784,6 +802,21 @@
return mActivityContext.getDeviceProfile().isTablet ? mBottomSheetBackground : this;
}
+ /**
+ * Sets whether the view or its children should react to the window inset.
+ * Used for when exiting all apps -> workspace and determines if window inset
+ * should be applied.. ex) the work mode switch.
+ */
+ public void setApplyWindowInset(boolean shouldApplyWindowInset) {
+ if (mWorkManager.getWorkModeSwitch() != null) {
+ mWorkManager.getWorkModeSwitch().setApplyWindowInset(shouldApplyWindowInset);
+ }
+ }
+
+ protected void onInitializeRecyclerView(RecyclerView rv) {
+ rv.addOnScrollListener(mScrollListener);
+ }
+
/** Holds a {@link BaseAllAppsAdapter} and related fields. */
public class AdapterHolder {
public static final int MAIN = 0;
@@ -820,7 +853,7 @@
mRecyclerView.setHasFixedSize(true);
// No animations will occur when changes occur to the items in this RecyclerView.
mRecyclerView.setItemAnimator(null);
- mRecyclerView.addOnScrollListener(mScrollListener);
+ onInitializeRecyclerView(mRecyclerView);
FocusedItemDecorator focusedItemDecorator = new FocusedItemDecorator(mRecyclerView);
mRecyclerView.addItemDecoration(focusedItemDecorator);
mAdapter.setIconFocusListener(focusedItemDecorator.getFocusListener());
diff --git a/src/com/android/launcher3/allapps/BaseSearchConfig.java b/src/com/android/launcher3/allapps/BaseSearchConfig.java
new file mode 100644
index 0000000..3900954
--- /dev/null
+++ b/src/com/android/launcher3/allapps/BaseSearchConfig.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.allapps;
+
+/** Base config values for search. */
+public class BaseSearchConfig {
+ public BaseSearchConfig() {}
+
+ /**
+ * Returns whether to enable the synchronized keyboard transition between Home and All Apps.
+ */
+ public boolean isKeyboardSyncEnabled() {
+ return false;
+ }
+
+ /**
+ * Returns whether IME is enabled on swipe up.
+ */
+ public boolean isImeEnabledOnSwipeUp() {
+ return false;
+ }
+}
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderView.java b/src/com/android/launcher3/allapps/FloatingHeaderView.java
index c5bdb69..1cbb0f9 100644
--- a/src/com/android/launcher3/allapps/FloatingHeaderView.java
+++ b/src/com/android/launcher3/allapps/FloatingHeaderView.java
@@ -42,6 +42,7 @@
import com.android.systemui.plugins.PluginListener;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Map;
public class FloatingHeaderView extends LinearLayout implements
@@ -67,7 +68,7 @@
mAnimator.cancel();
}
- int current = -mCurrentRV.getCurrentScrollY();
+ int current = -mCurrentRV.computeVerticalScrollOffset();
boolean headerCollapsed = mHeaderCollapsed;
moved(current);
applyVerticalMove();
@@ -82,8 +83,8 @@
protected final Map<AllAppsRow, PluginHeaderRow> mPluginRows = new ArrayMap<>();
// These two values are necessary to ensure that the header protection is drawn correctly.
- private final int mHeaderTopAdjustment;
- private final int mHeaderBottomAdjustment;
+ private final int mTabsAdditionalPaddingTop;
+ private final int mTabsAdditionalPaddingBottom;
private boolean mHeaderProtectionSupported;
protected ViewGroup mTabLayout;
@@ -91,7 +92,6 @@
private AllAppsRecyclerView mWorkRV;
private SearchRecyclerView mSearchRV;
private AllAppsRecyclerView mCurrentRV;
- public boolean mHeaderCollapsed;
protected int mSnappedScrolledY;
private int mTranslationY;
@@ -100,7 +100,12 @@
protected boolean mTabsHidden;
protected int mMaxTranslation;
- private boolean mCollapsed = false;
+ // Whether the header has been scrolled off-screen.
+ private boolean mHeaderCollapsed;
+ // Whether floating rows like predicted apps are hidden.
+ private boolean mFloatingRowsCollapsed;
+ // Total height of all current floating rows. Collapsed rows == 0 height.
+ private int mFloatingRowsHeight;
// This is initialized once during inflation and stays constant after that. Fixed views
// cannot be added or removed dynamically.
@@ -117,9 +122,9 @@
public FloatingHeaderView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
- mHeaderTopAdjustment = context.getResources()
+ mTabsAdditionalPaddingTop = context.getResources()
.getDimensionPixelSize(R.dimen.all_apps_header_top_adjustment);
- mHeaderBottomAdjustment = context.getResources()
+ mTabsAdditionalPaddingBottom = context.getResources()
.getDimensionPixelSize(R.dimen.all_apps_header_bottom_adjustment);
mHeaderProtectionSupported = context.getResources().getBoolean(
R.bool.config_header_protection_supported);
@@ -148,6 +153,7 @@
}
mFixedRows = rows.toArray(new FloatingHeaderRow[rows.size()]);
mAllRows = mFixedRows;
+ updateFloatingRowsHeight();
}
@Override
@@ -179,6 +185,7 @@
count++;
}
}
+ updateFloatingRowsHeight();
}
@Override
@@ -195,7 +202,7 @@
int oldMaxHeight = mMaxTranslation;
updateExpectedHeight();
- if (mMaxTranslation != oldMaxHeight || mCollapsed) {
+ if (mMaxTranslation != oldMaxHeight || mFloatingRowsCollapsed) {
BaseAllAppsContainerView<?> parent = (BaseAllAppsContainerView<?>) getParent();
if (parent != null) {
parent.setupHeader();
@@ -258,20 +265,20 @@
}
private void updateExpectedHeight() {
+ updateFloatingRowsHeight();
mMaxTranslation = 0;
- if (mCollapsed) {
+ if (mFloatingRowsCollapsed) {
return;
}
- for (FloatingHeaderRow row : mAllRows) {
- mMaxTranslation += row.getExpectedHeight();
- }
+ mMaxTranslation += mFloatingRowsHeight;
if (!mTabsHidden) {
- mMaxTranslation += mHeaderBottomAdjustment;
+ mMaxTranslation += mTabsAdditionalPaddingBottom
+ + getResources().getDimensionPixelSize(R.dimen.all_apps_tabs_margin_top);
}
}
- public int getMaxTranslation() {
- if (mMaxTranslation == 0 && mTabsHidden) {
+ int getMaxTranslation() {
+ if (mMaxTranslation == 0 && (mTabsHidden || mFloatingRowsCollapsed)) {
return getResources().getDimensionPixelSize(R.dimen.all_apps_search_bar_bottom_padding);
} else if (mMaxTranslation > 0 && mTabsHidden) {
return mMaxTranslation + getPaddingTop();
@@ -312,7 +319,7 @@
int uncappedTranslationY = mTranslationY;
mTranslationY = Math.max(mTranslationY, -mMaxTranslation);
- if (mCollapsed || uncappedTranslationY < mTranslationY - getPaddingTop()) {
+ if (mFloatingRowsCollapsed || uncappedTranslationY < mTranslationY - getPaddingTop()) {
// we hide it completely if already capped (for opening search anim)
for (FloatingHeaderRow row : mAllRows) {
row.setVerticalScroll(0, true /* isScrolledOut */);
@@ -325,11 +332,12 @@
mTabLayout.setTranslationY(mTranslationY);
- int clipTop = getPaddingTop() - mHeaderTopAdjustment;
+ int clipTop = getPaddingTop() - mTabsAdditionalPaddingTop;
if (mTabsHidden) {
- clipTop += getPaddingBottom() - mHeaderBottomAdjustment;
+ // Add back spacing that is otherwise covered by the tabs.
+ clipTop += mTabsAdditionalPaddingTop;
}
- mRVClip.top = mTabsHidden ? clipTop : 0;
+ mRVClip.top = mTabsHidden || mFloatingRowsCollapsed ? clipTop : 0;
mHeaderClip.top = clipTop;
// clipping on a draw might cause additional redraw
setClipBounds(mHeaderClip);
@@ -347,10 +355,12 @@
/**
* Hides all the floating rows
*/
- public void setCollapsed(boolean collapse) {
- if (mCollapsed == collapse) return;
+ public void setFloatingRowsCollapsed(boolean collapsed) {
+ if (mFloatingRowsCollapsed == collapsed) {
+ return;
+ }
- mCollapsed = collapse;
+ mFloatingRowsCollapsed = collapsed;
onHeightUpdated();
}
@@ -376,6 +386,34 @@
return !mHeaderCollapsed;
}
+ /** Returns true if personal/work tabs are currently in use. */
+ public boolean usingTabs() {
+ return !mTabsHidden;
+ }
+
+ ViewGroup getTabLayout() {
+ return mTabLayout;
+ }
+
+ /** Calculates the combined height of any floating rows (e.g. predicted apps, app divider). */
+ private void updateFloatingRowsHeight() {
+ mFloatingRowsHeight =
+ Arrays.stream(mAllRows).mapToInt(FloatingHeaderRow::getExpectedHeight).sum();
+ }
+
+ /** Gets the combined height of any floating rows (e.g. predicted apps, app divider). */
+ int getFloatingRowsHeight() {
+ return mFloatingRowsHeight;
+ }
+
+ int getTabsAdditionalPaddingTop() {
+ return mTabsAdditionalPaddingTop;
+ }
+
+ int getTabsAdditionalPaddingBottom() {
+ return mTabsAdditionalPaddingBottom;
+ }
+
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mTranslationY = (Integer) animation.getAnimatedValue();
@@ -440,16 +478,17 @@
/**
* Returns visible height of FloatingHeaderView contents requiring header protection
*/
- public int getPeripheralProtectionHeight() {
+ int getPeripheralProtectionHeight() {
if (!mHeaderProtectionSupported) {
return 0;
}
// we only want to show protection when work tab is available and header is either
// collapsed or animating to/from collapsed state
- if (mTabsHidden || !mHeaderCollapsed) {
+ if (mTabsHidden || mFloatingRowsCollapsed || !mHeaderCollapsed) {
return 0;
}
- return Math.max(getHeight() - getPaddingTop() + mTranslationY, 0);
+ return Math.max(0,
+ getTabLayout().getBottom() - getPaddingTop() + getPaddingBottom() + mTranslationY);
}
}
diff --git a/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java b/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java
index 20f5e74..92e29bb 100644
--- a/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java
@@ -16,19 +16,50 @@
package com.android.launcher3.allapps;
import android.content.Context;
+import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.WindowInsets;
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.launcher3.ExtendedEditText;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.Utilities;
+import com.android.launcher3.config.FeatureFlags;
/**
* AllAppsContainerView with launcher specific callbacks
*/
public class LauncherAllAppsContainerView extends ActivityAllAppsContainerView<Launcher> {
+ private final RecyclerView.OnScrollListener mActivityScrollListener =
+ new RecyclerView.OnScrollListener() {
+ @Override
+ public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
+ int scrolledOffset = recyclerView.computeVerticalScrollOffset();
+ ExtendedEditText input = mSearchUiManager.getEditText();
+ // Scroll up and scroll to top
+ if (dy < 0 && scrolledOffset == 0 && input != null) {
+ boolean isImeEnabledOnSwipeUp = Launcher.getLauncher(mActivityContext)
+ .getSearchConfig().isImeEnabledOnSwipeUp();
+ if (isImeEnabledOnSwipeUp || !TextUtils.isEmpty(input.getText())) {
+ input.showKeyboard();
+ }
+ }
+ }
+ };
+
+ @Override
+ protected void onInitializeRecyclerView(RecyclerView rv) {
+ super.onInitializeRecyclerView(rv);
+ if (FeatureFlags.SCROLL_TOP_TO_RESET.get()) {
+ rv.addOnScrollListener(mActivityScrollListener);
+ }
+ }
+
public LauncherAllAppsContainerView(Context context) {
this(context, null);
}
diff --git a/src/com/android/launcher3/allapps/SearchRecyclerView.java b/src/com/android/launcher3/allapps/SearchRecyclerView.java
index 482bd29..9d1dfc0 100644
--- a/src/com/android/launcher3/allapps/SearchRecyclerView.java
+++ b/src/com/android/launcher3/allapps/SearchRecyclerView.java
@@ -17,12 +17,17 @@
import android.content.Context;
import android.util.AttributeSet;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.core.util.Consumer;
import com.android.launcher3.views.RecyclerViewFastScroller;
/** A RecyclerView for AllApps Search results. */
public class SearchRecyclerView extends AllAppsRecyclerView {
- private static final String TAG = "SearchRecyclerView";
+
+ private Consumer<View> mChildAttachedConsumer;
public SearchRecyclerView(Context context) {
this(context, null);
@@ -41,6 +46,11 @@
super(context, attrs, defStyleAttr, defStyleRes);
}
+ /** This will be called just before a new child is attached to the window. */
+ public void setChildAttachedConsumer(Consumer<View> childAttachedConsumer) {
+ mChildAttachedConsumer = childAttachedConsumer;
+ }
+
@Override
protected void updatePoolSize() {
RecycledViewPool pool = getRecycledViewPool();
@@ -57,4 +67,12 @@
public RecyclerViewFastScroller getScrollbar() {
return null;
}
+
+ @Override
+ public void onChildAttachedToWindow(@NonNull View child) {
+ if (mChildAttachedConsumer != null) {
+ mChildAttachedConsumer.accept(child);
+ }
+ super.onChildAttachedToWindow(child);
+ }
}
diff --git a/src/com/android/launcher3/allapps/SearchTransitionController.java b/src/com/android/launcher3/allapps/SearchTransitionController.java
new file mode 100644
index 0000000..495f5c3
--- /dev/null
+++ b/src/com/android/launcher3/allapps/SearchTransitionController.java
@@ -0,0 +1,344 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.allapps;
+
+import static android.view.View.VISIBLE;
+
+import static androidx.recyclerview.widget.RecyclerView.NO_POSITION;
+
+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.graphics.drawable.Drawable;
+import android.util.FloatProperty;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.Interpolator;
+
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.model.data.ItemInfo;
+
+/** Coordinates the transition between Search and A-Z in All Apps. */
+public class SearchTransitionController {
+
+ private static final String LOG_TAG = "SearchTransitionCtrl";
+
+ // Interpolator when the user taps the QSB while already in All Apps.
+ private static final Interpolator INTERPOLATOR_WITHIN_ALL_APPS = DEACCEL_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;
+
+ /**
+ * These values represent points on the [0, 1] animation progress spectrum. They are used to
+ * animate items in the {@link SearchRecyclerView}.
+ */
+ private static final float TOP_CONTENT_FADE_PROGRESS_START = 0.133f;
+ private static final float CONTENT_FADE_PROGRESS_DURATION = 0.083f;
+ private static final float TOP_BACKGROUND_FADE_PROGRESS_START = 0.633f;
+ private static final float BACKGROUND_FADE_PROGRESS_DURATION = 0.15f;
+ private static final float CONTENT_STAGGER = 0.01f; // Progress before next item starts fading.
+
+ private static final FloatProperty<SearchTransitionController> SEARCH_TO_AZ_PROGRESS =
+ new FloatProperty<SearchTransitionController>("searchToAzProgress") {
+ @Override
+ public Float get(SearchTransitionController controller) {
+ return controller.getSearchToAzProgress();
+ }
+
+ @Override
+ public void setValue(SearchTransitionController controller, float progress) {
+ controller.setSearchToAzProgress(progress);
+ }
+ };
+
+ private final ActivityAllAppsContainerView<?> mAllAppsContainerView;
+
+ private ObjectAnimator mSearchToAzAnimator = null;
+ private float mSearchToAzProgress = 1f;
+
+ public SearchTransitionController(ActivityAllAppsContainerView<?> allAppsContainerView) {
+ mAllAppsContainerView = allAppsContainerView;
+ }
+
+ /** Returns true if a transition animation is currently in progress. */
+ public boolean isRunning() {
+ return mSearchToAzAnimator != null;
+ }
+
+ /**
+ * Starts the transition to or from search state. If a transition is already in progress, the
+ * animation will start from that point with the new duration, and the previous onEndRunnable
+ * will not be called.
+ *
+ * @param goingToSearch true if will be showing search results, otherwise will be showing a-z
+ * @param duration time in ms for the animation to run
+ * @param onEndRunnable will be called when the animation finishes, unless another animation is
+ * scheduled in the meantime
+ */
+ public void animateToSearchState(boolean goingToSearch, long duration, Runnable onEndRunnable) {
+ float targetProgress = goingToSearch ? 0 : 1;
+
+ if (mSearchToAzAnimator != null) {
+ mSearchToAzAnimator.cancel();
+ }
+
+ mSearchToAzAnimator = ObjectAnimator.ofFloat(this, SEARCH_TO_AZ_PROGRESS, targetProgress);
+ boolean inAllApps = Launcher.getLauncher(
+ mAllAppsContainerView.getContext()).getStateManager().isInStableState(
+ LauncherState.ALL_APPS);
+ if (!inAllApps) {
+ duration = 0; // Don't want to animate when coming from QSB.
+ }
+ mSearchToAzAnimator.setDuration(duration).setInterpolator(
+ inAllApps ? INTERPOLATOR_WITHIN_ALL_APPS : INTERPOLATOR_TRANSITIONING_TO_ALL_APPS);
+ mSearchToAzAnimator.addListener(forEndCallback(() -> mSearchToAzAnimator = null));
+ if (!goingToSearch) {
+ mSearchToAzAnimator.addListener(forSuccessCallback(() -> {
+ mAllAppsContainerView.getFloatingHeaderView().setFloatingRowsCollapsed(false);
+ mAllAppsContainerView.getFloatingHeaderView().reset(false /* animate */);
+ mAllAppsContainerView.getAppsRecyclerViewContainer().setTranslationY(0);
+ }));
+ }
+ mSearchToAzAnimator.addListener(forSuccessCallback(onEndRunnable));
+
+ mAllAppsContainerView.getFloatingHeaderView().setFloatingRowsCollapsed(true);
+ mAllAppsContainerView.getFloatingHeaderView().setVisibility(VISIBLE);
+ mAllAppsContainerView.getAppsRecyclerViewContainer().setVisibility(VISIBLE);
+ getSearchRecyclerView().setVisibility(VISIBLE);
+ getSearchRecyclerView().setChildAttachedConsumer(this::onSearchChildAttached);
+ mSearchToAzAnimator.start();
+ }
+
+ private SearchRecyclerView getSearchRecyclerView() {
+ return mAllAppsContainerView.getSearchRecyclerView();
+ }
+
+ private void setSearchToAzProgress(float searchToAzProgress) {
+ mSearchToAzProgress = searchToAzProgress;
+ int searchHeight = updateSearchRecyclerViewProgress();
+
+ FloatingHeaderView headerView = mAllAppsContainerView.getFloatingHeaderView();
+
+ // Add predictions + app divider height to account for predicted apps which will now be in
+ // the Search RV instead of the floating header view. Note `getFloatingRowsHeight` returns 0
+ // when predictions are not shown.
+ int appsTranslationY = searchHeight + headerView.getFloatingRowsHeight();
+
+ if (headerView.usingTabs()) {
+ // Move tabs below the search results, and fade them out in 20% of the animation.
+ headerView.setTranslationY(searchHeight);
+ headerView.setAlpha(clampToProgress(searchToAzProgress, 0.8f, 1f));
+
+ // Account for the additional padding added for the tabs.
+ appsTranslationY +=
+ headerView.getTabsAdditionalPaddingBottom()
+ + mAllAppsContainerView.getResources().getDimensionPixelOffset(
+ R.dimen.all_apps_tabs_margin_top)
+ - headerView.getPaddingTop();
+ }
+
+ View appsContainer = mAllAppsContainerView.getAppsRecyclerViewContainer();
+ appsContainer.setTranslationY(appsTranslationY);
+ // Fade apps out with tabs (in 20% of the total animation).
+ appsContainer.setAlpha(clampToProgress(searchToAzProgress, 0.8f, 1f));
+ }
+
+ /**
+ * Updates the children views of SearchRecyclerView based on the current animation progress.
+ *
+ * @return the total height of animating views (excluding at most one row of app icons).
+ */
+ private int updateSearchRecyclerViewProgress() {
+ int numSearchResultsAnimated = 0;
+ int totalHeight = 0;
+ int appRowHeight = 0;
+ boolean appRowComplete = false;
+ Integer top = null;
+ SearchRecyclerView searchRecyclerView = getSearchRecyclerView();
+
+ for (int i = 0; i < searchRecyclerView.getChildCount(); i++) {
+ View searchResultView = searchRecyclerView.getChildAt(i);
+ if (searchResultView == null) {
+ continue;
+ }
+
+ if (top == null) {
+ top = searchResultView.getTop();
+ }
+
+ int adapterPosition = searchRecyclerView.getChildAdapterPosition(searchResultView);
+ int spanIndex = getSpanIndex(searchRecyclerView, adapterPosition);
+ appRowComplete |= appRowHeight > 0 && spanIndex == 0;
+ // We don't animate the first (currently only) app row we see, as that is assumed to be
+ // predicted/prefix-matched apps.
+ boolean shouldAnimate = !isAppIcon(searchResultView) || appRowComplete;
+
+ float contentAlpha = 1f;
+ float backgroundAlpha = 1f;
+ if (shouldAnimate) {
+ if (spanIndex > 0) {
+ // Animate this item with the previous item on the same row.
+ numSearchResultsAnimated--;
+ }
+
+ // Adjust content alpha based on start progress and stagger.
+ float startContentFadeProgress = Math.max(0,
+ TOP_CONTENT_FADE_PROGRESS_START
+ - CONTENT_STAGGER * numSearchResultsAnimated);
+ float endContentFadeProgress = Math.min(1,
+ startContentFadeProgress + CONTENT_FADE_PROGRESS_DURATION);
+ contentAlpha = 1 - clampToProgress(mSearchToAzProgress,
+ startContentFadeProgress, endContentFadeProgress);
+
+ // Adjust background (or decorator) alpha based on start progress and stagger.
+ float startBackgroundFadeProgress = Math.max(0,
+ TOP_BACKGROUND_FADE_PROGRESS_START
+ - CONTENT_STAGGER * numSearchResultsAnimated);
+ float endBackgroundFadeProgress = Math.min(1,
+ startBackgroundFadeProgress + BACKGROUND_FADE_PROGRESS_DURATION);
+ backgroundAlpha = 1 - clampToProgress(mSearchToAzProgress,
+ startBackgroundFadeProgress, endBackgroundFadeProgress);
+
+ numSearchResultsAnimated++;
+ }
+
+ Drawable background = searchResultView.getBackground();
+ if (background != null
+ && searchResultView instanceof ViewGroup
+ && FeatureFlags.ENABLE_SEARCH_RESULT_BACKGROUND_DRAWABLES.get()) {
+ searchResultView.setAlpha(1f);
+
+ // Apply content alpha to each child, since the view needs to be fully opaque for
+ // the background to show properly.
+ ViewGroup searchResultViewGroup = (ViewGroup) searchResultView;
+ for (int j = 0; j < searchResultViewGroup.getChildCount(); j++) {
+ searchResultViewGroup.getChildAt(j).setAlpha(contentAlpha);
+ }
+
+ // Apply background alpha to the background drawable directly.
+ background.setAlpha((int) (255 * backgroundAlpha));
+ } else {
+ searchResultView.setAlpha(contentAlpha);
+
+ // Apply background alpha to decorator if possible.
+ if (adapterPosition != NO_POSITION) {
+ searchRecyclerView.getApps().getAdapterItems().get(adapterPosition)
+ .setDecorationFillAlpha((int) (255 * backgroundAlpha));
+ }
+
+ // Apply background alpha to view's background (e.g. for Search Edu card).
+ if (background != null) {
+ background.setAlpha((int) (255 * backgroundAlpha));
+ }
+ }
+
+ float scaleY = 1;
+ if (shouldAnimate) {
+ scaleY = 1 - mSearchToAzProgress;
+ }
+ int scaledHeight = (int) (searchResultView.getHeight() * scaleY);
+ searchResultView.setScaleY(scaleY);
+
+ // For rows with multiple elements, only count the height once and translate elements to
+ // the same y position.
+ int y = top + totalHeight;
+ if (spanIndex > 0) {
+ // Continuation of an existing row; move this item into the row.
+ y -= scaledHeight;
+ } else {
+ // Start of a new row contributes to total height.
+ totalHeight += scaledHeight;
+ if (!shouldAnimate) {
+ appRowHeight = scaledHeight;
+ }
+ }
+ searchResultView.setY(y);
+ }
+
+ return totalHeight - appRowHeight;
+ }
+
+ /** @return the column that the view at this position is found (0 assumed if indeterminate). */
+ private int getSpanIndex(SearchRecyclerView searchRecyclerView, int adapterPosition) {
+ if (adapterPosition == NO_POSITION) {
+ Log.w(LOG_TAG, "Can't determine span index - child not found in adapter");
+ return 0;
+ }
+ if (!(searchRecyclerView.getAdapter() instanceof AllAppsGridAdapter<?>)) {
+ Log.e(LOG_TAG, "Search RV doesn't have an AllAppsGridAdapter?");
+ // This case shouldn't happen, but for debug devices we will continue to create a more
+ // visible crash.
+ if (!Utilities.IS_DEBUG_DEVICE) {
+ return 0;
+ }
+ }
+ AllAppsGridAdapter<?> adapter = (AllAppsGridAdapter<?>) searchRecyclerView.getAdapter();
+ return adapter.getSpanIndex(adapterPosition);
+ }
+
+ private boolean isAppIcon(View item) {
+ return item instanceof BubbleTextView && item.getTag() instanceof ItemInfo
+ && ((ItemInfo) item.getTag()).itemType == ITEM_TYPE_APPLICATION;
+ }
+
+ /** Called just before a child is attached to the SearchRecyclerView. */
+ private void onSearchChildAttached(View child) {
+ // Avoid allocating hardware layers for alpha changes.
+ child.forceHasOverlappingRendering(false);
+ child.setPivotY(0);
+ if (mSearchToAzProgress > 0) {
+ // Before the child is rendered, apply the animation including it to avoid flicker.
+ updateSearchRecyclerViewProgress();
+ } else {
+ // Apply default states without processing the full layout.
+ child.setAlpha(1);
+ child.setScaleY(1);
+ child.setTranslationY(0);
+ int adapterPosition = getSearchRecyclerView().getChildAdapterPosition(child);
+ if (adapterPosition != NO_POSITION) {
+ getSearchRecyclerView().getApps().getAdapterItems().get(adapterPosition)
+ .setDecorationFillAlpha(255);
+ }
+ if (child instanceof ViewGroup
+ && FeatureFlags.ENABLE_SEARCH_RESULT_BACKGROUND_DRAWABLES.get()) {
+ ViewGroup childGroup = (ViewGroup) child;
+ for (int i = 0; i < childGroup.getChildCount(); i++) {
+ childGroup.getChildAt(i).setAlpha(1f);
+ }
+ }
+ if (child.getBackground() != null) {
+ child.getBackground().setAlpha(255);
+ }
+ }
+ }
+
+ private float getSearchToAzProgress() {
+ return mSearchToAzProgress;
+ }
+}
diff --git a/src/com/android/launcher3/allapps/SearchUiManager.java b/src/com/android/launcher3/allapps/SearchUiManager.java
index 6138bc4..228b02b 100644
--- a/src/com/android/launcher3/allapps/SearchUiManager.java
+++ b/src/com/android/launcher3/allapps/SearchUiManager.java
@@ -64,7 +64,8 @@
/**
* sets highlight result's title
*/
- default void setFocusedResultTitle(@Nullable CharSequence title) { }
+ default void setFocusedResultTitle(
+ @Nullable CharSequence title, @Nullable CharSequence subtitle) {}
/** Refresh the currently displayed list of results. */
default void refreshResults() {}
diff --git a/src/com/android/launcher3/allapps/WorkModeSwitch.java b/src/com/android/launcher3/allapps/WorkModeSwitch.java
index fedc91f..0a938b2 100644
--- a/src/com/android/launcher3/allapps/WorkModeSwitch.java
+++ b/src/com/android/launcher3/allapps/WorkModeSwitch.java
@@ -52,6 +52,7 @@
private int mFlags;
private boolean mWorkEnabled;
private boolean mOnWorkTab;
+ private boolean mApplyWindowInset;
public WorkModeSwitch(Context context) {
this(context, null, 0);
@@ -97,12 +98,10 @@
bottomMargin += dp.hotseatQsbHeight;
}
- if (!dp.isGestureMode) {
- if (dp.isTaskbarPresent) {
- bottomMargin += dp.taskbarSize;
- } else {
- bottomMargin += insets.bottom;
- }
+ if (!dp.isGestureMode && dp.isTaskbarPresent) {
+ bottomMargin += dp.taskbarSize;
+ } else {
+ bottomMargin += insets.bottom;
}
lp.bottomMargin = bottomMargin;
@@ -170,12 +169,14 @@
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
- if (Utilities.ATLEAST_R && isEnabled()) {
+ if (!Utilities.ATLEAST_R || !mApplyWindowInset) {
+ return insets;
+ }
+ if (insets.isVisible(WindowInsets.Type.ime())) {
+ Insets keyboardInsets = insets.getInsets(WindowInsets.Type.ime());
+ setTranslationY(mInsets.bottom - keyboardInsets.bottom);
+ } else {
setTranslationY(0);
- if (insets.isVisible(WindowInsets.Type.ime())) {
- Insets keyboardInsets = insets.getInsets(WindowInsets.Type.ime());
- setTranslationY(mInsets.bottom - keyboardInsets.bottom);
- }
}
return insets;
}
@@ -197,4 +198,8 @@
private void removeFlag(int flag) {
mFlags &= ~flag;
}
+
+ public void setApplyWindowInset(boolean applyWindowInset){
+ mApplyWindowInset = applyWindowInset;
+ }
}
diff --git a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
index 886460e..4d58eb0 100644
--- a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
+++ b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
@@ -15,9 +15,6 @@
*/
package com.android.launcher3.allapps.search;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_FOCUSED_ITEM_SELECTED_WITH_IME;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_QUICK_SEARCH_WITH_IME;
-
import android.text.Editable;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
@@ -84,7 +81,10 @@
mTextConversions = extractTextConversions(s);
}
- private static String[] extractTextConversions(CharSequence text) {
+ /**
+ * Extract text conversions from composing text and send them for search.
+ */
+ public static String[] extractTextConversions(CharSequence text) {
if (text instanceof SpannableStringBuilder) {
SpannableStringBuilder spanned = (SpannableStringBuilder) text;
SuggestionSpan[] suggestionSpans =
@@ -122,10 +122,6 @@
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_SEARCH || actionId == EditorInfo.IME_ACTION_GO) {
- mLauncher.getStatsLogManager().logger()
- .log(actionId == EditorInfo.IME_ACTION_SEARCH
- ? LAUNCHER_ALLAPPS_QUICK_SEARCH_WITH_IME
- : LAUNCHER_ALLAPPS_FOCUSED_ITEM_SELECTED_WITH_IME);
// selectFocusedView should return SearchTargetEvent that is passed onto onClick
return mLauncher.getAppsView().getMainAdapterProvider().launchHighlightedItem();
}
diff --git a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
index 52d8f63..78c305b 100644
--- a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
+++ b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
@@ -168,14 +168,11 @@
public void onSearchResult(String query, ArrayList<AdapterItem> items) {
if (items != null) {
mAppsView.setSearchResults(items);
- mAppsView.setLastSearchQuery(query);
}
}
@Override
public void clearSearchResult() {
- mAppsView.setSearchResults(null);
-
// Clear the search query
mSearchQueryBuilder.clear();
mSearchQueryBuilder.clearSpans();
diff --git a/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
index d2d7a6c..ab47097 100644
--- a/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
+++ b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
@@ -22,6 +22,7 @@
import android.os.Handler;
import androidx.annotation.AnyThread;
+import androidx.annotation.NonNull;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.allapps.BaseAllAppsAdapter.AdapterItem;
@@ -68,7 +69,8 @@
public void doSearch(String query, SearchCallback<AdapterItem> callback) {
mAppState.getModel().enqueueModelUpdateTask(new BaseModelUpdateTask() {
@Override
- public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+ public void execute(@NonNull final LauncherAppState app,
+ @NonNull final BgDataModel dataModel, @NonNull final AllAppsList apps) {
ArrayList<AdapterItem> result = getTitleMatchResult(apps.data, query);
if (mAddNoResultsMessage && result.isEmpty()) {
result.add(getEmptyMessageAdapterItem(query));
diff --git a/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java b/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java
index a95bd51..4fb732d 100644
--- a/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java
+++ b/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java
@@ -84,4 +84,9 @@
public RecyclerView.ItemDecoration getDecorator() {
return mDecoration;
}
+
+ @Override
+ public void clearHighlightedItem() {
+ mHighlightedView = null;
+ }
}
diff --git a/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java b/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java
index bc52784..3890741 100644
--- a/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java
+++ b/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java
@@ -58,4 +58,9 @@
* Returns the item decorator.
*/
public abstract RecyclerView.ItemDecoration getDecorator();
+
+ /**
+ * Clear the highlighted view.
+ */
+ public abstract void clearHighlightedItem();
}
diff --git a/src/com/android/launcher3/anim/AnimationSuccessListener.java b/src/com/android/launcher3/anim/AnimationSuccessListener.java
index a312070..6196df2 100644
--- a/src/com/android/launcher3/anim/AnimationSuccessListener.java
+++ b/src/com/android/launcher3/anim/AnimationSuccessListener.java
@@ -19,6 +19,8 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import androidx.annotation.CallSuper;
+
/**
* Extension of {@link AnimatorListenerAdapter} for listening for non-cancelled animations
*/
@@ -27,6 +29,7 @@
protected boolean mCancelled = false;
@Override
+ @CallSuper
public void onAnimationCancel(Animator animation) {
mCancelled = true;
}
diff --git a/src/com/android/launcher3/anim/Interpolators.java b/src/com/android/launcher3/anim/Interpolators.java
index 464b3ed..b55a1e4 100644
--- a/src/com/android/launcher3/anim/Interpolators.java
+++ b/src/com/android/launcher3/anim/Interpolators.java
@@ -50,8 +50,6 @@
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 FASTER_OUT_SLOWER_IN =
- new PathInterpolator(0.3f, 0f, 0.1f, 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);
@@ -59,6 +57,10 @@
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);
+ /**
+ * 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(
@@ -81,6 +83,7 @@
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);
@@ -89,7 +92,6 @@
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.
*/
@@ -220,4 +222,14 @@
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/PropertySetter.java b/src/com/android/launcher3/anim/PropertySetter.java
index b0ed2d2..6ba58b6 100644
--- a/src/com/android/launcher3/anim/PropertySetter.java
+++ b/src/com/android/launcher3/anim/PropertySetter.java
@@ -38,6 +38,7 @@
public void add(Animator animatorSet) {
animatorSet.setDuration(0);
animatorSet.start();
+ animatorSet.end();
}
};
diff --git a/src/com/android/launcher3/celllayout/CellLayoutLayoutParams.java b/src/com/android/launcher3/celllayout/CellLayoutLayoutParams.java
new file mode 100644
index 0000000..b14ae8d
--- /dev/null
+++ b/src/com/android/launcher3/celllayout/CellLayoutLayoutParams.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.celllayout;
+
+import android.content.Context;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.ViewDebug;
+import android.view.ViewGroup;
+
+import androidx.annotation.Nullable;
+
+/**
+ * Represents the logic of where a view is in a CellLayout and its size
+ */
+public class CellLayoutLayoutParams extends ViewGroup.MarginLayoutParams {
+ /**
+ * Horizontal location of the item in the grid.
+ */
+ @ViewDebug.ExportedProperty
+ public int cellX;
+
+ /**
+ * Vertical location of the item in the grid.
+ */
+ @ViewDebug.ExportedProperty
+ public int cellY;
+
+ /**
+ * Temporary horizontal location of the item in the grid during reorder
+ */
+ public int tmpCellX;
+
+ /**
+ * Temporary vertical location of the item in the grid during reorder
+ */
+ public int tmpCellY;
+
+ /**
+ * Indicates that the temporary coordinates should be used to layout the items
+ */
+ public boolean useTmpCoords;
+
+ /**
+ * Number of cells spanned horizontally by the item.
+ */
+ @ViewDebug.ExportedProperty
+ public int cellHSpan;
+
+ /**
+ * Number of cells spanned vertically by the item.
+ */
+ @ViewDebug.ExportedProperty
+ public int cellVSpan;
+
+ /**
+ * Indicates whether the item will set its x, y, width and height parameters freely,
+ * or whether these will be computed based on cellX, cellY, cellHSpan and cellVSpan.
+ */
+ public boolean isLockedToGrid = true;
+
+ /**
+ * Indicates whether this item can be reordered. Always true except in the case of the
+ * the AllApps button and QSB place holder.
+ */
+ public boolean canReorder = true;
+
+ // X coordinate of the view in the layout.
+ @ViewDebug.ExportedProperty
+ public int x;
+ // Y coordinate of the view in the layout.
+ @ViewDebug.ExportedProperty
+ public int y;
+
+ public boolean dropped;
+
+ public CellLayoutLayoutParams(Context c, AttributeSet attrs) {
+ super(c, attrs);
+ cellHSpan = 1;
+ cellVSpan = 1;
+ }
+
+ public CellLayoutLayoutParams(ViewGroup.LayoutParams source) {
+ super(source);
+ cellHSpan = 1;
+ cellVSpan = 1;
+ }
+
+ public CellLayoutLayoutParams(CellLayoutLayoutParams source) {
+ super(source);
+ this.cellX = source.cellX;
+ this.cellY = source.cellY;
+ this.cellHSpan = source.cellHSpan;
+ this.cellVSpan = source.cellVSpan;
+ }
+
+ public CellLayoutLayoutParams(int cellX, int cellY, int cellHSpan, int cellVSpan) {
+ super(CellLayoutLayoutParams.MATCH_PARENT, CellLayoutLayoutParams.MATCH_PARENT);
+ this.cellX = cellX;
+ this.cellY = cellY;
+ this.cellHSpan = cellHSpan;
+ this.cellVSpan = cellVSpan;
+ }
+
+ /**
+ * Updates the {@link CellLayoutLayoutParams} with the right measures using their
+ * full/invariant device profile sizes.
+ */
+ public void setup(int cellWidth, int cellHeight, boolean invertHorizontally, int colCount,
+ int rowCount, Point borderSpace, @Nullable Rect inset) {
+ setup(cellWidth, cellHeight, invertHorizontally, colCount, rowCount, 1.0f, 1.0f,
+ borderSpace, inset);
+ }
+
+ /**
+ * Use this method, as opposed to {@link #setup(int, int, boolean, int, int, Point, Rect)},
+ * if the view needs to be scaled.
+ *
+ * ie. In multi-window mode, we setup widgets so that they are measured and laid out
+ * using their full/invariant device profile sizes.
+ */
+ public void setup(int cellWidth, int cellHeight, boolean invertHorizontally, int colCount,
+ int rowCount, float cellScaleX, float cellScaleY, Point borderSpace,
+ @Nullable Rect inset) {
+ if (isLockedToGrid) {
+ final int myCellHSpan = cellHSpan;
+ final int myCellVSpan = cellVSpan;
+ int myCellX = useTmpCoords ? tmpCellX : cellX;
+ int myCellY = useTmpCoords ? tmpCellY : cellY;
+
+ if (invertHorizontally) {
+ myCellX = colCount - myCellX - cellHSpan;
+ }
+
+ int hBorderSpacing = (myCellHSpan - 1) * borderSpace.x;
+ int vBorderSpacing = (myCellVSpan - 1) * borderSpace.y;
+
+ float myCellWidth = ((myCellHSpan * cellWidth) + hBorderSpacing) / cellScaleX;
+ float myCellHeight = ((myCellVSpan * cellHeight) + vBorderSpacing) / cellScaleY;
+
+ width = Math.round(myCellWidth) - leftMargin - rightMargin;
+ height = Math.round(myCellHeight) - topMargin - bottomMargin;
+ x = leftMargin + (myCellX * cellWidth) + (myCellX * borderSpace.x);
+ y = topMargin + (myCellY * cellHeight) + (myCellY * borderSpace.y);
+
+ if (inset != null) {
+ x -= inset.left;
+ y -= inset.top;
+ width += inset.left + inset.right;
+ height += inset.top + inset.bottom;
+ }
+ }
+ }
+
+ /**
+ * Sets the position to the provided point
+ */
+ public void setCellXY(Point point) {
+ cellX = point.x;
+ cellY = point.y;
+ }
+
+ /**
+ * @return the string representation of the position of the {@link CellLayoutLayoutParams}
+ */
+ public String toString() {
+ return "(" + this.cellX + ", " + this.cellY + ")";
+ }
+}
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index f5683d1..9607da8 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -17,6 +17,7 @@
package com.android.launcher3.config;
import android.content.Context;
+import android.content.SharedPreferences;
import com.android.launcher3.BuildConfig;
import com.android.launcher3.Utilities;
@@ -37,7 +38,8 @@
public static final String FLAGS_PREF_NAME = "featureFlags";
- private FeatureFlags() { }
+ private FeatureFlags() {
+ }
public static boolean showFlagTogglerUi(Context context) {
return Utilities.IS_DEBUG_DEVICE && Utilities.isDevelopersOptionsEnabled(context);
@@ -61,17 +63,22 @@
* To add a new flag that can be toggled through the flags UI:
*
* Declare a new ToggleableFlag below. Give it a unique key (e.g. "QSB_ON_FIRST_SCREEN"),
- * and set a default value for the flag. This will be the default value on Debug builds.
+ * and set a default value for the flag. This will be the default value on Debug builds.
*/
+ public static final BooleanFlag ENABLE_INPUT_CONSUMER_REASON_LOGGING = getDebugFlag(
+ "ENABLE_INPUT_CONSUMER_REASON_LOGGING",
+ true,
+ "Log the reason why an Input Consumer was selected for a gesture.");
+
+ public static final BooleanFlag ENABLE_GESTURE_ERROR_DETECTION = getDebugFlag(
+ "ENABLE_GESTURE_ERROR_DETECTION",
+ true,
+ "Analyze gesture events and log detected errors");
+
// When enabled the promise icon is visible in all apps while installation an app.
public static final BooleanFlag PROMISE_APPS_IN_ALL_APPS = getDebugFlag(
"PROMISE_APPS_IN_ALL_APPS", false, "Add promise icon in all-apps");
- // When enabled a promise icon is added to the home screen when install session is active.
- public static final BooleanFlag PROMISE_APPS_NEW_INSTALLS = getDebugFlag(
- "PROMISE_APPS_NEW_INSTALLS", true,
- "Adds a promise icon to the home screen for new install sessions.");
-
// TODO: b/206508141: Long pressing on some icons on home screen cause launcher to crash.
public static final BooleanFlag ENABLE_LOCAL_COLOR_POPUPS = getDebugFlag(
"ENABLE_LOCAL_COLOR_POPUPS", false, "Enable local color extraction for popups.");
@@ -79,13 +86,6 @@
public static final BooleanFlag KEYGUARD_ANIMATION = getDebugFlag(
"KEYGUARD_ANIMATION", false, "Enable animation for keyguard going away on wallpaper");
- public static final BooleanFlag ENABLE_QUICKSTEP_LIVE_TILE = getDebugFlag(
- "ENABLE_QUICKSTEP_LIVE_TILE", true, "Enable live tile in Quickstep overview");
-
- public static final BooleanFlag ENABLE_QUICKSTEP_WIDGET_APP_START = getDebugFlag(
- "ENABLE_QUICKSTEP_WIDGET_APP_START", true,
- "Enable Quickstep animation when launching activities from an app widget");
-
public static final BooleanFlag ENABLE_DEVICE_SEARCH = new DeviceFlag(
"ENABLE_DEVICE_SEARCH", true, "Allows on device search in all apps");
@@ -93,12 +93,20 @@
getDebugFlag("ENABLE_FLOATING_SEARCH_BAR", false,
"Keep All Apps search bar at the bottom (but above keyboard if open)");
- public static final BooleanFlag ENABLE_QUICK_SEARCH = new DeviceFlag("ENABLE_QUICK_SEARCH",
- true, "Use quick search behavior.");
+ public static final BooleanFlag ENABLE_QUICK_LAUNCH_V2 = new DeviceFlag(
+ "ENABLE_QUICK_LAUNCH_V2", false, "Use quick launch v2 "
+ + "behavior. Quick search and quick launch v1 would be unavailable if this is enabled");
+
+ public static final BooleanFlag GBOARD_UPDATE_ENTER_KEY = new DeviceFlag(
+ "GBOARD_UPDATE_ENTER_KEY", false, "Update gBoard enter key "
+ + "icon dynamically based on top search content for Quick Launch V2");
public static final BooleanFlag ENABLE_HIDE_HEADER = new DeviceFlag("ENABLE_HIDE_HEADER",
true, "Hide header on keyboard before typing in all apps");
+ public static final BooleanFlag ENABLE_HIDE_HEADER_STATIC = new DeviceFlag(
+ "ENABLE_HIDE_HEADER_STATIC", false, "Hide keyboard suggestion strip");
+
public static final BooleanFlag COLLECT_SEARCH_HISTORY = new DeviceFlag(
"COLLECT_SEARCH_HISTORY", false, "Allow launcher to collect search history for log");
@@ -116,30 +124,14 @@
"ENABLE_PEOPLE_TILE_PREVIEW", false,
"Experimental: Shows conversation shortcuts on home screen as search results");
- public static final BooleanFlag FOLDER_NAME_SUGGEST = new DeviceFlag(
- "FOLDER_NAME_SUGGEST", true,
- "Suggests folder names instead of blank text.");
-
public static final BooleanFlag FOLDER_NAME_MAJORITY_RANKING = getDebugFlag(
"FOLDER_NAME_MAJORITY_RANKING", true,
"Suggests folder names based on majority based ranking.");
- public static final BooleanFlag ENABLE_PREDICTION_DISMISS = getDebugFlag(
- "ENABLE_PREDICTION_DISMISS", true, "Allow option to dimiss apps from predicted list");
-
public static final BooleanFlag ASSISTANT_GIVES_LAUNCHER_FOCUS = getDebugFlag(
"ASSISTANT_GIVES_LAUNCHER_FOCUS", false,
"Allow Launcher to handle nav bar gestures while Assistant is running over it");
- public static final BooleanFlag HOTSEAT_MIGRATE_TO_FOLDER = getDebugFlag(
- "HOTSEAT_MIGRATE_TO_FOLDER", false, "Should move hotseat items into a folder");
-
- public static final BooleanFlag ENABLE_DEEP_SHORTCUT_ICON_CACHE = getDebugFlag(
- "ENABLE_DEEP_SHORTCUT_ICON_CACHE", true, "R/W deep shortcut in IconCache");
-
- public static final BooleanFlag ENABLE_THEMED_ICONS = getDebugFlag(
- "ENABLE_THEMED_ICONS", true, "Enable themed icons on workspace");
-
public static final BooleanFlag ENABLE_BULK_WORKSPACE_ICON_LOADING = getDebugFlag(
"ENABLE_BULK_WORKSPACE_ICON_LOADING",
true,
@@ -169,14 +161,22 @@
"ENABLE_SMARTSPACE_DISMISS", true,
"Adds a menu option to dismiss the current Enhanced Smartspace card.");
+ public static final BooleanFlag ENABLE_OVERLAY_CONNECTION_OPTIM = getDebugFlag(
+ "ENABLE_OVERLAY_CONNECTION_OPTIM",
+ false,
+ "Enable optimizing overlay service connection");
+
+ /**
+ * Enables region sampling for text color: Needs system health assessment before turning on
+ */
+ public static final BooleanFlag ENABLE_REGION_SAMPLING = getDebugFlag(
+ "ENABLE_REGION_SAMPLING", false,
+ "Enable region sampling to determine color of text on screen.");
+
public static final BooleanFlag ALWAYS_USE_HARDWARE_OPTIMIZATION_FOR_FOLDER_ANIMATIONS =
getDebugFlag(
- "ALWAYS_USE_HARDWARE_OPTIMIZATION_FOR_FOLDER_ANIMATIONS", false,
- "Always use hardware optimization for folder animations.");
-
- public static final BooleanFlag ENABLE_ALL_APPS_EDU = getDebugFlag(
- "ENABLE_ALL_APPS_EDU", true,
- "Shows user a tutorial on how to get to All Apps after X amount of attempts.");
+ "ALWAYS_USE_HARDWARE_OPTIMIZATION_FOR_FOLDER_ANIMATIONS", false,
+ "Always use hardware optimization for folder animations.");
public static final BooleanFlag SEPARATE_RECENTS_ACTIVITY = getDebugFlag(
"SEPARATE_RECENTS_ACTIVITY", false,
@@ -186,10 +186,6 @@
"ENABLE_MINIMAL_DEVICE", false,
"Allow user to toggle minimal device mode in launcher.");
- public static final BooleanFlag EXPANDED_SMARTSPACE = new DeviceFlag(
- "EXPANDED_SMARTSPACE", false, "Expands smartspace height to two rows. "
- + "Any apps occupying the first row will be removed from workspace.");
-
// TODO: b/172467144 Remove ENABLE_LAUNCHER_ACTIVITY_THEME_CROSSFADE feature flag.
public static final BooleanFlag ENABLE_LAUNCHER_ACTIVITY_THEME_CROSSFADE = new DeviceFlag(
"ENABLE_LAUNCHER_ACTIVITY_THEME_CROSSFADE", false, "Enables a "
@@ -215,10 +211,6 @@
public static final BooleanFlag ENABLE_ENFORCED_ROUNDED_CORNERS = new DeviceFlag(
"ENABLE_ENFORCED_ROUNDED_CORNERS", true, "Enforce rounded corners on all App Widgets");
- public static final BooleanFlag ENABLE_LOCAL_RECOMMENDED_WIDGETS_FILTER = new DeviceFlag(
- "ENABLE_LOCAL_RECOMMENDED_WIDGETS_FILTER", true,
- "Enables a local filter for recommended widgets.");
-
public static final BooleanFlag NOTIFY_CRASHES = getDebugFlag("NOTIFY_CRASHES", false,
"Sends a notification whenever launcher encounters an uncaught exception.");
@@ -254,10 +246,22 @@
"ENABLE_ALL_APPS_ONE_SEARCH_IN_TASKBAR", false,
"Enables One Search box in Taskbar All Apps.");
+ public static final BooleanFlag ENABLE_TASKBAR_IN_OVERVIEW = getDebugFlag(
+ "ENABLE_TASKBAR_IN_OVERVIEW", false,
+ "Enables accessing the system Taskbar in overview.");
+
public static final BooleanFlag ENABLE_SPLIT_FROM_WORKSPACE = getDebugFlag(
"ENABLE_SPLIT_FROM_WORKSPACE", true,
"Enable initiating split screen from workspace.");
+ public static final BooleanFlag ENABLE_SPLIT_FROM_FULLSCREEN_WITH_KEYBOARD_SHORTCUTS =
+ getDebugFlag("ENABLE_SPLIT_FROM_FULLSCREEN_SHORTCUT", false,
+ "Enable splitting from fullscreen app with keyboard shortcuts");
+
+ public static final BooleanFlag ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE = getDebugFlag(
+ "ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE", false,
+ "Enable initiating split screen from workspace to workspace.");
+
public static final BooleanFlag ENABLE_NEW_MIGRATION_LOGIC = getDebugFlag(
"ENABLE_NEW_MIGRATION_LOGIC", true,
"Enable the new grid migration logic, keeping pages when src < dest");
@@ -265,10 +269,9 @@
public static final BooleanFlag ENABLE_ONE_SEARCH_MOTION = new DeviceFlag(
"ENABLE_ONE_SEARCH_MOTION", true, "Enables animations in OneSearch.");
- public static final BooleanFlag ENABLE_KEYBOARD_TRANSITION_SYNC = new DeviceFlag(
- "ENABLE_KEYBOARD_TRANSITION_SYNC", false,
- "Enable option to synchronize the keyboard open and close animations when transitioning"
- + " between home and all apps");
+ public static final BooleanFlag ENABLE_SEARCH_RESULT_BACKGROUND_DRAWABLES = new DeviceFlag(
+ "ENABLE_SEARCH_RESULT_BACKGROUND_DRAWABLES", false,
+ "Enable option to replace decorator-based search result backgrounds with drawables");
public static final BooleanFlag ENABLE_SHOW_KEYBOARD_OPTION_IN_ALL_APPS = new DeviceFlag(
"ENABLE_SHOW_KEYBOARD_OPTION_IN_ALL_APPS", true,
@@ -293,12 +296,75 @@
public static final BooleanFlag CONTINUOUS_VIEW_TREE_CAPTURE = getDebugFlag(
"CONTINUOUS_VIEW_TREE_CAPTURE", false, "Capture View tree every frame");
+ public static final BooleanFlag FOLDABLE_WORKSPACE_REORDER = getDebugFlag(
+ "FOLDABLE_WORKSPACE_REORDER", true,
+ "In foldables, when reordering the icons and widgets, is now going to use both sides");
+
+ public static final BooleanFlag ENABLE_WIDGET_PICKER_DEPTH = new DeviceFlag(
+ "ENABLE_WIDGET_PICKER_DEPTH", true, "Enable changing depth in widget picker.");
+
+ public static final BooleanFlag SCROLL_TOP_TO_RESET = new DeviceFlag(
+ "SCROLL_TOP_TO_RESET", false, "Bring up IME and focus on "
+ + "input when scroll to top if 'Always show keyboard' is enabled or in prefix state");
+
+ public static final BooleanFlag SHOW_DELIGHTFUL_PAGINATION = getDebugFlag(
+ "SHOW_DELIGHTFUL_PAGINATION", false,
+ "Enable showing the new 'delightful pagination' which is a brand"
+ + " new animation for folder pagination and workspace pagination");
+ public static final BooleanFlag POPUP_MATERIAL_U = new DeviceFlag(
+ "POPUP_MATERIAL_U", false, "Switch popup UX to use material U");
+
+ public static final BooleanFlag SHOW_HOME_GARDENING = getDebugFlag(
+ "SHOW_HOME_GARDENING", false,
+ "Show the new home gardening mode");
+
+ public static final BooleanFlag HOME_GARDENING_WORKSPACE_BUTTONS = getDebugFlag(
+ "HOME_GARDENING_WORKSPACE_BUTTONS", false,
+ "Change workspace edit buttons to reflect home gardening");
+
+ public static final BooleanFlag ENABLE_TRANSIENT_TASKBAR = getDebugFlag(
+ "ENABLE_TRANSIENT_TASKBAR", false, "Enables transient taskbar.");
+
+ public static final BooleanFlag SECONDARY_DRAG_N_DROP_TO_PIN = getDebugFlag(
+ "SECONDARY_DRAG_N_DROP_TO_PIN", false,
+ "Enable dragging and dropping to pin apps within secondary display");
+
+ public static final BooleanFlag SHOW_DOT_PAGINATION = getDebugFlag(
+ "SHOW_DOT_PAGINATION", false, "Enable showing dot pagination in workspace");
+
+ public static final BooleanFlag LARGE_SCREEN_WIDGET_PICKER = getDebugFlag(
+ "LARGE_SCREEN_WIDGET_PICKER", false, "Enable new widget picker that takes "
+ + "advantage of large screen format");
+
+ public static final BooleanFlag ENABLE_NEW_GESTURE_NAV_TUTORIAL = getDebugFlag(
+ "ENABLE_NEW_GESTURE_NAV_TUTORIAL", false,
+ "Enable the redesigned gesture navigation tutorial");
+
+ public static final BooleanFlag ENABLE_TOAST_IMPRESSION_LOGGING = getDebugFlag(
+ "ENABLE_TOAST_IMPRESSION_LOGGING", false, "Enable toast impression logging");
+
+ public static final BooleanFlag ENABLE_DEVICE_PROFILE_LOGGING = new DeviceFlag(
+ "ENABLE_DEVICE_PROFILE_LOGGING", false, "Allows DeviceProfile logging");
+
+ public static final BooleanFlag ENABLE_LAUNCH_FROM_STAGED_APP = getDebugFlag(
+ "ENABLE_LAUNCH_FROM_STAGED_APP", false,
+ "Enable the ability to tap a staged app during split select to launch it in full screen"
+ );
+
public static void initialize(Context context) {
synchronized (sDebugFlags) {
for (DebugFlag flag : sDebugFlags) {
flag.initialize(context);
}
- sDebugFlags.sort((f1, f2) -> f1.key.compareToIgnoreCase(f2.key));
+
+ sDebugFlags.sort((f1, f2) -> {
+ // Sort first by any prefs that the user has changed, then alphabetically.
+ int changeComparison = Boolean.compare(f2.mHasBeenChangedAtLeastOnce,
+ f1.mHasBeenChangedAtLeastOnce);
+ return changeComparison != 0
+ ? changeComparison
+ : f1.key.compareToIgnoreCase(f2.key);
+ });
}
}
@@ -354,6 +420,7 @@
public static class DebugFlag extends BooleanFlag {
public final String description;
+ protected boolean mHasBeenChangedAtLeastOnce;
protected boolean mCurrentValue;
public DebugFlag(String key, boolean defaultValue, String description) {
@@ -371,8 +438,10 @@
}
public void initialize(Context context) {
- mCurrentValue = context.getSharedPreferences(FLAGS_PREF_NAME, Context.MODE_PRIVATE)
- .getBoolean(key, defaultValue);
+ SharedPreferences prefs =
+ context.getSharedPreferences(FLAGS_PREF_NAME, Context.MODE_PRIVATE);
+ mHasBeenChangedAtLeastOnce = prefs.contains(key);
+ mCurrentValue = prefs.getBoolean(key, defaultValue);
}
@Override
diff --git a/src/com/android/launcher3/config/FlagTogglerPrefUi.java b/src/com/android/launcher3/config/FlagTogglerPrefUi.java
index 6729f74..2eb6e6d 100644
--- a/src/com/android/launcher3/config/FlagTogglerPrefUi.java
+++ b/src/com/android/launcher3/config/FlagTogglerPrefUi.java
@@ -52,12 +52,17 @@
public void putBoolean(String key, boolean value) {
for (DebugFlag flag : FeatureFlags.getDebugFlags()) {
if (flag.key.equals(key)) {
- SharedPreferences.Editor editor = mContext.getSharedPreferences(
- FLAGS_PREF_NAME, Context.MODE_PRIVATE).edit();
- if (value == flag.defaultValue) {
+ SharedPreferences prefs = mContext.getSharedPreferences(
+ FLAGS_PREF_NAME, Context.MODE_PRIVATE);
+ SharedPreferences.Editor editor = prefs.edit();
+ // We keep the key in the prefs even if it has the default value, because it's a
+ // signal that it has been changed at one point.
+ if (!prefs.contains(key) && value == flag.defaultValue) {
editor.remove(key).apply();
+ flag.mHasBeenChangedAtLeastOnce = false;
} else {
editor.putBoolean(key, value).apply();
+ flag.mHasBeenChangedAtLeastOnce = true;
}
updateMenu();
}
diff --git a/src/com/android/launcher3/dragndrop/AddItemActivity.java b/src/com/android/launcher3/dragndrop/AddItemActivity.java
index 466b268..5a49f4a 100644
--- a/src/com/android/launcher3/dragndrop/AddItemActivity.java
+++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java
@@ -31,6 +31,7 @@
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.Intent;
+import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherApps.PinItemRequest;
import android.content.pm.ShortcutInfo;
import android.content.res.Configuration;
@@ -52,6 +53,8 @@
import android.view.accessibility.AccessibilityManager;
import android.widget.TextView;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.BaseActivity;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.Launcher;
@@ -62,12 +65,14 @@
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.pm.PinRequestHelper;
+import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.views.AbstractSlideInView;
import com.android.launcher3.views.BaseDragLayer;
import com.android.launcher3.widget.AddItemWidgetsBottomSheet;
-import com.android.launcher3.widget.LauncherAppWidgetHost;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.widget.LauncherWidgetHolder;
import com.android.launcher3.widget.NavigableAppWidgetHostView;
import com.android.launcher3.widget.PendingAddShortcutInfo;
import com.android.launcher3.widget.PendingAddWidgetInfo;
@@ -102,7 +107,8 @@
private WidgetCell mWidgetCell;
// Widget request specific options.
- private LauncherAppWidgetHost mAppWidgetHost;
+ @Nullable
+ private LauncherWidgetHolder mAppWidgetHolder = null;
private WidgetManagerHelper mAppWidgetManager;
private int mPendingBindWidgetId;
private Bundle mWidgetOptions;
@@ -136,13 +142,25 @@
mAccessibilityManager =
getApplicationContext().getSystemService(AccessibilityManager.class);
- if (mRequest.getRequestType() == PinItemRequest.REQUEST_TYPE_SHORTCUT) {
- setupShortcut();
- } else {
- if (!setupWidget()) {
- // TODO: show error toast?
- finish();
- }
+ PackageUserKey targetApp = null;
+ switch (mRequest.getRequestType()) {
+ case PinItemRequest.REQUEST_TYPE_SHORTCUT:
+ targetApp = setupShortcut();
+ break;
+ case PinItemRequest.REQUEST_TYPE_APPWIDGET:
+ targetApp = setupWidget();
+ break;
+ }
+ if (targetApp == null) {
+ // TODO: show error toast?
+ finish();
+ return;
+ }
+ ApplicationInfo info = new PackageManagerHelper(this)
+ .getApplicationInfo(targetApp.mPackageName, targetApp.mUser, 0);
+ if (info == null) {
+ finish();
+ return;
}
WidgetCellPreview previewContainer = mWidgetCell.findViewById(
@@ -156,8 +174,10 @@
logCommand(LAUNCHER_ADD_EXTERNAL_ITEM_START);
}
+ // Set the label synchronously instead of via IconCache as this is the first thing
+ // user sees
TextView widgetAppName = findViewById(R.id.widget_appName);
- widgetAppName.setText(getApplicationInfo().labelRes);
+ widgetAppName.setText(info.loadLabel(getPackageManager()));
mSlideInView = findViewById(R.id.add_item_bottom_sheet);
mSlideInView.addOnCloseListener(this);
@@ -246,25 +266,28 @@
}
}
- private void setupShortcut() {
+ private PackageUserKey setupShortcut() {
PinShortcutRequestActivityInfo shortcutInfo =
new PinShortcutRequestActivityInfo(mRequest, this);
mWidgetCell.getWidgetView().setTag(new PendingAddShortcutInfo(shortcutInfo));
applyWidgetItemAsync(
() -> new WidgetItem(shortcutInfo, mApp.getIconCache(), getPackageManager()));
+ return new PackageUserKey(
+ mRequest.getShortcutInfo().getPackage(),
+ mRequest.getShortcutInfo().getUserHandle());
}
- private boolean setupWidget() {
+ private PackageUserKey setupWidget() {
LauncherAppWidgetProviderInfo widgetInfo = LauncherAppWidgetProviderInfo
.fromProviderInfo(this, mRequest.getAppWidgetProviderInfo(this));
if (widgetInfo.minSpanX > mIdp.numColumns || widgetInfo.minSpanY > mIdp.numRows) {
// Cannot add widget
- return false;
+ return null;
}
mWidgetCell.setRemoteViewsPreview(PinItemDragListener.getPreview(mRequest));
mAppWidgetManager = new WidgetManagerHelper(this);
- mAppWidgetHost = new LauncherAppWidgetHost(this);
+ mAppWidgetHolder = new LauncherWidgetHolder(this);
PendingAddWidgetInfo pendingInfo =
new PendingAddWidgetInfo(widgetInfo, CONTAINER_PIN_WIDGETS);
@@ -274,7 +297,7 @@
mWidgetCell.getWidgetView().setTag(pendingInfo);
applyWidgetItemAsync(() -> new WidgetItem(widgetInfo, mIdp, mApp.getIconCache()));
- return true;
+ return new PackageUserKey(widgetInfo.provider.getPackageName(), widgetInfo.getUser());
}
private void applyWidgetItemAsync(final Supplier<WidgetItem> itemProvider) {
@@ -318,7 +341,7 @@
return;
}
- mPendingBindWidgetId = mAppWidgetHost.allocateAppWidgetId();
+ mPendingBindWidgetId = mAppWidgetHolder.allocateAppWidgetId();
AppWidgetProviderInfo widgetProviderInfo = mRequest.getAppWidgetProviderInfo(this);
boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed(
mPendingBindWidgetId, widgetProviderInfo, mWidgetOptions);
@@ -329,7 +352,7 @@
}
// request bind widget
- mAppWidgetHost.startBindFlow(this, mPendingBindWidgetId,
+ mAppWidgetHolder.startBindFlow(this, mPendingBindWidgetId,
mRequest.getAppWidgetProviderInfo(this), REQUEST_BIND_APPWIDGET);
}
@@ -343,6 +366,15 @@
}
@Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (mAppWidgetHolder != null) {
+ // Necessary to destroy the holder to free up possible activity context
+ mAppWidgetHolder.destroy();
+ }
+ }
+
+ @Override
public void onBackPressed() {
logCommand(LAUNCHER_ADD_EXTERNAL_ITEM_BACK);
mSlideInView.close(/* animate= */ true);
@@ -358,7 +390,7 @@
acceptWidget(widgetId);
} else {
// Simply wait it out.
- mAppWidgetHost.deleteAppWidgetId(widgetId);
+ mAppWidgetHolder.deleteAppWidgetId(widgetId);
mPendingBindWidgetId = -1;
}
return;
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index 8616f35..5368397 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -31,6 +31,7 @@
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;
@@ -90,6 +91,8 @@
protected boolean mIsInPreDrag;
+ private final int DRAG_VIEW_SCALE_DURATION_MS = 500;
+
/**
* Interface to receive notifications when a drag starts or stops
*/
@@ -214,6 +217,15 @@
mOptions.preDragCondition.onPreDragEnd(mDragObject, true /* dragStarted*/);
}
mIsInPreDrag = false;
+ if (mOptions.preDragEndScale != 0) {
+ mDragObject.dragView
+ .animate()
+ .scaleX(mOptions.preDragEndScale)
+ .scaleY(mOptions.preDragEndScale)
+ .setInterpolator(Interpolators.EMPHASIZED)
+ .setDuration(DRAG_VIEW_SCALE_DURATION_MS)
+ .start();
+ }
mDragObject.dragView.onDragStart();
for (DragListener listener : new ArrayList<>(mListeners)) {
listener.onDragStart(mDragObject, mOptions);
@@ -295,9 +307,9 @@
} else if (mIsInPreDrag) {
animateDragViewToOriginalPosition(null, null, -1);
}
+ mDragObject.dragView.clearAnimation();
mDragObject.dragView = null;
}
-
// Only end the drag if we are not deferred
if (!isDeferred) {
callOnDragEnd();
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index 8eeca7d..366870b 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -43,26 +43,28 @@
import android.view.animation.Interpolator;
import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.CellLayout;
import com.android.launcher3.DropTargetBar;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.ShortcutAndWidgetContainer;
+import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
+import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.SpringProperty;
+import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.graphics.Scrim;
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
-import com.android.launcher3.util.TouchController;
import com.android.launcher3.views.BaseDragLayer;
+import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlayCallbacks;
import java.util.ArrayList;
/**
* A ViewGroup that coordinates dragging across its descendants
*/
-public class DragLayer extends BaseDragLayer<Launcher> {
+public class DragLayer extends BaseDragLayer<Launcher> implements LauncherOverlayCallbacks {
public static final int ALPHA_INDEX_OVERLAY = 0;
private static final int ALPHA_CHANNEL_COUNT = 1;
@@ -70,6 +72,8 @@
public static final int ANIMATION_END_DISAPPEAR = 0;
public static final int ANIMATION_END_REMAIN_VISIBLE = 2;
+ private final boolean mIsRtl;
+
private DragController mDragController;
// Variables relating to animation of views after drop
@@ -100,6 +104,7 @@
setChildrenDrawingOrderEnabled(true);
mFocusIndicatorHelper = new ViewGroupFocusHelper(this);
+ mIsRtl = Utilities.isRtl(getResources());
}
/**
@@ -109,6 +114,7 @@
mDragController = dragController;
recreateControllers();
mWorkspaceDragScrim = new Scrim(this);
+ workspace.addOverlayCallback(this);
}
@Override
@@ -237,7 +243,7 @@
View anchorView) {
ShortcutAndWidgetContainer parentChildren = (ShortcutAndWidgetContainer) child.getParent();
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
parentChildren.measureChild(child);
parentChildren.layoutChild(child);
@@ -467,13 +473,15 @@
return mWorkspaceDragScrim;
}
- /**
- * Called when one handed mode state changed.
- * @param activated true if one handed mode activated, false otherwise.
- */
- public void onOneHandedModeStateChanged(boolean activated) {
- for (TouchController controller : mControllers) {
- controller.onOneHandedModeStateChanged(activated);
+ @Override
+ public void onOverlayScrollChanged(float progress) {
+ float alpha = 1 - Interpolators.DEACCEL_3.getInterpolation(progress);
+ float transX = getMeasuredWidth() * progress;
+
+ if (mIsRtl) {
+ transX = -transX;
}
+ setTranslationX(transX);
+ getAlphaProperty(ALPHA_INDEX_OVERLAY).setValue(alpha);
}
}
diff --git a/src/com/android/launcher3/dragndrop/DragOptions.java b/src/com/android/launcher3/dragndrop/DragOptions.java
index e8ff8da..1ff4335 100644
--- a/src/com/android/launcher3/dragndrop/DragOptions.java
+++ b/src/com/android/launcher3/dragndrop/DragOptions.java
@@ -40,6 +40,12 @@
/** Determines when a pre-drag should transition to a drag. By default, this is immediate. */
public PreDragCondition preDragCondition = null;
+ /**
+ * A drag scale that scales the original drag view size when the preDragCondition is met (or
+ * is ignored if preDragEndScale is 0).
+ */
+ public float preDragEndScale;
+
/** Scale of the icons over the workspace icon size. */
public float intrinsicIconScaleFactor = 1f;
diff --git a/src/com/android/launcher3/dragndrop/SpringLoadedDragController.java b/src/com/android/launcher3/dragndrop/SpringLoadedDragController.java
index fb8a1bc..08e50dd 100644
--- a/src/com/android/launcher3/dragndrop/SpringLoadedDragController.java
+++ b/src/com/android/launcher3/dragndrop/SpringLoadedDragController.java
@@ -20,12 +20,14 @@
import com.android.launcher3.CellLayout;
import com.android.launcher3.Launcher;
import com.android.launcher3.OnAlarmListener;
+import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
public class SpringLoadedDragController implements OnAlarmListener {
// how long the user must hover over a mini-screen before it unshrinks
- final long ENTER_SPRING_LOAD_HOVER_TIME = 500;
- final long ENTER_SPRING_LOAD_CANCEL_HOVER_TIME = 950;
+ private static final long ENTER_SPRING_LOAD_HOVER_TIME = 500;
+ private static final long ENTER_SPRING_LOAD_HOVER_TIME_IN_TEST = 1500;
+ private static final long ENTER_SPRING_LOAD_CANCEL_HOVER_TIME = 950;
Alarm mAlarm;
@@ -39,6 +41,13 @@
mAlarm.setOnAlarmListener(this);
}
+ private long getEnterSpringLoadHoverTime() {
+ // Some TAPL tests are flaky on Cuttlefish with a low waiting time
+ return Utilities.IS_RUNNING_IN_TEST_HARNESS
+ ? ENTER_SPRING_LOAD_HOVER_TIME_IN_TEST
+ : ENTER_SPRING_LOAD_HOVER_TIME;
+ }
+
public void cancel() {
mAlarm.cancelAlarm();
}
@@ -46,8 +55,8 @@
// Set a new alarm to expire for the screen that we are hovering over now
public void setAlarm(CellLayout cl) {
mAlarm.cancelAlarm();
- mAlarm.setAlarm((cl == null) ? ENTER_SPRING_LOAD_CANCEL_HOVER_TIME :
- ENTER_SPRING_LOAD_HOVER_TIME);
+ mAlarm.setAlarm((cl == null) ? ENTER_SPRING_LOAD_CANCEL_HOVER_TIME
+ : getEnterSpringLoadHoverTime());
mScreen = cl;
}
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index e68ebdb..99822da 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -292,7 +292,7 @@
mFolderName.forceDisableSuggestions(true);
mFooter = findViewById(R.id.folder_footer);
- mFooterHeight = getResources().getDimensionPixelSize(R.dimen.folder_label_height);
+ mFooterHeight = dp.folderFooterHeightPx;
if (Utilities.ATLEAST_R) {
mKeyboardInsetAnimationCallback = new KeyboardInsetAnimationCallback(this);
@@ -368,9 +368,7 @@
public void startEditingFolderName() {
post(() -> {
- if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
- showLabelSuggestions();
- }
+ showLabelSuggestions();
mFolderName.setHint("");
mIsEditingName = true;
});
@@ -1080,8 +1078,7 @@
if (!items.isEmpty()) {
mLauncherDelegate.getModelWriter().moveItemsInDatabase(items, mInfo.id, 0);
}
- if (FeatureFlags.FOLDER_NAME_SUGGEST.get() && !isBind
- && total > 1 /* no need to update if there's one icon */) {
+ if (!isBind && total > 1 /* no need to update if there's one icon */) {
Executors.MODEL_EXECUTOR.post(() -> {
FolderNameInfos nameInfos = new FolderNameInfos();
FolderNameProvider fnp = FolderNameProvider.newInstance(getContext());
@@ -1170,14 +1167,6 @@
mContent.setFixedSize(contentWidth, contentHeight);
mContent.measure(contentAreaWidthSpec, contentAreaHeightSpec);
- if (mContent.getChildCount() > 0) {
- int cellIconGap = (mContent.getPageAt(0).getCellWidth()
- - mActivityContext.getDeviceProfile().iconSizePx) / 2;
- mFooter.setPadding(mContent.getPaddingLeft() + cellIconGap,
- mFooter.getPaddingTop(),
- mContent.getPaddingRight() + cellIconGap,
- mFooter.getPaddingBottom());
- }
mFooter.measure(contentAreaWidthSpec,
MeasureSpec.makeMeasureSpec(mFooterHeight, MeasureSpec.EXACTLY));
diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java
index 61ffd9d..05ad57a 100644
--- a/src/com/android/launcher3/folder/FolderAnimationManager.java
+++ b/src/com/android/launcher3/folder/FolderAnimationManager.java
@@ -43,6 +43,7 @@
import com.android.launcher3.ShortcutAndWidgetContainer;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.PropertyResetListener;
+import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.BaseDragLayer;
@@ -167,9 +168,11 @@
final int paddingOffsetY = (int) (mContent.getPaddingTop() * initialScale);
int initialX = folderIconPos.left + mFolder.getPaddingLeft()
- + mPreviewBackground.getOffsetX() - paddingOffsetX - previewItemOffsetX;
+ + Math.round(mPreviewBackground.getOffsetX() * scaleRelativeToDragLayer)
+ - paddingOffsetX - previewItemOffsetX;
int initialY = folderIconPos.top + mFolder.getPaddingTop()
- + mPreviewBackground.getOffsetY() - paddingOffsetY;
+ + Math.round(mPreviewBackground.getOffsetY() * scaleRelativeToDragLayer)
+ - paddingOffsetY;
final float xDistance = initialX - lp.x;
final float yDistance = initialY - lp.y;
@@ -215,6 +218,7 @@
final int footerStartDelay;
if (isLargeFolder()) {
if (mIsOpening) {
+ mFolder.mFooter.setAlpha(0);
footerAlphaDuration = LARGE_FOLDER_FOOTER_DURATION;
footerStartDelay = mDuration - footerAlphaDuration;
} else {
@@ -232,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.x
+ int width = mDeviceProfile.folderCellLayoutBorderSpacePx
+ mDeviceProfile.folderCellWidthPx * 2;
- int height = mDeviceProfile.folderCellLayoutBorderSpacePx.y
+ int height = mDeviceProfile.folderCellLayoutBorderSpacePx
+ mDeviceProfile.folderCellHeightPx * 2;
int page = mIsOpening ? mContent.getCurrentPage() : mContent.getDestinationPage();
int left = mContent.getPaddingLeft() + page * lp.width;
@@ -311,7 +315,7 @@
addPreviewItemAnimators(a, initialScale / scaleRelativeToDragLayer,
// Background can have a scaled radius in drag and drop mode, so we need to add the
// difference to keep the preview items centered.
- previewItemOffsetX + radiusDiff, radiusDiff);
+ (int) (previewItemOffsetX / scaleRelativeToDragLayer) + radiusDiff, radiusDiff);
return a;
}
@@ -341,7 +345,7 @@
ShortcutAndWidgetContainer cwc = mContent.getPageAt(0).getShortcutsAndWidgets();
for (int i = 0; i < numItemsInPreview; ++i) {
final BubbleTextView btv = itemsInPreview.get(i);
- CellLayout.LayoutParams btvLp = (CellLayout.LayoutParams) btv.getLayoutParams();
+ CellLayoutLayoutParams btvLp = (CellLayoutLayoutParams) btv.getLayoutParams();
// Calculate the final values in the LayoutParams.
btvLp.isLockedToGrid = true;
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index b1e2701..dd00f07 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -57,7 +57,7 @@
import com.android.launcher3.Workspace;
import com.android.launcher3.allapps.ActivityAllAppsContainerView;
import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.dot.FolderDotInfo;
import com.android.launcher3.dragndrop.BaseItemDragListener;
import com.android.launcher3.dragndrop.DragLayer;
@@ -278,7 +278,7 @@
public void onDragEnter(ItemInfo dragInfo) {
if (mFolder.isDestroyed() || !willAcceptItem(dragInfo)) return;
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) getLayoutParams();
CellLayout cl = (CellLayout) getParent().getParent();
mBackground.animateToAccept(cl, lp.cellX, lp.cellY);
@@ -418,35 +418,23 @@
if (!itemAdded) mPreviewItemManager.hidePreviewItem(index, true);
FolderNameInfos nameInfos = new FolderNameInfos();
- if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
- Executors.MODEL_EXECUTOR.post(() -> {
- d.folderNameProvider.getSuggestedFolderName(
- getContext(), mInfo.contents, nameInfos);
- showFinalView(finalIndex, item, nameInfos, d.logInstanceId);
- });
- } else {
- showFinalView(finalIndex, item, nameInfos, d.logInstanceId);
- }
+ Executors.MODEL_EXECUTOR.post(() -> {
+ d.folderNameProvider.getSuggestedFolderName(
+ getContext(), mInfo.contents, nameInfos);
+ postDelayed(() -> {
+ setLabelSuggestion(nameInfos, d.logInstanceId);
+ invalidate();
+ }, DROP_IN_ANIMATION_DURATION);
+ });
} else {
addItem(item);
}
}
- private void showFinalView(int finalIndex, final WorkspaceItemInfo item,
- FolderNameInfos nameInfos, InstanceId instanceId) {
- postDelayed(() -> {
- setLabelSuggestion(nameInfos, instanceId);
- invalidate();
- }, DROP_IN_ANIMATION_DURATION);
- }
-
/**
* Set the suggested folder name.
*/
public void setLabelSuggestion(FolderNameInfos nameInfos, InstanceId instanceId) {
- if (!FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
- return;
- }
if (!mInfo.getLabelState().equals(LabelState.UNLABELED)) {
return;
}
@@ -628,11 +616,14 @@
public void drawDot(Canvas canvas) {
if (!mForceHideDot && ((mDotInfo != null && mDotInfo.hasDot()) || mDotScale > 0)) {
Rect iconBounds = mDotParams.iconBounds;
+ // FolderIcon draws the icon to be top-aligned (with padding) & horizontally-centered
+ int iconSize = mActivity.getDeviceProfile().iconSizePx;
+ iconBounds.left = (getWidth() - iconSize) / 2;
+ iconBounds.right = iconBounds.left + iconSize;
+ iconBounds.top = getPaddingTop();
+ iconBounds.bottom = iconBounds.top + iconSize;
- Utilities.setRectToViewCenter(this, mActivity.getDeviceProfile().iconSizePx,
- iconBounds);
- iconBounds.offsetTo(iconBounds.left, getPaddingTop());
- float iconScale = (float) mBackground.previewSize / iconBounds.width();
+ float iconScale = (float) mBackground.previewSize / iconSize;
Utilities.scaleRectAboutCenter(iconBounds, iconScale);
// If we are animating to the accepting state, animate the dot out.
diff --git a/src/com/android/launcher3/folder/FolderNameProvider.java b/src/com/android/launcher3/folder/FolderNameProvider.java
index 5021644..bf59594 100644
--- a/src/com/android/launcher3/folder/FolderNameProvider.java
+++ b/src/com/android/launcher3/folder/FolderNameProvider.java
@@ -24,6 +24,7 @@
import android.text.TextUtils;
import android.util.Log;
+import androidx.annotation.NonNull;
import androidx.annotation.WorkerThread;
import com.android.launcher3.LauncherAppState;
@@ -192,7 +193,8 @@
private class FolderNameWorker extends BaseModelUpdateTask {
@Override
- public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+ public void execute(@NonNull final LauncherAppState app,
+ @NonNull final BgDataModel dataModel, @NonNull final AllAppsList apps) {
mFolderInfos = dataModel.folders.clone();
mAppInfos = Arrays.asList(apps.copyData());
}
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index 3d5aef5..efd511d 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -41,6 +41,7 @@
import com.android.launcher3.R;
import com.android.launcher3.ShortcutAndWidgetContainer;
import com.android.launcher3.Utilities;
+import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -112,6 +113,7 @@
public void setFolder(Folder folder) {
mFolder = folder;
mPageIndicator = folder.findViewById(R.id.folder_page_indicator);
+ mPageIndicator.setShouldAutoHide(false);
initParentViews(folder);
}
@@ -202,7 +204,7 @@
public void addViewForRank(View view, WorkspaceItemInfo item, int rank) {
int pageNo = rank / mOrganizer.getMaxItemsPerPage();
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) view.getLayoutParams();
lp.setCellXY(mOrganizer.getPosForRank(rank));
getPageAt(pageNo).addViewToCellLayout(view, -1, item.getViewId(), lp, true);
}
@@ -218,9 +220,9 @@
textView.setOnClickListener(ItemClickHandler.INSTANCE);
textView.setOnLongClickListener(mFolder);
textView.setOnFocusChangeListener(mFocusIndicatorHelper);
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) textView.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) textView.getLayoutParams();
if (lp == null) {
- textView.setLayoutParams(new CellLayout.LayoutParams(
+ textView.setLayoutParams(new CellLayoutLayoutParams(
item.cellX, item.cellY, item.spanX, item.spanY));
} else {
lp.cellX = item.cellX;
@@ -314,7 +316,7 @@
}
if (v != null) {
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) v.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) v.getLayoutParams();
ItemInfo info = (ItemInfo) v.getTag();
lp.setCellXY(mOrganizer.getPosForRank(rank));
currentPage.addViewToCellLayout(v, -1, info.getViewId(), lp, true);
diff --git a/src/com/android/launcher3/graphics/DragPreviewProvider.java b/src/com/android/launcher3/graphics/DragPreviewProvider.java
index f027b33..7457f30 100644
--- a/src/com/android/launcher3/graphics/DragPreviewProvider.java
+++ b/src/com/android/launcher3/graphics/DragPreviewProvider.java
@@ -16,24 +16,16 @@
package com.android.launcher3.graphics;
-import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
-
import android.content.Context;
import android.graphics.Bitmap;
-import android.graphics.BlurMaskFilter;
import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.view.View;
import androidx.annotation.Nullable;
-import com.android.launcher3.BubbleTextView;
import com.android.launcher3.R;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DraggableView;
import com.android.launcher3.icons.BitmapRenderer;
import com.android.launcher3.icons.FastBitmapDrawable;
@@ -41,8 +33,6 @@
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
-import java.nio.ByteBuffer;
-
/**
* A utility class to generate preview bitmap for dragging.
*/
@@ -57,9 +47,6 @@
public final int blurSizeOutline;
- private OutlineGeneratorCallback mOutlineGeneratorCallback;
- public Bitmap generatedDragOutline;
-
public DragPreviewProvider(View view) {
this(view, view.getContext());
}
@@ -129,15 +116,6 @@
return null;
}
- public final void generateDragOutline(Bitmap preview) {
- if (FeatureFlags.IS_STUDIO_BUILD && mOutlineGeneratorCallback != null) {
- throw new RuntimeException("Drag outline generated twice");
- }
-
- mOutlineGeneratorCallback = new OutlineGeneratorCallback(preview);
- UI_HELPER_EXECUTOR.post(mOutlineGeneratorCallback);
- }
-
protected static Rect getDrawableBounds(Drawable d) {
Rect bounds = new Rect();
d.copyBounds(bounds);
@@ -184,92 +162,4 @@
protected Bitmap convertPreviewToAlphaBitmap(Bitmap preview) {
return preview.copy(Bitmap.Config.ALPHA_8, true);
}
-
- private class OutlineGeneratorCallback implements Runnable {
-
- private final Bitmap mPreviewSnapshot;
- private final Context mContext;
- private final boolean mIsIcon;
-
- OutlineGeneratorCallback(Bitmap preview) {
- mPreviewSnapshot = preview;
- mContext = mView.getContext();
- mIsIcon = mView instanceof BubbleTextView;
- }
-
- @Override
- public void run() {
- Bitmap preview = convertPreviewToAlphaBitmap(mPreviewSnapshot);
- if (mIsIcon) {
- int size = ActivityContext.lookupContext(mContext).getDeviceProfile().iconSizePx;
- preview = Bitmap.createScaledBitmap(preview, size, size, false);
- }
- //else case covers AppWidgetHost (doesn't drag/drop across different device profiles)
-
- // We start by removing most of the alpha channel so as to ignore shadows, and
- // other types of partial transparency when defining the shape of the object
- byte[] pixels = new byte[preview.getWidth() * preview.getHeight()];
- ByteBuffer buffer = ByteBuffer.wrap(pixels);
- buffer.rewind();
- preview.copyPixelsToBuffer(buffer);
-
- for (int i = 0; i < pixels.length; i++) {
- if ((pixels[i] & 0xFF) < 188) {
- pixels[i] = 0;
- }
- }
-
- buffer.rewind();
- preview.copyPixelsFromBuffer(buffer);
-
- final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
- Canvas canvas = new Canvas();
-
- // calculate the outer blur first
- paint.setMaskFilter(new BlurMaskFilter(blurSizeOutline, BlurMaskFilter.Blur.OUTER));
- int[] outerBlurOffset = new int[2];
- Bitmap thickOuterBlur = preview.extractAlpha(paint, outerBlurOffset);
-
- paint.setMaskFilter(new BlurMaskFilter(
- mContext.getResources().getDimension(R.dimen.blur_size_thin_outline),
- BlurMaskFilter.Blur.OUTER));
- int[] brightOutlineOffset = new int[2];
- Bitmap brightOutline = preview.extractAlpha(paint, brightOutlineOffset);
-
- // calculate the inner blur
- canvas.setBitmap(preview);
- canvas.drawColor(0xFF000000, PorterDuff.Mode.SRC_OUT);
- paint.setMaskFilter(new BlurMaskFilter(blurSizeOutline, BlurMaskFilter.Blur.NORMAL));
- int[] thickInnerBlurOffset = new int[2];
- Bitmap thickInnerBlur = preview.extractAlpha(paint, thickInnerBlurOffset);
-
- // mask out the inner blur
- paint.setMaskFilter(null);
- paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
- canvas.setBitmap(thickInnerBlur);
- canvas.drawBitmap(preview, -thickInnerBlurOffset[0],
- -thickInnerBlurOffset[1], paint);
- canvas.drawRect(0, 0, -thickInnerBlurOffset[0], thickInnerBlur.getHeight(), paint);
- canvas.drawRect(0, 0, thickInnerBlur.getWidth(), -thickInnerBlurOffset[1], paint);
-
- // draw the inner and outer blur
- paint.setXfermode(null);
- canvas.setBitmap(preview);
- canvas.drawColor(0, PorterDuff.Mode.CLEAR);
- canvas.drawBitmap(thickInnerBlur, thickInnerBlurOffset[0], thickInnerBlurOffset[1],
- paint);
- canvas.drawBitmap(thickOuterBlur, outerBlurOffset[0], outerBlurOffset[1], paint);
-
- // draw the bright outline
- canvas.drawBitmap(brightOutline, brightOutlineOffset[0], brightOutlineOffset[1], paint);
-
- // cleanup
- canvas.setBitmap(null);
- brightOutline.recycle();
- thickOuterBlur.recycle();
- thickInnerBlur.recycle();
-
- generatedDragOutline = preview;
- }
- }
}
diff --git a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
index fc8d855..c28bab5 100644
--- a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
+++ b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
@@ -26,7 +26,6 @@
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.InvariantDeviceProfile.GridOption;
import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.Executors;
/**
@@ -143,11 +142,9 @@
}
case ICON_THEMED:
case SET_ICON_THEMED: {
- if (FeatureFlags.ENABLE_THEMED_ICONS.get()) {
- getPrefs(getContext()).edit()
- .putBoolean(KEY_THEMED_ICONS, values.getAsBoolean(BOOLEAN_VALUE))
- .apply();
- }
+ getPrefs(getContext()).edit()
+ .putBoolean(KEY_THEMED_ICONS, values.getAsBoolean(BOOLEAN_VALUE))
+ .apply();
return 1;
}
default:
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index c1bab54..2361907 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -72,6 +72,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
import com.android.launcher3.WorkspaceLayoutManager;
+import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.icons.BaseIconFactory;
@@ -98,8 +99,8 @@
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.BaseDragLayer;
import com.android.launcher3.widget.BaseLauncherAppWidgetHostView;
-import com.android.launcher3.widget.LauncherAppWidgetHost;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.widget.LauncherWidgetHolder;
import com.android.launcher3.widget.LocalColorExtractor;
import com.android.launcher3.widget.NavigableAppWidgetHostView;
import com.android.launcher3.widget.custom.CustomWidgetManager;
@@ -532,8 +533,8 @@
CellLayout firstScreen = mWorkspaceScreens.get(FIRST_SCREEN_ID);
View qsb = mHomeElementInflater.inflate(R.layout.qsb_preview, firstScreen,
false);
- CellLayout.LayoutParams lp =
- new CellLayout.LayoutParams(0, 0, firstScreen.getCountX(), 1);
+ CellLayoutLayoutParams lp = new CellLayoutLayoutParams(0, 0, firstScreen.getCountX(),
+ 1);
lp.canReorder = false;
firstScreen.addViewToCellLayout(qsb, 0, R.id.search_container_workspace, lp, true);
}
@@ -553,7 +554,7 @@
private class LauncherPreviewAppWidgetHost extends AppWidgetHost {
private LauncherPreviewAppWidgetHost(Context context) {
- super(context, LauncherAppWidgetHost.APPWIDGET_HOST_ID);
+ super(context, LauncherWidgetHolder.APPWIDGET_HOST_ID);
}
@Override
diff --git a/src/com/android/launcher3/icons/ComponentWithLabel.java b/src/com/android/launcher3/icons/ComponentWithLabel.java
index 372c591..30575fc 100644
--- a/src/com/android/launcher3/icons/ComponentWithLabel.java
+++ b/src/com/android/launcher3/icons/ComponentWithLabel.java
@@ -20,6 +20,8 @@
import android.content.pm.PackageManager;
import android.os.UserHandle;
+import androidx.annotation.NonNull;
+
import com.android.launcher3.icons.cache.CachingLogic;
public interface ComponentWithLabel {
@@ -42,22 +44,26 @@
}
@Override
- public ComponentName getComponent(T object) {
+ @NonNull
+ public ComponentName getComponent(@NonNull T object) {
return object.getComponent();
}
+ @NonNull
@Override
- public UserHandle getUser(T object) {
+ public UserHandle getUser(@NonNull T object) {
return object.getUser();
}
+ @NonNull
@Override
- public CharSequence getLabel(T object) {
+ public CharSequence getLabel(@NonNull T object) {
return object.getLabel(mPackageManager);
}
+ @NonNull
@Override
- public BitmapInfo loadIcon(Context context, T object) {
+ public BitmapInfo loadIcon(@NonNull Context context, @NonNull T object) {
return BitmapInfo.LOW_RES_INFO;
}
diff --git a/src/com/android/launcher3/icons/ComponentWithLabelAndIcon.java b/src/com/android/launcher3/icons/ComponentWithLabelAndIcon.java
index c8606b1..0a52dd7 100644
--- a/src/com/android/launcher3/icons/ComponentWithLabelAndIcon.java
+++ b/src/com/android/launcher3/icons/ComponentWithLabelAndIcon.java
@@ -41,7 +41,8 @@
@NonNull
@Override
- public BitmapInfo loadIcon(Context context, ComponentWithLabelAndIcon object) {
+ public BitmapInfo loadIcon(@NonNull Context context,
+ @NonNull ComponentWithLabelAndIcon object) {
Drawable d = object.getFullResIcon(LauncherAppState.getInstance(context)
.getIconCache());
if (d == null) {
diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java
index fe9b633..0b4a4a5 100644
--- a/src/com/android/launcher3/icons/IconCache.java
+++ b/src/com/android/launcher3/icons/IconCache.java
@@ -51,7 +51,6 @@
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherFiles;
import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.ComponentWithLabel.ComponentCachingLogic;
import com.android.launcher3.icons.cache.BaseIconCache;
import com.android.launcher3.icons.cache.CachingLogic;
@@ -119,15 +118,16 @@
}
@Override
- protected long getSerialNumberForUser(UserHandle user) {
+ protected long getSerialNumberForUser(@NonNull UserHandle user) {
return mUserManager.getSerialNumberForUser(user);
}
@Override
- protected boolean isInstantApp(ApplicationInfo info) {
+ protected boolean isInstantApp(@NonNull ApplicationInfo info) {
return mInstantAppResolver.isInstantApp(info);
}
+ @NonNull
@Override
public BaseIconFactory getIconFactory() {
return LauncherIcons.obtain(mContext);
@@ -136,7 +136,8 @@
/**
* Updates the entries related to the given package in memory and persistent DB.
*/
- public synchronized void updateIconsForPkg(String packageName, UserHandle user) {
+ public synchronized void updateIconsForPkg(@NonNull final String packageName,
+ @NonNull final UserHandle user) {
removeIconsForPkg(packageName, user);
try {
PackageInfo info = mPackageManager.getPackageInfo(packageName,
@@ -231,14 +232,8 @@
*/
public <T extends ItemInfoWithIcon> void getShortcutIcon(T info, ShortcutInfo si,
@NonNull Predicate<T> fallbackIconCheck) {
- BitmapInfo bitmapInfo;
- if (FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) {
- bitmapInfo = cacheLocked(ShortcutKey.fromInfo(si).componentName, si.getUserHandle(),
- () -> si, mShortcutCachingLogic, false, false).bitmap;
- } else {
- // If caching is disabled, load the full icon
- bitmapInfo = mShortcutCachingLogic.loadIcon(mContext, si);
- }
+ BitmapInfo bitmapInfo = cacheLocked(ShortcutKey.fromInfo(si).componentName,
+ si.getUserHandle(), () -> si, mShortcutCachingLogic, false, false).bitmap;
if (bitmapInfo.isNullOrLowRes()) {
bitmapInfo = getDefaultIcon(si.getUserHandle());
}
@@ -478,7 +473,7 @@
* Fill in {@param infoInOut} with the corresponding icon and label.
*/
public synchronized void getTitleAndIconForApp(
- PackageItemInfo infoInOut, boolean useLowResIcon) {
+ @NonNull final PackageItemInfo infoInOut, final boolean useLowResIcon) {
CacheEntry entry = getEntryForPackageLocked(
infoInOut.packageName, infoInOut.user, useLowResIcon);
applyCacheEntry(entry, infoInOut);
@@ -517,10 +512,16 @@
return bitmap.withFlags(getUserFlagOpLocked(user));
}
- protected void applyCacheEntry(CacheEntry entry, ItemInfoWithIcon info) {
+ protected void applyCacheEntry(@NonNull final CacheEntry entry,
+ @NonNull final ItemInfoWithIcon info) {
info.title = Utilities.trim(entry.title);
info.contentDescription = entry.contentDescription;
- info.bitmap = (entry.bitmap == null) ? getDefaultIcon(info.user) : entry.bitmap;
+ info.bitmap = entry.bitmap;
+ if (entry.bitmap == null) {
+ // TODO: entry.bitmap can never be null, so this should not happen at all.
+ Log.wtf(TAG, "Cannot find bitmap from the cache, default icon was loaded.");
+ info.bitmap = getDefaultIcon(info.user);
+ }
}
public Drawable getFullResIcon(LauncherActivityInfo info) {
@@ -533,6 +534,7 @@
}
@Override
+ @NonNull
protected String getIconSystemState(String packageName) {
return mIconProvider.getSystemStateForPackage(mSystemState, packageName);
}
diff --git a/src/com/android/launcher3/icons/LauncherActivityCachingLogic.java b/src/com/android/launcher3/icons/LauncherActivityCachingLogic.java
index 4b8c1ad..406f697 100644
--- a/src/com/android/launcher3/icons/LauncherActivityCachingLogic.java
+++ b/src/com/android/launcher3/icons/LauncherActivityCachingLogic.java
@@ -20,6 +20,8 @@
import android.content.pm.LauncherActivityInfo;
import android.os.UserHandle;
+import androidx.annotation.NonNull;
+
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
import com.android.launcher3.icons.BaseIconFactory.IconOptions;
@@ -40,23 +42,27 @@
R.string.launcher_activity_logic_class);
}
+ @NonNull
@Override
- public ComponentName getComponent(LauncherActivityInfo object) {
+ public ComponentName getComponent(@NonNull LauncherActivityInfo object) {
return object.getComponentName();
}
+ @NonNull
@Override
- public UserHandle getUser(LauncherActivityInfo object) {
+ public UserHandle getUser(@NonNull LauncherActivityInfo object) {
return object.getUser();
}
+ @NonNull
@Override
- public CharSequence getLabel(LauncherActivityInfo object) {
+ public CharSequence getLabel(@NonNull LauncherActivityInfo object) {
return object.getLabel();
}
+ @NonNull
@Override
- public BitmapInfo loadIcon(Context context, LauncherActivityInfo object) {
+ public BitmapInfo loadIcon(@NonNull Context context, @NonNull LauncherActivityInfo object) {
try (LauncherIcons li = LauncherIcons.obtain(context)) {
return li.createBadgedIconBitmap(LauncherAppState.getInstance(context)
.getIconProvider().getIcon(object, li.mFillResIconDpi),
diff --git a/src/com/android/launcher3/icons/ShortcutCachingLogic.java b/src/com/android/launcher3/icons/ShortcutCachingLogic.java
index 6a8f34a..9a86ede 100644
--- a/src/com/android/launcher3/icons/ShortcutCachingLogic.java
+++ b/src/com/android/launcher3/icons/ShortcutCachingLogic.java
@@ -29,9 +29,9 @@
import android.util.Log;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.cache.CachingLogic;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.util.Themes;
@@ -44,41 +44,48 @@
private static final String TAG = "ShortcutCachingLogic";
@Override
- public ComponentName getComponent(ShortcutInfo info) {
+ @NonNull
+ public ComponentName getComponent(@NonNull ShortcutInfo info) {
return ShortcutKey.fromInfo(info).componentName;
}
+ @NonNull
@Override
- public UserHandle getUser(ShortcutInfo info) {
+ public UserHandle getUser(@NonNull ShortcutInfo info) {
return info.getUserHandle();
}
+ @NonNull
@Override
- public CharSequence getLabel(ShortcutInfo info) {
+ public CharSequence getLabel(@NonNull ShortcutInfo info) {
return info.getShortLabel();
}
@Override
- public CharSequence getDescription(ShortcutInfo object, CharSequence fallback) {
+ @NonNull
+ public CharSequence getDescription(@NonNull ShortcutInfo object,
+ @NonNull CharSequence fallback) {
CharSequence label = object.getLongLabel();
return TextUtils.isEmpty(label) ? fallback : label;
}
@NonNull
@Override
- public BitmapInfo loadIcon(Context context, ShortcutInfo info) {
+ public BitmapInfo loadIcon(@NonNull Context context, @NonNull ShortcutInfo info) {
try (LauncherIcons li = LauncherIcons.obtain(context)) {
Drawable unbadgedDrawable = ShortcutCachingLogic.getIcon(
context, info, LauncherAppState.getIDP(context).fillResIconDpi);
if (unbadgedDrawable == null) return BitmapInfo.LOW_RES_INFO;
- return new BitmapInfo(li.createScaledBitmapWithoutShadow(unbadgedDrawable),
+ return new BitmapInfo(
+ li.createScaledBitmap(unbadgedDrawable, BaseIconFactory.MODE_WITH_SHADOW),
Themes.getColorAccent(context));
}
}
@Override
- public long getLastUpdatedTime(ShortcutInfo shortcutInfo, PackageInfo info) {
- if (shortcutInfo == null || !FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) {
+ public long getLastUpdatedTime(@Nullable ShortcutInfo shortcutInfo,
+ @NonNull PackageInfo info) {
+ if (shortcutInfo == null) {
return info.lastUpdateTime;
}
return Math.max(shortcutInfo.getLastChangedTimestamp(), info.lastUpdateTime);
diff --git a/src/com/android/launcher3/logging/EventLogArray.java b/src/com/android/launcher3/logging/EventLogArray.java
deleted file mode 100644
index 3ecfb23..0000000
--- a/src/com/android/launcher3/logging/EventLogArray.java
+++ /dev/null
@@ -1,150 +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.logging;
-
-
-import android.util.Log;
-import java.io.PrintWriter;
-import java.text.SimpleDateFormat;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.Locale;
-import java.util.Random;
-
-/**
- * A utility class to record and log events. Events are stored in a fixed size array and old logs
- * are purged as new events come.
- */
-public class EventLogArray {
-
- private static final int TYPE_ONE_OFF = 0;
- private static final int TYPE_FLOAT = 1;
- private static final int TYPE_INTEGER = 2;
- private static final int TYPE_BOOL_TRUE = 3;
- private static final int TYPE_BOOL_FALSE = 4;
-
- private final String name;
- private final EventEntry[] logs;
- private int nextIndex;
- private int mLogId;
-
- public EventLogArray(String name, int size) {
- this.name = name;
- logs = new EventEntry[size];
- nextIndex = 0;
- }
-
- public void addLog(String event) {
- addLog(TYPE_ONE_OFF, event, 0);
- }
-
- public void addLog(String event, int extras) {
- addLog(TYPE_INTEGER, event, extras);
- }
-
- public void addLog(String event, boolean extras) {
- addLog(extras ? TYPE_BOOL_TRUE : TYPE_BOOL_FALSE, event, 0);
- }
-
- private void addLog(int type, String event, float extras) {
- // Merge the logs if its a duplicate
- int last = (nextIndex + logs.length - 1) % logs.length;
- int secondLast = (nextIndex + logs.length - 2) % logs.length;
- if (isEntrySame(logs[last], type, event) && isEntrySame(logs[secondLast], type, event)) {
- logs[last].update(type, event, extras, mLogId);
- logs[secondLast].duplicateCount++;
- return;
- }
-
- if (logs[nextIndex] == null) {
- logs[nextIndex] = new EventEntry();
- }
- logs[nextIndex].update(type, event, extras, mLogId);
- nextIndex = (nextIndex + 1) % logs.length;
- }
-
- public void clear() {
- Arrays.setAll(logs, (i) -> null);
- }
-
- public void dump(String prefix, PrintWriter writer) {
- writer.println(prefix + "EventLog (" + name + ") history:");
- SimpleDateFormat sdf = new SimpleDateFormat(" HH:mm:ss.SSSZ ", Locale.US);
- Date date = new Date();
-
- for (int i = 0; i < logs.length; i++) {
- EventEntry log = logs[(nextIndex + logs.length - i - 1) % logs.length];
- if (log == null) {
- continue;
- }
- date.setTime(log.time);
-
- StringBuilder msg = new StringBuilder(prefix).append(sdf.format(date))
- .append(log.event);
- switch (log.type) {
- case TYPE_BOOL_FALSE:
- msg.append(": false");
- break;
- case TYPE_BOOL_TRUE:
- msg.append(": true");
- break;
- case TYPE_FLOAT:
- msg.append(": ").append(log.extras);
- break;
- case TYPE_INTEGER:
- msg.append(": ").append((int) log.extras);
- break;
- default: // fall out
- }
- if (log.duplicateCount > 0) {
- msg.append(" & ").append(log.duplicateCount).append(" similar events");
- }
- msg.append(" traceId: ").append(log.traceId);
- writer.println(msg);
- }
- }
-
- /** Returns a 3 digit random number between 100-999 */
- public int generateAndSetLogId() {
- Random r = new Random();
- mLogId = r.nextInt(900) + 100;
- return mLogId;
- }
-
- private boolean isEntrySame(EventEntry entry, int type, String event) {
- return entry != null && entry.type == type && entry.event.equals(event);
- }
-
- /** A single event entry. */
- private static class EventEntry {
-
- private int type;
- private String event;
- private float extras;
- private long time;
- private int duplicateCount;
- private int traceId;
-
- public void update(int type, String event, float extras, int traceId) {
- this.type = type;
- this.event = event;
- this.extras = extras;
- this.traceId = traceId;
- time = System.currentTimeMillis();
- duplicateCount = 0;
- }
- }
-}
diff --git a/src/com/android/launcher3/logging/InstanceId.java b/src/com/android/launcher3/logging/InstanceId.java
index 3c4a644..5bbe07c 100644
--- a/src/com/android/launcher3/logging/InstanceId.java
+++ b/src/com/android/launcher3/logging/InstanceId.java
@@ -47,7 +47,6 @@
this(in.readInt());
}
- @VisibleForTesting
public int getId() {
return mId;
}
diff --git a/src/com/android/launcher3/logging/KeyboardStateManager.java b/src/com/android/launcher3/logging/KeyboardStateManager.java
new file mode 100644
index 0000000..6dc0a0b
--- /dev/null
+++ b/src/com/android/launcher3/logging/KeyboardStateManager.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.logging;
+
+import static com.android.launcher3.logging.KeyboardStateManager.KeyboardState.NO_IME_ACTION;
+
+import android.os.SystemClock;
+
+/**
+ * Class to maintain keyboard states.
+ */
+public class KeyboardStateManager {
+ private long mUpdatedTime;
+ private int mImeHeight;
+
+ public enum KeyboardState {
+ NO_IME_ACTION,
+ SHOW,
+ HIDE,
+ }
+
+ private KeyboardState mKeyboardState;
+
+ public KeyboardStateManager() {
+ mKeyboardState = NO_IME_ACTION;
+ }
+
+ /**
+ * Returns time when keyboard state was updated.
+ */
+ public long getLastUpdatedTime() {
+ return mUpdatedTime;
+ }
+
+ /**
+ * Returns current keyboard state.
+ */
+ public KeyboardState getKeyboardState() {
+ return mKeyboardState;
+ }
+
+ /**
+ * Setter method to set keyboard state.
+ */
+ public void setKeyboardState(KeyboardState keyboardState) {
+ mUpdatedTime = SystemClock.elapsedRealtime();
+ mKeyboardState = keyboardState;
+ }
+
+ /**
+ * Returns keyboard's current height.
+ */
+ public int getImeHeight() {
+ return mImeHeight;
+ }
+
+ /**
+ * Setter method to set keyboard height.
+ */
+ public void setImeHeight(int imeHeight) {
+ mImeHeight = imeHeight;
+ }
+}
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index c4ec4e3..0e42d58 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -32,9 +32,12 @@
import com.android.launcher3.logger.LauncherAtom.FromState;
import com.android.launcher3.logger.LauncherAtom.ToState;
import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.ResourceBasedOverride;
import com.android.launcher3.views.ActivityContext;
+import java.util.List;
+
/**
* Handles the user event logging in R+.
*
@@ -56,6 +59,7 @@
private InstanceId mInstanceId;
protected @Nullable ActivityContext mActivityContext = null;
+ private KeyboardStateManager mKeyboardStateManager;
/**
* Returns event enum based on the two state transition information when swipe
@@ -554,6 +558,20 @@
+ "result page etc.")
LAUNCHER_ALLAPPS_SCROLLED(985),
+ @UiEvent(doc = "User scrolled up on one of the all apps surfaces such as A-Z list, search "
+ + "result page etc.")
+ LAUNCHER_ALLAPPS_SCROLLED_UP(1229),
+
+ @UiEvent(doc =
+ "User scrolled down on one of the all apps surfaces such as A-Z list, search "
+ + "result page etc.")
+ LAUNCHER_ALLAPPS_SCROLLED_DOWN(1230),
+
+ @UiEvent(doc = "User scrolled on one of the all apps surfaces such as A-Z list, search "
+ + "result page etc and we don't know the direction since user came back to "
+ + "original position from which they scrolled.")
+ LAUNCHER_ALLAPPS_SCROLLED_UNKNOWN_DIRECTION(1231),
+
@UiEvent(doc = "User tapped taskbar home button")
LAUNCHER_TASKBAR_HOME_BUTTON_TAP(1003),
@@ -592,6 +610,21 @@
@UiEvent(doc = "User tapped on Share app system shortcut.")
LAUNCHER_SYSTEM_SHORTCUT_APP_SHARE_TAP(1075),
+
+ @UiEvent(doc = "User has invoked split to right half from an app icon menu")
+ LAUNCHER_APP_ICON_MENU_SPLIT_RIGHT_BOTTOM(1199),
+
+ @UiEvent(doc = "User has invoked split to left half from an app icon menu")
+ LAUNCHER_APP_ICON_MENU_SPLIT_LEFT_TOP(1200),
+
+ @UiEvent(doc = "Number of apps in A-Z list (personal and work profile)")
+ LAUNCHER_ALLAPPS_COUNT(1225),
+
+ @UiEvent(doc = "User has invoked split to right half with a keyboard shortcut.")
+ LAUNCHER_KEYBOARD_SHORTCUT_SPLIT_RIGHT_BOTTOM(1232),
+
+ @UiEvent(doc = "User has invoked split to left half with a keyboard shortcut.")
+ LAUNCHER_KEYBOARD_SHORTCUT_SPLIT_LEFT_TOP(1233)
;
// ADD MORE
@@ -713,6 +746,13 @@
}
/**
+ * Sets cardinality of log message.
+ */
+ default StatsLogger withCardinality(int cardinality) {
+ return this;
+ }
+
+ /**
* Builds the final message and logs it as {@link EventEnum}.
*/
default void log(EventEnum event) {
@@ -737,8 +777,9 @@
HOT(2),
TIMEOUT(3),
FAIL(4),
- COLD_USERWAITING(5);
-
+ COLD_USERWAITING(5),
+ ATOMIC(6),
+ CONTROLLED(7);
private final int mId;
LatencyType(int id) {
@@ -748,7 +789,6 @@
public int getId() {
return mId;
}
-
}
/**
@@ -781,6 +821,13 @@
}
/**
+ * Sets sub event type.
+ */
+ default StatsLatencyLogger withSubEventType(int type) {
+ return this;
+ }
+
+ /**
* Sets packageId of log message.
*/
default StatsLatencyLogger withPackageId(int packageId) {
@@ -795,6 +842,77 @@
}
/**
+ * Helps to construct and log impression event.
+ */
+ public interface StatsImpressionLogger {
+
+ enum State {
+ UNKNOWN(0),
+ ALLAPPS(1),
+ SEARCHBOX_WIDGET(2);
+ private final int mLauncherState;
+
+ State(int id) {
+ this.mLauncherState = id;
+ }
+
+ public int getLauncherState() {
+ return mLauncherState;
+ }
+ }
+
+ /**
+ * Sets {@link InstanceId} of log message.
+ */
+ default StatsImpressionLogger withInstanceId(InstanceId instanceId) {
+ return this;
+ }
+
+ /**
+ * Sets {@link State} of impression event.
+ */
+ default StatsImpressionLogger withState(State state) {
+ return this;
+ }
+
+ /**
+ * Sets query length of the event.
+ */
+ default StatsImpressionLogger withQueryLength(int queryLength) {
+ return this;
+ }
+
+ /**
+ * Sets list of {@link com.android.app.search.ResultType} for the impression event.
+ */
+ default StatsImpressionLogger withResultType(IntArray resultType) {
+ return this;
+ }
+
+ /**
+ * Sets list of count for each of {@link com.android.app.search.ResultType} for the
+ * impression event.
+ */
+ default StatsImpressionLogger withResultCount(IntArray resultCount) {
+ return this;
+ }
+
+ /**
+ * Sets list of boolean for each of {@link com.android.app.search.ResultType} that indicates
+ * if this result is above keyboard or not for the impression event.
+ */
+ default StatsImpressionLogger withAboveKeyboard(List<Boolean> aboveKeyboard) {
+ return this;
+ }
+
+ /**
+ * Builds the final message and logs it as {@link EventEnum}.
+ */
+ default void log(EventEnum event) {
+ }
+ }
+
+ /**
* Returns new logger object.
*/
public StatsLogger logger() {
@@ -816,6 +934,27 @@
return logger;
}
+ /**
+ * Returns new impression logger object.
+ */
+ public StatsImpressionLogger impressionLogger() {
+ StatsImpressionLogger logger = createImpressionLogger();
+ if (mInstanceId != null) {
+ logger.withInstanceId(mInstanceId);
+ }
+ return logger;
+ }
+
+ /**
+ * Returns a singleton KeyboardStateManager.
+ */
+ public KeyboardStateManager keyboardStateManager() {
+ if (mKeyboardStateManager == null) {
+ mKeyboardStateManager = new KeyboardStateManager();
+ }
+ return mKeyboardStateManager;
+ }
+
protected StatsLogger createLogger() {
return new StatsLogger() {
};
@@ -826,6 +965,11 @@
};
}
+ protected StatsImpressionLogger createImpressionLogger() {
+ return new StatsImpressionLogger() {
+ };
+ }
+
/**
* Sets InstanceId to every new {@link StatsLogger} object returned by {@link #logger()} when
* not-null.
diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
index 4c0f1ae..0d978e1 100644
--- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
+++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
@@ -23,6 +23,9 @@
import android.util.Log;
import android.util.Pair;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel.CallbackTask;
import com.android.launcher3.LauncherSettings;
@@ -42,6 +45,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
/**
* Task to add auto-created workspace items.
@@ -50,14 +54,16 @@
private static final String LOG = "AddWorkspaceItemsTask";
+ @NonNull
private final List<Pair<ItemInfo, Object>> mItemList;
+ @NonNull
private final WorkspaceItemSpaceFinder mItemSpaceFinder;
/**
* @param itemList items to add on the workspace
*/
- public AddWorkspaceItemsTask(List<Pair<ItemInfo, Object>> itemList) {
+ public AddWorkspaceItemsTask(@NonNull final List<Pair<ItemInfo, Object>> itemList) {
this(itemList, new WorkspaceItemSpaceFinder());
}
@@ -65,14 +71,15 @@
* @param itemList items to add on the workspace
* @param itemSpaceFinder inject WorkspaceItemSpaceFinder dependency for testing
*/
- public AddWorkspaceItemsTask(List<Pair<ItemInfo, Object>> itemList,
- WorkspaceItemSpaceFinder itemSpaceFinder) {
+ public AddWorkspaceItemsTask(@NonNull final List<Pair<ItemInfo, Object>> itemList,
+ @NonNull final WorkspaceItemSpaceFinder itemSpaceFinder) {
mItemList = itemList;
mItemSpaceFinder = itemSpaceFinder;
}
@Override
- public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+ public void execute(@NonNull final LauncherAppState app, @NonNull final BgDataModel dataModel,
+ @NonNull final AllAppsList apps) {
if (mItemList.isEmpty()) {
return;
}
@@ -98,7 +105,8 @@
}
// b/139663018 Short-circuit this logic if the icon is a system app
- if (PackageManagerHelper.isSystemApp(app.getContext(), item.getIntent())) {
+ if (PackageManagerHelper.isSystemApp(app.getContext(),
+ Objects.requireNonNull(item.getIntent()))) {
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.MISSING_PROMISE_ICON,
LOG + " Item is a system app.");
@@ -159,7 +167,7 @@
continue;
}
- List<LauncherActivityInfo> activities = launcherApps
+ List<LauncherActivityInfo> activities = Objects.requireNonNull(launcherApps)
.getActivityList(packageName, item.user);
boolean hasActivity = activities != null && !activities.isEmpty();
@@ -218,7 +226,7 @@
if (!addedItemsFinal.isEmpty()) {
scheduleCallbackTask(new CallbackTask() {
@Override
- public void execute(Callbacks callbacks) {
+ public void execute(@NonNull Callbacks callbacks) {
final ArrayList<ItemInfo> addAnimated = new ArrayList<>();
final ArrayList<ItemInfo> addNotAnimated = new ArrayList<>();
if (!addedItemsFinal.isEmpty()) {
@@ -243,7 +251,8 @@
* Returns true if the shortcuts already exists on the workspace. This must be called after
* the workspace has been loaded. We identify a shortcut by its intent.
*/
- protected boolean shortcutExists(BgDataModel dataModel, Intent intent, UserHandle user) {
+ protected boolean shortcutExists(@NonNull final BgDataModel dataModel,
+ @Nullable final Intent intent, @NonNull final UserHandle user) {
final String compPkgName, intentWithPkg, intentWithoutPkg;
if (intent == null) {
// Skip items with null intents
diff --git a/src/com/android/launcher3/model/AllAppsList.java b/src/com/android/launcher3/model/AllAppsList.java
index 95150dc..6da948c 100644
--- a/src/com/android/launcher3/model/AllAppsList.java
+++ b/src/com/android/launcher3/model/AllAppsList.java
@@ -66,7 +66,10 @@
/** The list off all apps. */
public final ArrayList<AppInfo> data = new ArrayList<>(DEFAULT_APPLICATIONS_NUMBER);
+ @NonNull
private IconCache mIconCache;
+
+ @NonNull
private AppFilter mAppFilter;
private boolean mDataChanged = false;
diff --git a/src/com/android/launcher3/model/BaseModelUpdateTask.java b/src/com/android/launcher3/model/BaseModelUpdateTask.java
index 2a6a691..5b6f9f6 100644
--- a/src/com/android/launcher3/model/BaseModelUpdateTask.java
+++ b/src/com/android/launcher3/model/BaseModelUpdateTask.java
@@ -17,6 +17,7 @@
import android.util.Log;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.launcher3.LauncherAppState;
@@ -47,14 +48,17 @@
private static final boolean DEBUG_TASKS = false;
private static final String TAG = "BaseModelUpdateTask";
+ // Nullabilities are explicitly omitted here because these are late-init fields,
+ // They will be non-null after init(), which is always the case in enqueueModelUpdateTask().
private LauncherAppState mApp;
private LauncherModel mModel;
private BgDataModel mDataModel;
private AllAppsList mAllAppsList;
private Executor mUiExecutor;
- public void init(LauncherAppState app, LauncherModel model,
- BgDataModel dataModel, AllAppsList allAppsList, Executor uiExecutor) {
+ public void init(@NonNull final LauncherAppState app, @NonNull final LauncherModel model,
+ @NonNull final BgDataModel dataModel, @NonNull final AllAppsList allAppsList,
+ @NonNull final Executor uiExecutor) {
mApp = app;
mModel = model;
mDataModel = dataModel;
@@ -64,7 +68,7 @@
@Override
public final void run() {
- if (!mModel.isModelLoaded()) {
+ if (!Objects.requireNonNull(mModel).isModelLoaded()) {
if (DEBUG_TASKS) {
Log.d(TAG, "Ignoring model task since loader is pending=" + this);
}
@@ -77,13 +81,13 @@
/**
* Execute the actual task. Called on the worker thread.
*/
- public abstract void execute(
- LauncherAppState app, BgDataModel dataModel, AllAppsList apps);
+ public abstract void execute(@NonNull LauncherAppState app,
+ @NonNull BgDataModel dataModel, @NonNull AllAppsList apps);
/**
* Schedules a {@param task} to be executed on the current callbacks.
*/
- public final void scheduleCallbackTask(final CallbackTask task) {
+ public final void scheduleCallbackTask(@NonNull final CallbackTask task) {
for (final Callbacks cb : mModel.getCallbacks()) {
mUiExecutor.execute(() -> task.execute(cb));
}
@@ -95,7 +99,7 @@
return mModel.getWriter(false /* hasVerticalHotseat */, false /* verifyChanges */, null);
}
- public void bindUpdatedWorkspaceItems(List<WorkspaceItemInfo> allUpdates) {
+ public void bindUpdatedWorkspaceItems(@NonNull final List<WorkspaceItemInfo> allUpdates) {
// Bind workspace items
List<WorkspaceItemInfo> workspaceUpdates = allUpdates.stream()
.filter(info -> info.id != ItemInfo.NO_ID)
@@ -113,18 +117,18 @@
.forEach(this::bindExtraContainerItems);
}
- public void bindExtraContainerItems(FixedContainerItems item) {
+ public void bindExtraContainerItems(@NonNull final FixedContainerItems item) {
FixedContainerItems copy = item.clone();
scheduleCallbackTask(c -> c.bindExtraContainerItems(copy));
}
- public void bindDeepShortcuts(BgDataModel dataModel) {
+ public void bindDeepShortcuts(@NonNull final BgDataModel dataModel) {
final HashMap<ComponentKey, Integer> shortcutMapCopy =
new HashMap<>(dataModel.deepShortcutMap);
scheduleCallbackTask(callbacks -> callbacks.bindDeepShortcutMap(shortcutMapCopy));
}
- public void bindUpdatedWidgets(BgDataModel dataModel) {
+ public void bindUpdatedWidgets(@NonNull final BgDataModel dataModel) {
final ArrayList<WidgetsListBaseEntry> widgets =
dataModel.widgetsModel.getWidgetsListForPicker(mApp.getContext());
scheduleCallbackTask(c -> c.bindAllWidgets(widgets));
diff --git a/src/com/android/launcher3/model/CacheDataUpdatedTask.java b/src/com/android/launcher3/model/CacheDataUpdatedTask.java
index f644d49..57fefaa 100644
--- a/src/com/android/launcher3/model/CacheDataUpdatedTask.java
+++ b/src/com/android/launcher3/model/CacheDataUpdatedTask.java
@@ -18,6 +18,8 @@
import android.content.ComponentName;
import android.os.UserHandle;
+import androidx.annotation.NonNull;
+
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.icons.IconCache;
@@ -35,17 +37,23 @@
public static final int OP_SESSION_UPDATE = 2;
private final int mOp;
+
+ @NonNull
private final UserHandle mUser;
+
+ @NonNull
private final HashSet<String> mPackages;
- public CacheDataUpdatedTask(int op, UserHandle user, HashSet<String> packages) {
+ public CacheDataUpdatedTask(final int op, @NonNull final UserHandle user,
+ @NonNull final HashSet<String> packages) {
mOp = op;
mUser = user;
mPackages = packages;
}
@Override
- public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+ public void execute(@NonNull final LauncherAppState app, @NonNull final BgDataModel dataModel,
+ @NonNull final AllAppsList apps) {
IconCache iconCache = app.getIconCache();
ArrayList<WorkspaceItemInfo> updatedShortcuts = new ArrayList<>();
@@ -65,7 +73,7 @@
bindApplicationsIfNeeded();
}
- public boolean isValidShortcut(WorkspaceItemInfo si) {
+ public boolean isValidShortcut(@NonNull final WorkspaceItemInfo si) {
switch (mOp) {
case OP_CACHE_UPDATE:
return true;
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
index c25929a..341372e 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
@@ -31,6 +31,7 @@
import android.util.ArrayMap;
import android.util.Log;
+import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import com.android.launcher3.InvariantDeviceProfile;
@@ -47,6 +48,7 @@
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.android.launcher3.widget.WidgetManagerHelper;
+import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -776,17 +778,6 @@
values.put(LauncherSettings.Favorites.SPANY, spanY);
}
- /**
- * This method should return an id that should be the same for two folders containing the
- * same elements.
- */
- private String getFolderMigrationId() {
- return mFolderItems.keySet().stream()
- .map(intentString -> mFolderItems.get(intentString).size() + intentString)
- .sorted()
- .collect(Collectors.joining(","));
- }
-
/** This id is not used in the DB is only used while doing the migration and it identifies
* an entry on each workspace. For example two calculator icons would have the same
* migration id even thought they have different database ids.
@@ -797,9 +788,47 @@
return getFolderMigrationId();
case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
return mProvider;
+ case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
+ final String intentStr = cleanIntentString(mIntent);
+ try {
+ Intent i = Intent.parseUri(intentStr, 0);
+ return Objects.requireNonNull(i.getComponent()).toString();
+ } catch (Exception e) {
+ return intentStr;
+ }
default:
- return mIntent;
+ return cleanIntentString(mIntent);
}
}
+
+ /**
+ * This method should return an id that should be the same for two folders containing the
+ * same elements.
+ */
+ @NonNull
+ private String getFolderMigrationId() {
+ return mFolderItems.keySet().stream()
+ .map(intentString -> mFolderItems.get(intentString).size()
+ + cleanIntentString(intentString))
+ .sorted()
+ .collect(Collectors.joining(","));
+ }
+
+ /**
+ * This is needed because sourceBounds can change and make the id of two equal items
+ * different.
+ */
+ @NonNull
+ private String cleanIntentString(@NonNull String intentStr) {
+ try {
+ Intent i = Intent.parseUri(intentStr, 0);
+ i.setSourceBounds(null);
+ return i.toURI();
+ } catch (URISyntaxException e) {
+ Log.e(TAG, "Unable to parse Intent string", e);
+ return intentStr;
+ }
+
+ }
}
}
diff --git a/src/com/android/launcher3/model/ItemInstallQueue.java b/src/com/android/launcher3/model/ItemInstallQueue.java
index 229bb2d..69f9b53 100644
--- a/src/com/android/launcher3/model/ItemInstallQueue.java
+++ b/src/com/android/launcher3/model/ItemInstallQueue.java
@@ -288,6 +288,7 @@
}
@Override
+ @Nullable
public Intent getIntent() {
return intent;
}
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index 87e8ebf..6c62b31 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -465,7 +465,7 @@
// occupied (if the feature is enabled) in order to account for the search
// container.
int spanX = mIDP.numSearchContainerColumns;
- int spanY = FeatureFlags.EXPANDED_SMARTSPACE.get() ? 2 : 1;
+ int spanY = 1;
screen.markCells(0, 0, spanX, spanY, true);
}
occupied.put(item.screenId, screen);
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index f1c5d59..b644b6b 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -256,12 +256,10 @@
mApp.getModel()::onPackageIconsUpdated);
logASplit(logger, "update icon cache");
- if (FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) {
- verifyNotStopped();
- logASplit(logger, "save shortcuts in icon cache");
- updateHandler.updateIcons(allShortcuts, new ShortcutCachingLogic(),
- mApp.getModel()::onPackageIconsUpdated);
- }
+ verifyNotStopped();
+ logASplit(logger, "save shortcuts in icon cache");
+ updateHandler.updateIcons(allShortcuts, new ShortcutCachingLogic(),
+ mApp.getModel()::onPackageIconsUpdated);
// Take a break
waitForIdle();
@@ -276,12 +274,10 @@
mResults.bindDeepShortcuts();
logASplit(logger, "bindDeepShortcuts");
- if (FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) {
- verifyNotStopped();
- logASplit(logger, "save deep shortcuts in icon cache");
- updateHandler.updateIcons(allDeepShortcuts,
- new ShortcutCachingLogic(), (pkgs, user) -> { });
- }
+ verifyNotStopped();
+ logASplit(logger, "save deep shortcuts in icon cache");
+ updateHandler.updateIcons(allDeepShortcuts,
+ new ShortcutCachingLogic(), (pkgs, user) -> { });
// Take a break
waitForIdle();
@@ -304,9 +300,7 @@
logASplit(logger, "save widgets in icon cache");
// fifth step
- if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
- loadFolderNames();
- }
+ loadFolderNames();
verifyNotStopped();
updateHandler.finish();
diff --git a/src/com/android/launcher3/model/ModelUtils.java b/src/com/android/launcher3/model/ModelUtils.java
index 1ced0b1..c21fc38 100644
--- a/src/com/android/launcher3/model/ModelUtils.java
+++ b/src/com/android/launcher3/model/ModelUtils.java
@@ -15,8 +15,6 @@
*/
package com.android.launcher3.model;
-import static com.android.launcher3.Utilities.isValidExtraType;
-
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
@@ -149,4 +147,12 @@
info.intent = launchIntent;
return info;
}
+
+ /**
+ * @return true if the extra is either null or is of type {@param type}
+ */
+ private static boolean isValidExtraType(Intent intent, String key, Class type) {
+ Object extra = intent.getParcelableExtra(key);
+ return extra == null || type.isInstance(extra);
+ }
}
diff --git a/src/com/android/launcher3/model/ModelWriter.java b/src/com/android/launcher3/model/ModelWriter.java
index 0a68d4a..f444bd5 100644
--- a/src/com/android/launcher3/model/ModelWriter.java
+++ b/src/com/android/launcher3/model/ModelWriter.java
@@ -48,7 +48,7 @@
import com.android.launcher3.util.Executors;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.LooperExecutor;
-import com.android.launcher3.widget.LauncherAppWidgetHost;
+import com.android.launcher3.widget.LauncherWidgetHolder;
import java.util.ArrayList;
import java.util.Arrays;
@@ -333,13 +333,13 @@
/**
* Deletes the widget info and the widget id.
*/
- public void deleteWidgetInfo(final LauncherAppWidgetInfo info, LauncherAppWidgetHost host,
+ public void deleteWidgetInfo(final LauncherAppWidgetInfo info, LauncherWidgetHolder holder,
@Nullable final String reason) {
notifyDelete(Collections.singleton(info));
- if (host != null && !info.isCustomWidget() && info.isWidgetIdAllocated()) {
+ 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(() -> host.deleteAppWidgetId(info.appWidgetId));
+ enqueueDeleteRunnable(() -> holder.deleteAppWidgetId(info.appWidgetId));
}
deleteItemFromDatabase(info, reason);
}
diff --git a/src/com/android/launcher3/model/PackageIncrementalDownloadUpdatedTask.java b/src/com/android/launcher3/model/PackageIncrementalDownloadUpdatedTask.java
index c0dc34a..b9fba9d 100644
--- a/src/com/android/launcher3/model/PackageIncrementalDownloadUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageIncrementalDownloadUpdatedTask.java
@@ -17,6 +17,8 @@
import android.os.UserHandle;
+import androidx.annotation.NonNull;
+
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
@@ -31,19 +33,24 @@
*/
public class PackageIncrementalDownloadUpdatedTask extends BaseModelUpdateTask {
+ @NonNull
private final UserHandle mUser;
+
private final int mProgress;
+
+ @NonNull
private final String mPackageName;
- public PackageIncrementalDownloadUpdatedTask(
- String packageName, UserHandle user, float progress) {
+ public PackageIncrementalDownloadUpdatedTask(@NonNull final String packageName,
+ @NonNull final UserHandle user, final float progress) {
mUser = user;
mProgress = 1 - progress > 0.001 ? (int) (100 * progress) : 100;
mPackageName = packageName;
}
@Override
- public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList appsList) {
+ public void execute(@NonNull LauncherAppState app, @NonNull final BgDataModel dataModel,
+ @NonNull final AllAppsList appsList) {
PackageInstallInfo downloadInfo = new PackageInstallInfo(
mPackageName,
PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING,
diff --git a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
index b74d0fc..76a87ed 100644
--- a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
+++ b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
@@ -18,6 +18,8 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import androidx.annotation.NonNull;
+
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfo;
@@ -33,14 +35,16 @@
*/
public class PackageInstallStateChangedTask extends BaseModelUpdateTask {
+ @NonNull
private final PackageInstallInfo mInstallInfo;
- public PackageInstallStateChangedTask(PackageInstallInfo installInfo) {
+ public PackageInstallStateChangedTask(@NonNull final PackageInstallInfo installInfo) {
mInstallInfo = installInfo;
}
@Override
- public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+ public void execute(@NonNull final LauncherAppState app, @NonNull final BgDataModel dataModel,
+ @NonNull final AllAppsList apps) {
if (mInstallInfo.state == PackageInstallInfo.STATUS_INSTALLED) {
try {
// For instant apps we do not get package-add. Use setting events to update
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index a9d272e..3d9d81f 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -29,6 +29,8 @@
import android.os.UserManager;
import android.util.Log;
+import androidx.annotation.NonNull;
+
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
@@ -80,17 +82,23 @@
public static final int OP_USER_AVAILABILITY_CHANGE = 7; // user available/unavailable
private final int mOp;
+
+ @NonNull
private final UserHandle mUser;
+
+ @NonNull
private final String[] mPackages;
- public PackageUpdatedTask(int op, UserHandle user, String... packages) {
+ public PackageUpdatedTask(final int op, @NonNull final UserHandle user,
+ @NonNull final String... packages) {
mOp = op;
mUser = user;
mPackages = packages;
}
@Override
- public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList appsList) {
+ public void execute(@NonNull final LauncherAppState app, @NonNull final BgDataModel dataModel,
+ @NonNull final AllAppsList appsList) {
final Context context = app.getContext();
final IconCache iconCache = app.getIconCache();
diff --git a/src/com/android/launcher3/model/ReloadStringCacheTask.java b/src/com/android/launcher3/model/ReloadStringCacheTask.java
index f4d4298..34f7057 100644
--- a/src/com/android/launcher3/model/ReloadStringCacheTask.java
+++ b/src/com/android/launcher3/model/ReloadStringCacheTask.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.model;
+import androidx.annotation.NonNull;
+
import com.android.launcher3.LauncherAppState;
/**
@@ -22,14 +24,17 @@
* {@link android.app.admin.DevicePolicyManager#ACTION_DEVICE_POLICY_RESOURCE_UPDATED}.
*/
public class ReloadStringCacheTask extends BaseModelUpdateTask {
+
+ @NonNull
private ModelDelegate mModelDelegate;
- public ReloadStringCacheTask(ModelDelegate modelDelegate) {
+ public ReloadStringCacheTask(@NonNull final ModelDelegate modelDelegate) {
mModelDelegate = modelDelegate;
}
@Override
- public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList appsList) {
+ public void execute(@NonNull final LauncherAppState app, @NonNull final BgDataModel dataModel,
+ @NonNull final AllAppsList appsList) {
synchronized (dataModel) {
mModelDelegate.loadStringCache(dataModel.stringCache);
StringCache cloneSC = dataModel.stringCache.clone();
diff --git a/src/com/android/launcher3/model/ShortcutsChangedTask.java b/src/com/android/launcher3/model/ShortcutsChangedTask.java
index 1026e0b..a6a04a7 100644
--- a/src/com/android/launcher3/model/ShortcutsChangedTask.java
+++ b/src/com/android/launcher3/model/ShortcutsChangedTask.java
@@ -19,6 +19,8 @@
import android.content.pm.ShortcutInfo;
import android.os.UserHandle;
+import androidx.annotation.NonNull;
+
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -38,13 +40,20 @@
*/
public class ShortcutsChangedTask extends BaseModelUpdateTask {
+ @NonNull
private final String mPackageName;
+
+ @NonNull
private final List<ShortcutInfo> mShortcuts;
+
+ @NonNull
private final UserHandle mUser;
+
private final boolean mUpdateIdMap;
- public ShortcutsChangedTask(String packageName, List<ShortcutInfo> shortcuts,
- UserHandle user, boolean updateIdMap) {
+ public ShortcutsChangedTask(@NonNull final String packageName,
+ @NonNull final List<ShortcutInfo> shortcuts, @NonNull final UserHandle user,
+ final boolean updateIdMap) {
mPackageName = packageName;
mShortcuts = shortcuts;
mUser = user;
@@ -52,7 +61,8 @@
}
@Override
- public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+ public void execute(@NonNull final LauncherAppState app, @NonNull final BgDataModel dataModel,
+ @NonNull final AllAppsList apps) {
final Context context = app.getContext();
// Find WorkspaceItemInfo's that have changed on the workspace.
ArrayList<WorkspaceItemInfo> matchingWorkspaceItems = new ArrayList<>();
diff --git a/src/com/android/launcher3/model/UserLockStateChangedTask.java b/src/com/android/launcher3/model/UserLockStateChangedTask.java
index 1565b19..63ca35b 100644
--- a/src/com/android/launcher3/model/UserLockStateChangedTask.java
+++ b/src/com/android/launcher3/model/UserLockStateChangedTask.java
@@ -21,6 +21,8 @@
import android.content.pm.ShortcutInfo;
import android.os.UserHandle;
+import androidx.annotation.NonNull;
+
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -40,16 +42,18 @@
*/
public class UserLockStateChangedTask extends BaseModelUpdateTask {
+ @NonNull
private final UserHandle mUser;
private boolean mIsUserUnlocked;
- public UserLockStateChangedTask(UserHandle user, boolean isUserUnlocked) {
+ public UserLockStateChangedTask(@NonNull final UserHandle user, final boolean isUserUnlocked) {
mUser = user;
mIsUserUnlocked = isUserUnlocked;
}
@Override
- public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+ public void execute(@NonNull final LauncherAppState app, @NonNull final BgDataModel dataModel,
+ @NonNull final AllAppsList apps) {
Context context = app.getContext();
HashMap<ShortcutKey, ShortcutInfo> pinnedShortcuts = new HashMap<>();
diff --git a/src/com/android/launcher3/model/data/AppInfo.java b/src/com/android/launcher3/model/data/AppInfo.java
index 5b2bcf5..34972e7 100644
--- a/src/com/android/launcher3/model/data/AppInfo.java
+++ b/src/com/android/launcher3/model/data/AppInfo.java
@@ -55,6 +55,7 @@
*/
public Intent intent;
+ @NonNull
public ComponentName componentName;
// Section name used for indexing.
@@ -65,6 +66,7 @@
}
@Override
+ @Nullable
public Intent getIntent() {
return intent;
}
@@ -151,7 +153,7 @@
| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
}
- @Nullable
+ @NonNull
@Override
public ComponentName getTargetComponent() {
return componentName;
diff --git a/src/com/android/launcher3/model/data/FolderInfo.java b/src/com/android/launcher3/model/data/FolderInfo.java
index efebce3..524b769 100644
--- a/src/com/android/launcher3/model/data/FolderInfo.java
+++ b/src/com/android/launcher3/model/data/FolderInfo.java
@@ -26,11 +26,11 @@
import android.os.Process;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.FolderNameInfos;
import com.android.launcher3.logger.LauncherAtom;
import com.android.launcher3.logger.LauncherAtom.Attribute;
@@ -155,7 +155,7 @@
}
@Override
- public void onAddToDatabase(ContentWriter writer) {
+ public void onAddToDatabase(@NonNull ContentWriter writer) {
super.onAddToDatabase(writer);
writer.put(LauncherSettings.Favorites.TITLE, title)
.put(LauncherSettings.Favorites.OPTIONS, options);
@@ -207,8 +207,9 @@
return String.format("%s; labelState=%s", super.dumpProperties(), getLabelState());
}
+ @NonNull
@Override
- public LauncherAtom.ItemInfo buildProto(FolderInfo fInfo) {
+ public LauncherAtom.ItemInfo buildProto(@Nullable FolderInfo fInfo) {
FolderIcon.Builder folderIcon = FolderIcon.newBuilder()
.setCardinality(contents.size());
if (LabelState.SUGGESTED.equals(getLabelState())) {
@@ -262,6 +263,7 @@
: LabelState.SUGGESTED;
}
+ @NonNull
@Override
public ItemInfo makeShallowCopy() {
FolderInfo folderInfo = new FolderInfo();
@@ -273,6 +275,7 @@
/**
* Returns {@link LauncherAtom.FolderIcon} wrapped as {@link LauncherAtom.ItemInfo} for logging.
*/
+ @NonNull
@Override
public LauncherAtom.ItemInfo buildProto() {
return buildProto(null);
@@ -321,12 +324,6 @@
return LauncherAtom.ToState.TO_STATE_UNSPECIFIED;
}
- if (!FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
- return title.length() > 0
- ? LauncherAtom.ToState.TO_CUSTOM_WITH_SUGGESTIONS_DISABLED
- : LauncherAtom.ToState.TO_EMPTY_WITH_SUGGESTIONS_DISABLED;
- }
-
// TODO: if suggestedFolderNames is null then it infrastructure issue, not
// ranking issue. We should log these appropriately.
if (suggestedFolderNames == null || !suggestedFolderNames.hasSuggestions()) {
diff --git a/src/com/android/launcher3/model/data/ItemInfo.java b/src/com/android/launcher3/model/data/ItemInfo.java
index 1e8e3ca..159af60 100644
--- a/src/com/android/launcher3/model/data/ItemInfo.java
+++ b/src/com/android/launcher3/model/data/ItemInfo.java
@@ -39,9 +39,12 @@
import android.content.ComponentName;
import android.content.ContentValues;
import android.content.Intent;
+import android.net.Uri;
import android.os.Process;
import android.os.UserHandle;
+import android.provider.Settings;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.launcher3.LauncherSettings;
@@ -60,6 +63,7 @@
import com.android.launcher3.logger.LauncherAtomExtensions.ExtendedContainers;
import com.android.launcher3.model.ModelWriter;
import com.android.launcher3.util.ContentWriter;
+import com.android.launcher3.util.SettingsCache;
import java.util.Optional;
@@ -73,6 +77,9 @@
// An id that doesn't match any item, including predicted apps with have an id=NO_ID
public static final int NO_MATCHING_ID = Integer.MIN_VALUE;
+ /** Hidden field Settings.Secure.NAV_BAR_KIDS_MODE */
+ private static final Uri NAV_BAR_KIDS_MODE = Settings.Secure.getUriFor("nav_bar_kids_mode");
+
/**
* The id in the settings database for this item
*/
@@ -141,30 +148,34 @@
/**
* Title of the item
*/
+ @Nullable
public CharSequence title;
/**
* Content description of the item.
*/
+ @Nullable
public CharSequence contentDescription;
/**
* When the instance is created using {@link #copyFrom}, this field is used to keep track of
* original {@link ComponentName}.
*/
+ @Nullable
private ComponentName mComponentName;
+ @NonNull
public UserHandle user;
public ItemInfo() {
user = Process.myUserHandle();
}
- protected ItemInfo(ItemInfo info) {
+ protected ItemInfo(@NonNull final ItemInfo info) {
copyFrom(info);
}
- public void copyFrom(ItemInfo info) {
+ public void copyFrom(@NonNull final ItemInfo info) {
id = info.id;
title = info.title;
cellX = info.cellX;
@@ -182,6 +193,7 @@
mComponentName = info.getTargetComponent();
}
+ @Nullable
public Intent getIntent() {
return null;
}
@@ -209,7 +221,7 @@
: null;
}
- public void writeToValues(ContentWriter writer) {
+ public void writeToValues(@NonNull final ContentWriter writer) {
writer.put(LauncherSettings.Favorites.ITEM_TYPE, itemType)
.put(LauncherSettings.Favorites.CONTAINER, container)
.put(LauncherSettings.Favorites.SCREEN, screenId)
@@ -220,7 +232,7 @@
.put(LauncherSettings.Favorites.RANK, rank);
}
- public void readFromValues(ContentValues values) {
+ public void readFromValues(@NonNull final ContentValues values) {
itemType = values.getAsInteger(LauncherSettings.Favorites.ITEM_TYPE);
container = values.getAsInteger(LauncherSettings.Favorites.CONTAINER);
screenId = values.getAsInteger(LauncherSettings.Favorites.SCREEN);
@@ -234,7 +246,7 @@
/**
* Write the fields of this item to the DB
*/
- public void onAddToDatabase(ContentWriter writer) {
+ public void onAddToDatabase(@NonNull final ContentWriter writer) {
if (Workspace.EXTRA_EMPTY_SCREEN_IDS.contains(screenId)) {
// We should never persist an item on the extra empty screen.
throw new RuntimeException("Screen id should not be extra empty screen: " + screenId);
@@ -245,10 +257,12 @@
}
@Override
+ @NonNull
public final String toString() {
return getClass().getSimpleName() + "(" + dumpProperties() + ")";
}
+ @NonNull
protected String dumpProperties() {
return "id=" + id
+ " type=" + LauncherSettings.Favorites.itemTypeToString(itemType)
@@ -288,14 +302,17 @@
/**
* Creates {@link LauncherAtom.ItemInfo} with important fields and parent container info.
*/
+ @NonNull
public LauncherAtom.ItemInfo buildProto() {
return buildProto(null);
}
/**
* Creates {@link LauncherAtom.ItemInfo} with important fields and parent container info.
+ * @param fInfo
*/
- public LauncherAtom.ItemInfo buildProto(FolderInfo fInfo) {
+ @NonNull
+ public LauncherAtom.ItemInfo buildProto(@Nullable final FolderInfo fInfo) {
LauncherAtom.ItemInfo.Builder itemBuilder = getDefaultItemInfoBuilder();
Optional<ComponentName> nullableComponent = Optional.ofNullable(getTargetComponent());
switch (itemType) {
@@ -339,9 +356,11 @@
break;
case ITEM_TYPE_TASK:
itemBuilder
- .setTask(LauncherAtom.Task.newBuilder()
- .setComponentName(getTargetComponent().flattenToShortString())
- .setIndex(screenId));
+ .setTask(nullableComponent
+ .map(component -> LauncherAtom.Task.newBuilder()
+ .setComponentName(component.flattenToShortString())
+ .setIndex(screenId))
+ .orElse(LauncherAtom.Task.newBuilder()));
break;
default:
break;
@@ -373,9 +392,13 @@
return itemBuilder.build();
}
+ @NonNull
protected LauncherAtom.ItemInfo.Builder getDefaultItemInfoBuilder() {
LauncherAtom.ItemInfo.Builder itemBuilder = LauncherAtom.ItemInfo.newBuilder();
itemBuilder.setIsWork(!Process.myUserHandle().equals(user));
+ SettingsCache settingsCache = SettingsCache.INSTANCE.getNoCreate();
+ boolean isKidsMode = settingsCache != null && settingsCache.getValue(NAV_BAR_KIDS_MODE, 0);
+ itemBuilder.setIsKidsMode(isKidsMode);
itemBuilder.setRank(rank);
return itemBuilder;
}
@@ -383,6 +406,7 @@
/**
* Returns {@link ContainerInfo} used when logging this item.
*/
+ @NonNull
public ContainerInfo getContainerInfo() {
switch (container) {
case CONTAINER_HOTSEAT:
@@ -447,6 +471,7 @@
* Returns non-AOSP container wrapped by {@link ExtendedContainers} object. Should be overridden
* by build variants.
*/
+ @NonNull
protected ExtendedContainers getExtendedContainer() {
return ExtendedContainers.getDefaultInstance();
}
@@ -454,6 +479,7 @@
/**
* Returns shallow copy of the object.
*/
+ @NonNull
public ItemInfo makeShallowCopy() {
ItemInfo itemInfo = new ItemInfo();
itemInfo.copyFrom(this);
@@ -463,7 +489,8 @@
/**
* Sets the title of the item and writes to DB model if needed.
*/
- public void setTitle(CharSequence title, ModelWriter modelWriter) {
+ public void setTitle(@Nullable final CharSequence title,
+ @Nullable final ModelWriter modelWriter) {
this.title = title;
}
}
diff --git a/src/com/android/launcher3/model/data/ItemInfoWithIcon.java b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
index 76a0c4d..e5fb015 100644
--- a/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
+++ b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
@@ -16,7 +16,6 @@
package com.android.launcher3.model.data;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -220,10 +219,10 @@
/** Creates an intent to that launches the app store at this app's page. */
@Nullable
public Intent getMarketIntent(Context context) {
- ComponentName componentName = getTargetComponent();
+ String targetPackage = getTargetPackage();
- return componentName != null
- ? new PackageManagerHelper(context).getMarketIntent(componentName.getPackageName())
+ return targetPackage != null
+ ? new PackageManagerHelper(context).getMarketIntent(targetPackage)
: null;
}
diff --git a/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java b/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java
index e57a895..1fbe04f 100644
--- a/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java
+++ b/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java
@@ -29,6 +29,7 @@
import android.content.res.Resources;
import android.os.Process;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.launcher3.Launcher;
@@ -191,7 +192,7 @@
}
@Override
- public void onAddToDatabase(ContentWriter writer) {
+ public void onAddToDatabase(@NonNull ContentWriter writer) {
super.onAddToDatabase(writer);
writer.put(LauncherSettings.Favorites.APPWIDGET_ID, appWidgetId)
.put(LauncherSettings.Favorites.APPWIDGET_PROVIDER, providerName.flattenToString())
@@ -283,8 +284,9 @@
}
}
+ @NonNull
@Override
- public LauncherAtom.ItemInfo buildProto(FolderInfo folderInfo) {
+ public LauncherAtom.ItemInfo buildProto(@Nullable FolderInfo folderInfo) {
LauncherAtom.ItemInfo info = super.buildProto(folderInfo);
return info.toBuilder()
.setWidget(info.getWidget().toBuilder().setWidgetFeatures(widgetFeatures))
diff --git a/src/com/android/launcher3/model/data/SearchActionItemInfo.java b/src/com/android/launcher3/model/data/SearchActionItemInfo.java
index e879313..04042ea 100644
--- a/src/com/android/launcher3/model/data/SearchActionItemInfo.java
+++ b/src/com/android/launcher3/model/data/SearchActionItemInfo.java
@@ -24,6 +24,7 @@
import android.os.Process;
import android.os.UserHandle;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.launcher3.LauncherAppState;
@@ -70,7 +71,7 @@
}
@Override
- public void copyFrom(com.android.launcher3.model.data.ItemInfo info) {
+ public void copyFrom(@NonNull com.android.launcher3.model.data.ItemInfo info) {
super.copyFrom(info);
SearchActionItemInfo itemInfo = (SearchActionItemInfo) info;
this.mFallbackPackageName = itemInfo.mFallbackPackageName;
@@ -91,6 +92,7 @@
}
@Override
+ @Nullable
public Intent getIntent() {
return mIntent;
}
@@ -131,8 +133,9 @@
return new SearchActionItemInfo(this);
}
+ @NonNull
@Override
- public ItemInfo buildProto(FolderInfo fInfo) {
+ public ItemInfo buildProto(@Nullable FolderInfo fInfo) {
SearchActionItem.Builder itemBuilder = SearchActionItem.newBuilder()
.setPackageName(mFallbackPackageName);
diff --git a/src/com/android/launcher3/model/data/WorkspaceItemInfo.java b/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
index 2b3da33..59ef320 100644
--- a/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
+++ b/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
@@ -75,6 +75,7 @@
/**
* The intent used to start the application.
*/
+ @NonNull
public Intent intent;
/**
@@ -130,7 +131,7 @@
}
@Override
- public void onAddToDatabase(ContentWriter writer) {
+ public void onAddToDatabase(@NonNull ContentWriter writer) {
super.onAddToDatabase(writer);
writer.put(Favorites.TITLE, title)
.put(Favorites.INTENT, getIntent())
@@ -147,6 +148,7 @@
}
@Override
+ @NonNull
public Intent getIntent() {
return intent;
}
@@ -164,7 +166,8 @@
return isPromise() && !hasStatusFlag(FLAG_SUPPORTS_WEB_UI);
}
- public void updateFromDeepShortcutInfo(ShortcutInfo shortcutInfo, Context context) {
+ public void updateFromDeepShortcutInfo(@NonNull final ShortcutInfo shortcutInfo,
+ @NonNull final Context context) {
// {@link ShortcutInfo#getActivity} can change during an update. Recreate the intent
intent = ShortcutKey.makeIntent(shortcutInfo);
title = shortcutInfo.getShortLabel();
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
index 29eefe2..e9b6606 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
@@ -16,6 +16,9 @@
package com.android.launcher3.pageindicators;
+import static com.android.launcher3.config.FeatureFlags.SHOW_DELIGHTFUL_PAGINATION;
+import static com.android.launcher3.config.FeatureFlags.SHOW_DOT_PAGINATION;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
@@ -27,77 +30,120 @@
import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.Paint.Style;
+import android.graphics.Rect;
import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Looper;
import android.util.AttributeSet;
-import android.util.Property;
+import android.util.FloatProperty;
import android.view.View;
+import android.view.ViewConfiguration;
import android.view.ViewOutlineProvider;
import android.view.animation.Interpolator;
import android.view.animation.OvershootInterpolator;
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.Insettable;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.util.Themes;
/**
* {@link PageIndicator} which shows dots per page. The active page is shown with the current
* accent color.
*/
-public class PageIndicatorDots extends View implements PageIndicator {
+public class PageIndicatorDots extends View implements Insettable, PageIndicator {
private static final float SHIFT_PER_ANIMATION = 0.5f;
private static final float SHIFT_THRESHOLD = 0.1f;
private static final long ANIMATION_DURATION = 150;
+ private static final int PAGINATION_FADE_DELAY = ViewConfiguration.getScrollDefaultDelay();
+ private static final int ALPHA_ANIMATE_DURATION = ViewConfiguration.getScrollBarFadeDuration();
private static final int ENTER_ANIMATION_START_DELAY = 300;
private static final int ENTER_ANIMATION_STAGGERED_DELAY = 150;
private static final int ENTER_ANIMATION_DURATION = 400;
- private static final int DOT_ACTIVE_ALPHA = 255;
- private static final int DOT_INACTIVE_ALPHA = 128;
+ private static final int PAGE_INDICATOR_ALPHA = 255;
+ private static final int DOT_ALPHA = 128;
+ private static final int DOT_GAP_FACTOR = 3;
+ private static final float DOT_GAP_FACTOR_FLOAT = 3.8f;
+ private static final int VISIBLE_ALPHA = 1;
+ private static final int INVISIBLE_ALPHA = 0;
+ private Paint mPaginationPaint;
// This value approximately overshoots to 1.5 times the original size.
private static final float ENTER_ANIMATION_OVERSHOOT_TENSION = 4.9f;
+ private static final float INDICATOR_ROTATION = 180f;
+
private static final RectF sTempRect = new RectF();
- private static final Property<PageIndicatorDots, Float> CURRENT_POSITION
- = new Property<PageIndicatorDots, Float>(float.class, "current_position") {
- @Override
- public Float get(PageIndicatorDots obj) {
- return obj.mCurrentPosition;
- }
+ private static final FloatProperty<PageIndicatorDots> CURRENT_POSITION =
+ new FloatProperty<PageIndicatorDots>("current_position") {
+ @Override
+ public Float get(PageIndicatorDots obj) {
+ return obj.mCurrentPosition;
+ }
- @Override
- public void set(PageIndicatorDots obj, Float pos) {
- obj.mCurrentPosition = pos;
- obj.invalidate();
- obj.invalidateOutline();
- }
- };
+ @Override
+ public void setValue(PageIndicatorDots obj, float pos) {
+ obj.mCurrentPosition = pos;
+ obj.invalidate();
+ obj.invalidateOutline();
+ }
+ };
- private final Paint mCirclePaint;
+ private static final FloatProperty<PageIndicatorDots> PAGINATION_ALPHA =
+ new FloatProperty<PageIndicatorDots>("pagination_alpha") {
+ @Override
+ public Float get(PageIndicatorDots obj) {
+ return obj.getAlpha();
+ }
+
+ @Override
+ public void setValue(PageIndicatorDots obj, float alpha) {
+ obj.setAlpha(alpha);
+ obj.invalidate();
+ }
+ };
+
+ private final Handler mDelayedPaginationFadeHandler = new Handler(Looper.getMainLooper());
+ private final Drawable mPageIndicatorDrawable;
private final float mDotRadius;
+ private final float mCircleGap;
+ private final float mPageIndicatorSize;
+ private final float mPageIndicatorRadius;
private final boolean mIsRtl;
private int mNumPages;
private int mActivePage;
+ private int mCurrentScroll;
+ private int mTotalScroll;
+ private boolean mShouldAutoHide = true;
+ private int mToAlpha;
/**
* The current position of the active dot including the animation progress.
* For ex:
- * 0.0 => Active dot is at position 0
- * 0.33 => Active dot is at position 0 and is moving towards 1
- * 0.50 => Active dot is at position [0, 1]
- * 0.77 => Active dot has left position 0 and is collapsing towards position 1
- * 1.0 => Active dot is at position 1
+ * 0.0 => Active dot is at position 0
+ * 0.33 => Active dot is at position 0 and is moving towards 1
+ * 0.50 => Active dot is at position [0, 1]
+ * 0.77 => Active dot has left position 0 and is collapsing towards position 1
+ * 1.0 => Active dot is at position 1
*/
private float mCurrentPosition;
private float mFinalPosition;
private ObjectAnimator mAnimator;
+ private @Nullable ObjectAnimator mAlphaAnimator;
private float[] mEntryAnimationRadiusFactors;
+ private Runnable mHidePaginationRunnable = () -> animatePaginationToAlpha(INVISIBLE_ALPHA);
+
public PageIndicatorDots(Context context) {
this(context, null);
}
@@ -109,37 +155,138 @@
public PageIndicatorDots(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
- mCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mCirclePaint.setStyle(Style.FILL);
- mCirclePaint.setColor(Themes.getAttrColor(context, R.attr.folderPaginationColor));
+ mPaginationPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mPaginationPaint.setStyle(Style.FILL);
+ mPaginationPaint.setColor(Themes.getAttrColor(context, R.attr.folderPaginationColor));
mDotRadius = getResources().getDimension(R.dimen.page_indicator_dot_size) / 2;
- setOutlineProvider(new MyOutlineProver());
+ if (SHOW_DELIGHTFUL_PAGINATION.get()) {
+ mPageIndicatorSize = getResources().getDimension(
+ R.dimen.page_indicator_size);
+ mPageIndicatorRadius = mPageIndicatorSize / 2;
+ mPageIndicatorDrawable = context.getDrawable(R.drawable.page_indicator);
+ mPageIndicatorDrawable.setBounds(0, 0, (int) mPageIndicatorSize,
+ (int) mPageIndicatorSize);
+ mCircleGap = DOT_GAP_FACTOR_FLOAT * mDotRadius;
+
+ } else {
+ mPageIndicatorSize = 0;
+ mPageIndicatorRadius = 0;
+ mPageIndicatorDrawable = null;
+ mCircleGap = DOT_GAP_FACTOR * mDotRadius;
+ }
+ if (!SHOW_DELIGHTFUL_PAGINATION.get()) {
+ setOutlineProvider(new MyOutlineProver());
+ }
mIsRtl = Utilities.isRtl(getResources());
}
@Override
public void setScroll(int currentScroll, int totalScroll) {
- if (mNumPages > 1) {
- if (mIsRtl) {
- currentScroll = totalScroll - currentScroll;
- }
- int scrollPerPage = totalScroll / (mNumPages - 1);
- int pageToLeft = currentScroll / scrollPerPage;
- int pageToLeftScroll = pageToLeft * scrollPerPage;
- int pageToRightScroll = pageToLeftScroll + scrollPerPage;
+ if (SHOW_DELIGHTFUL_PAGINATION.get() || SHOW_DOT_PAGINATION.get()) {
+ animatePaginationToAlpha(VISIBLE_ALPHA);
+ }
- float scrollThreshold = SHIFT_THRESHOLD * scrollPerPage;
- if (currentScroll < pageToLeftScroll + scrollThreshold) {
- // scroll is within the left page's threshold
- animateToPosition(pageToLeft);
- } else if (currentScroll > pageToRightScroll - scrollThreshold) {
- // scroll is far enough from left page to go to the right page
- animateToPosition(pageToLeft + 1);
- } else {
- // scroll is between left and right page
- animateToPosition(pageToLeft + SHIFT_PER_ANIMATION);
+ if (mNumPages <= 1) {
+ mCurrentScroll = 0;
+ return;
+ }
+
+ if (mIsRtl) {
+ currentScroll = totalScroll - currentScroll;
+ }
+
+ mTotalScroll = totalScroll;
+ if (SHOW_DELIGHTFUL_PAGINATION.get()) {
+ mCurrentScroll = currentScroll;
+ invalidate();
+
+ if (mShouldAutoHide
+ && (getScrollPerPage() == 0 || mCurrentScroll % getScrollPerPage() == 0)) {
+ hideAfterDelay();
}
+ return;
+ }
+
+ int scrollPerPage = totalScroll / (mNumPages - 1);
+ int pageToLeft = currentScroll / scrollPerPage;
+ int pageToLeftScroll = pageToLeft * scrollPerPage;
+ int pageToRightScroll = pageToLeftScroll + scrollPerPage;
+
+ float scrollThreshold = SHIFT_THRESHOLD * scrollPerPage;
+ if (currentScroll < pageToLeftScroll + scrollThreshold) {
+ // scroll is within the left page's threshold
+ animateToPosition(pageToLeft);
+ if (SHOW_DOT_PAGINATION.get()) {
+ hideAfterDelay();
+ }
+ } else if (currentScroll > pageToRightScroll - scrollThreshold) {
+ // scroll is far enough from left page to go to the right page
+ animateToPosition(pageToLeft + 1);
+ if (SHOW_DOT_PAGINATION.get()) {
+ hideAfterDelay();
+ }
+ } else {
+ // scroll is between left and right page
+ animateToPosition(pageToLeft + SHIFT_PER_ANIMATION);
+ }
+ }
+
+ @Override
+ public void setShouldAutoHide(boolean shouldAutoHide) {
+ mShouldAutoHide = shouldAutoHide;
+ if (shouldAutoHide && this.getAlpha() > INVISIBLE_ALPHA) {
+ hideAfterDelay();
+ } else if (!shouldAutoHide) {
+ mDelayedPaginationFadeHandler.removeCallbacksAndMessages(null);
+ }
+ }
+
+ private void hideAfterDelay() {
+ mDelayedPaginationFadeHandler.removeCallbacksAndMessages(null);
+ mDelayedPaginationFadeHandler.postDelayed(mHidePaginationRunnable, PAGINATION_FADE_DELAY);
+ }
+
+ private void animatePaginationToAlpha(int alpha) {
+ if (alpha == mToAlpha) {
+ // Ignore the new animation if it is going to the same alpha as the current animation.
+ return;
+ }
+ mToAlpha = alpha;
+
+ if (mAlphaAnimator != null) {
+ mAlphaAnimator.cancel();
+ }
+ mAlphaAnimator = ObjectAnimator.ofFloat(this, PAGINATION_ALPHA,
+ alpha);
+ mAlphaAnimator.setDuration(ALPHA_ANIMATE_DURATION);
+ mAlphaAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mAlphaAnimator = null;
+ }
+ });
+ mAlphaAnimator.start();
+
+ }
+
+ /**
+ * Pauses all currently running animations.
+ */
+ @Override
+ public void pauseAnimations() {
+ if (mAlphaAnimator != null) {
+ mAlphaAnimator.pause();
+ }
+ }
+
+ /**
+ * Force-ends all currently running or paused animations.
+ */
+ @Override
+ public void skipAnimationsToEnd() {
+ if (mAlphaAnimator != null) {
+ mAlphaAnimator.end();
}
}
@@ -177,7 +324,7 @@
}
public void playEntryAnimation() {
- int count = mEntryAnimationRadiusFactors.length;
+ int count = mEntryAnimationRadiusFactors.length;
if (count == 0) {
mEntryAnimationRadiusFactors = null;
invalidate();
@@ -231,16 +378,20 @@
// Add extra spacing of mDotRadius on all sides so than entry animation could be run.
int width = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY ?
MeasureSpec.getSize(widthMeasureSpec) : (int) ((mNumPages * 3 + 2) * mDotRadius);
- int height= MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY ?
- MeasureSpec.getSize(heightMeasureSpec) : (int) (4 * mDotRadius);
+ int height = MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY
+ ? MeasureSpec.getSize(heightMeasureSpec) : (int) (4 * mDotRadius);
setMeasuredDimension(width, height);
}
@Override
protected void onDraw(Canvas canvas) {
+ if ((mShouldAutoHide && mTotalScroll == 0) || mNumPages < 2) {
+ return;
+ }
+
// Draw all page indicators;
- float circleGap = 3 * mDotRadius;
- float startX = (getWidth() - mNumPages * circleGap + mDotRadius) / 2;
+ float circleGap = mCircleGap;
+ float startX = (getWidth() - (mNumPages * circleGap) + mDotRadius) / 2;
float x = startX + mDotRadius;
float y = getHeight() / 2;
@@ -252,43 +403,123 @@
circleGap = -circleGap;
}
for (int i = 0; i < mEntryAnimationRadiusFactors.length; i++) {
- mCirclePaint.setAlpha(i == mActivePage ? DOT_ACTIVE_ALPHA : DOT_INACTIVE_ALPHA);
- canvas.drawCircle(x, y, mDotRadius * mEntryAnimationRadiusFactors[i], mCirclePaint);
+ mPaginationPaint.setAlpha(i == mActivePage ? PAGE_INDICATOR_ALPHA : DOT_ALPHA);
+ if (SHOW_DELIGHTFUL_PAGINATION.get()) {
+ if (i != mActivePage) {
+ canvas.drawCircle(x, y, mDotRadius * mEntryAnimationRadiusFactors[i],
+ mPaginationPaint);
+ } else {
+ drawPageIndicator(canvas, mEntryAnimationRadiusFactors[i]);
+ }
+ } else {
+ canvas.drawCircle(x, y, mDotRadius * mEntryAnimationRadiusFactors[i],
+ mPaginationPaint);
+ }
x += circleGap;
}
} else {
- mCirclePaint.setAlpha(DOT_INACTIVE_ALPHA);
+ // Here we draw the dots
+ mPaginationPaint.setAlpha(DOT_ALPHA);
for (int i = 0; i < mNumPages; i++) {
- canvas.drawCircle(x, y, mDotRadius, mCirclePaint);
+ if (SHOW_DELIGHTFUL_PAGINATION.get()) {
+ canvas.drawCircle(x, y, getRadius(x), mPaginationPaint);
+ } else {
+ canvas.drawCircle(x, y, mDotRadius, mPaginationPaint);
+ }
x += circleGap;
}
- mCirclePaint.setAlpha(DOT_ACTIVE_ALPHA);
- canvas.drawRoundRect(getActiveRect(), mDotRadius, mDotRadius, mCirclePaint);
+ // Here we draw the current page indicator
+ mPaginationPaint.setAlpha(PAGE_INDICATOR_ALPHA);
+ if (SHOW_DELIGHTFUL_PAGINATION.get()) {
+ drawPageIndicator(canvas, 1);
+ } else {
+ canvas.drawRoundRect(getActiveRect(), mDotRadius, mDotRadius, mPaginationPaint);
+ }
}
}
+ /**
+ * Draws the page indicator, denoting the currently selected page
+ *
+ * @param canvas is used to draw the page indicator and to rotate it as we scroll
+ * @param scale is used to set the scale of our canvas
+ */
+ private void drawPageIndicator(Canvas canvas, float scale) {
+ RectF currRect = getActiveRect();
+
+ // saves the canvas so we can later restore it to its original scale
+ canvas.save();
+
+ // Moves the canvas to start at the top left corner of the page indicator
+ canvas.translate(currRect.left, currRect.top);
+
+ // Scales the canvas in place to animate the indicator on entry
+ canvas.scale(scale, scale, mPageIndicatorRadius, mPageIndicatorRadius);
+
+ int scrollPerPage = getScrollPerPage();
+ // This IF is to avoid division by 0
+ if (scrollPerPage != 0) {
+ int delta = mCurrentScroll % scrollPerPage;
+ canvas.rotate((INDICATOR_ROTATION * delta) / scrollPerPage,
+ mPageIndicatorRadius, mPageIndicatorRadius);
+ }
+
+ mPageIndicatorDrawable.draw(canvas);
+ canvas.restore();
+ }
+
+ /**
+ * Returns the radius of the circle based on how close the page indicator is to it
+ *
+ * @param dotPositionX is the position the dot is located at in the x-axis
+ */
+ private float getRadius(float dotPositionX) {
+
+ float startXIndicator =
+ ((getWidth() - (mNumPages * mCircleGap) + mDotRadius) / 2) - getOffset();
+ float indicatorPosition = startXIndicator + getIndicatorScrollDistance()
+ + mPageIndicatorRadius;
+
+ // If the indicator gets close enough to a dot then we change the radius
+ // of the dot based on how close the indicator is to it.
+ float dotDistance = Math.abs(indicatorPosition - dotPositionX);
+ if (dotDistance <= mCircleGap) {
+ return Utilities.mapToRange(dotDistance, 0, mCircleGap, 0f, mDotRadius,
+ Interpolators.LINEAR);
+ }
+ return mDotRadius;
+ }
+
private RectF getActiveRect() {
float startCircle = (int) mCurrentPosition;
float delta = mCurrentPosition - startCircle;
float diameter = 2 * mDotRadius;
- float circleGap = 3 * mDotRadius;
- float startX = (getWidth() - mNumPages * circleGap + mDotRadius) / 2;
+ float startX;
- sTempRect.top = getHeight() * 0.5f - mDotRadius;
- sTempRect.bottom = getHeight() * 0.5f + mDotRadius;
- sTempRect.left = startX + startCircle * circleGap;
- sTempRect.right = sTempRect.left + diameter;
-
- if (delta < SHIFT_PER_ANIMATION) {
- // dot is capturing the right circle.
- sTempRect.right += delta * circleGap * 2;
+ if (SHOW_DELIGHTFUL_PAGINATION.get()) {
+ startX = ((getWidth() - (mNumPages * mCircleGap) + mDotRadius) / 2) - getOffset();
+ sTempRect.top = (getHeight() - mPageIndicatorSize) * 0.5f;
+ sTempRect.bottom = (getHeight() + mPageIndicatorSize) * 0.5f;
+ sTempRect.left = startX + getIndicatorScrollDistance();
+ sTempRect.right = sTempRect.left + mPageIndicatorSize;
} else {
- // Dot is leaving the left circle.
- sTempRect.right += circleGap;
+ startX = ((getWidth() - (mNumPages * mCircleGap) + mDotRadius) / 2);
+ sTempRect.top = (getHeight() * 0.5f) - mDotRadius;
+ sTempRect.bottom = (getHeight() * 0.5f) + mDotRadius;
+ sTempRect.left = startX + (startCircle * mCircleGap);
+ sTempRect.right = sTempRect.left + diameter;
- delta -= SHIFT_PER_ANIMATION;
- sTempRect.left += delta * circleGap * 2;
+ if (delta < SHIFT_PER_ANIMATION) {
+ // dot is capturing the right circle.
+ sTempRect.right += delta * mCircleGap * 2;
+ } else {
+ // Dot is leaving the left circle.
+ sTempRect.right += mCircleGap;
+
+ delta -= SHIFT_PER_ANIMATION;
+ sTempRect.left += delta * mCircleGap * 2;
+ }
}
if (mIsRtl) {
@@ -296,9 +527,33 @@
sTempRect.right = getWidth() - sTempRect.left;
sTempRect.left = sTempRect.right - rectWidth;
}
+
return sTempRect;
}
+ /**
+ * The offset between the radius of the dot and the midpoint of the indicator so that
+ * the indicator is centered in with the indicator circles
+ */
+ private float getOffset() {
+ return mPageIndicatorRadius - mDotRadius;
+ }
+
+ /**
+ * Returns an int that is the amount we need to scroll per page
+ */
+ private int getScrollPerPage() {
+ return mNumPages > 1 ? mTotalScroll / (mNumPages - 1) : 0;
+ }
+
+ /**
+ * The current scroll adjusted for the distance the indicator needs to travel on the screen
+ */
+ private float getIndicatorScrollDistance() {
+ int scrollPerPage = getScrollPerPage();
+ return scrollPerPage != 0 ? ((float) mCurrentScroll / scrollPerPage) * mCircleGap : 0;
+ }
+
private class MyOutlineProver extends ViewOutlineProvider {
@Override
@@ -336,4 +591,12 @@
}
}
}
+
+ /**
+ * We need to override setInsets to prevent InsettableFrameLayout from applying different
+ * margins on the pagination.
+ */
+ @Override
+ public void setInsets(Rect insets) {
+ }
}
diff --git a/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java b/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
index 87ae890..bde4e52 100644
--- a/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
+++ b/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
@@ -14,12 +14,9 @@
import android.os.Looper;
import android.util.AttributeSet;
import android.util.Property;
-import android.view.Gravity;
import android.view.View;
import android.view.ViewConfiguration;
-import android.widget.FrameLayout;
-import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Insettable;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
@@ -258,21 +255,11 @@
}
}
+ /**
+ * We need to override setInsets to prevent InsettableFrameLayout from applying different
+ * margins on the page indicator.
+ */
@Override
public void setInsets(Rect insets) {
- DeviceProfile grid = mLauncher.getDeviceProfile();
- FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
-
- if (grid.isVerticalBarLayout()) {
- Rect padding = grid.workspacePadding;
- lp.leftMargin = padding.left + grid.workspaceCellPaddingXPx;
- lp.rightMargin = padding.right + grid.workspaceCellPaddingXPx;
- lp.bottomMargin = padding.bottom;
- } else {
- lp.leftMargin = lp.rightMargin = 0;
- lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
- lp.bottomMargin = grid.hotseatBarSizePx;
- }
- setLayoutParams(lp);
}
}
diff --git a/src/com/android/launcher3/pm/InstallSessionHelper.java b/src/com/android/launcher3/pm/InstallSessionHelper.java
index 9da6670..16bb868 100644
--- a/src/com/android/launcher3/pm/InstallSessionHelper.java
+++ b/src/com/android/launcher3/pm/InstallSessionHelper.java
@@ -30,6 +30,7 @@
import android.util.Log;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.WorkerThread;
@@ -51,38 +52,50 @@
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
+import java.util.Objects;
/**
* Utility class to tracking install sessions
*/
public class InstallSessionHelper {
+ @NonNull
private static final String LOG = "InstallSessionHelper";
// Set<String> of session ids of promise icons that have been added to the home screen
// as FLAG_PROMISE_NEW_INSTALLS.
+ @NonNull
protected static final String PROMISE_ICON_IDS = "promise_icon_ids";
private static final boolean DEBUG = false;
+ @NonNull
public static final MainThreadInitializedObject<InstallSessionHelper> INSTANCE =
new MainThreadInitializedObject<>(InstallSessionHelper::new);
+ @Nullable
private final LauncherApps mLauncherApps;
+
+ @NonNull
private final Context mAppContext;
+ @NonNull
private final PackageInstaller mInstaller;
+
+ @NonNull
private final HashMap<String, Boolean> mSessionVerifiedMap = new HashMap<>();
+ @Nullable
private IntSet mPromiseIconIds;
- public InstallSessionHelper(Context context) {
+ public InstallSessionHelper(@NonNull final Context context) {
mInstaller = context.getPackageManager().getPackageInstaller();
mAppContext = context.getApplicationContext();
mLauncherApps = context.getSystemService(LauncherApps.class);
}
@WorkerThread
+ @NonNull
private IntSet getPromiseIconIds() {
Preconditions.assertWorkerThread();
if (mPromiseIconIds != null) {
@@ -108,6 +121,7 @@
return mPromiseIconIds;
}
+ @NonNull
public HashMap<PackageUserKey, SessionInfo> getActiveSessions() {
HashMap<PackageUserKey, SessionInfo> activePackages = new HashMap<>();
for (SessionInfo info : getAllVerifiedSessions()) {
@@ -117,6 +131,7 @@
return activePackages;
}
+ @Nullable
public SessionInfo getActiveSessionInfo(UserHandle user, String pkg) {
for (SessionInfo info : getAllVerifiedSessions()) {
boolean match = pkg.equals(info.getAppPackageName());
@@ -136,11 +151,13 @@
.apply();
}
- SessionInfo getVerifiedSessionInfo(int sessionId) {
+ @Nullable
+ SessionInfo getVerifiedSessionInfo(final int sessionId) {
return verify(mInstaller.getSessionInfo(sessionId));
}
- private SessionInfo verify(SessionInfo sessionInfo) {
+ @Nullable
+ private SessionInfo verify(@Nullable final SessionInfo sessionInfo) {
if (sessionInfo == null
|| sessionInfo.getInstallerPackageName() == null
|| TextUtils.isEmpty(sessionInfo.getAppPackageName())) {
@@ -167,9 +184,10 @@
return mSessionVerifiedMap.get(pkg) ? sessionInfo : null;
}
+ @NonNull
public List<SessionInfo> getAllVerifiedSessions() {
List<SessionInfo> list = new ArrayList<>(Utilities.ATLEAST_Q
- ? mLauncherApps.getAllPackageInstallerSessions()
+ ? Objects.requireNonNull(mLauncherApps).getAllPackageInstallerSessions()
: mInstaller.getAllSessions());
Iterator<SessionInfo> it = list.iterator();
while (it.hasNext()) {
@@ -201,12 +219,12 @@
}
@WorkerThread
- public boolean promiseIconAddedForId(int sessionId) {
+ public boolean promiseIconAddedForId(final int sessionId) {
return getPromiseIconIds().contains(sessionId);
}
@WorkerThread
- public void removePromiseIconId(int sessionId) {
+ public void removePromiseIconId(final int sessionId) {
if (promiseIconAddedForId(sessionId)) {
getPromiseIconIds().getArray().removeValue(sessionId);
updatePromiseIconPrefs();
@@ -222,17 +240,15 @@
* - A promise icon for the session has not already been created
*/
@WorkerThread
- void tryQueuePromiseAppIcon(PackageInstaller.SessionInfo sessionInfo) {
+ void tryQueuePromiseAppIcon(@Nullable final PackageInstaller.SessionInfo sessionInfo) {
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.MISSING_PROMISE_ICON, LOG + " tryQueuePromiseAppIcon"
- + ", FeatureFlags=" + FeatureFlags.PROMISE_APPS_NEW_INSTALLS.get()
+ ", SessionCommitReceiveEnabled" + SessionCommitReceiver.isEnabled(mAppContext)
+ ", verifySessionInfo(sessionInfo)=" + verifySessionInfo(sessionInfo)
+ ", !promiseIconAdded=" + (sessionInfo != null
&& !promiseIconAddedForId(sessionInfo.getSessionId())));
}
- if (FeatureFlags.PROMISE_APPS_NEW_INSTALLS.get()
- && SessionCommitReceiver.isEnabled(mAppContext)
+ if (SessionCommitReceiver.isEnabled(mAppContext)
&& verifySessionInfo(sessionInfo)
&& !promiseIconAddedForId(sessionInfo.getSessionId())) {
FileLog.d(LOG, "Adding package name to install queue: "
@@ -246,7 +262,7 @@
}
}
- public boolean verifySessionInfo(PackageInstaller.SessionInfo sessionInfo) {
+ public boolean verifySessionInfo(@Nullable final PackageInstaller.SessionInfo sessionInfo) {
if (TestProtocol.sDebugTracing) {
boolean appNotInstalled = sessionInfo == null
|| !new PackageManagerHelper(mAppContext)
@@ -269,14 +285,15 @@
sessionInfo.getAppPackageName(), getUserHandle(sessionInfo));
}
- public InstallSessionTracker registerInstallTracker(InstallSessionTracker.Callback callback) {
+ public InstallSessionTracker registerInstallTracker(
+ @Nullable final InstallSessionTracker.Callback callback) {
InstallSessionTracker tracker = new InstallSessionTracker(
this, callback, mInstaller, mLauncherApps);
tracker.register();
return tracker;
}
- public static UserHandle getUserHandle(SessionInfo info) {
+ public static UserHandle getUserHandle(@NonNull final SessionInfo info) {
return Utilities.ATLEAST_Q ? info.getUser() : Process.myUserHandle();
}
}
diff --git a/src/com/android/launcher3/pm/InstallSessionTracker.java b/src/com/android/launcher3/pm/InstallSessionTracker.java
index b16aaa2..aeaa320 100644
--- a/src/com/android/launcher3/pm/InstallSessionTracker.java
+++ b/src/com/android/launcher3/pm/InstallSessionTracker.java
@@ -28,12 +28,15 @@
import android.util.Log;
import android.util.SparseArray;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.util.PackageUserKey;
import java.lang.ref.WeakReference;
+import java.util.Objects;
@WorkerThread
public class InstallSessionTracker extends PackageInstaller.SessionCallback {
@@ -41,14 +44,22 @@
// Lazily initialized
private SparseArray<PackageUserKey> mActiveSessions = null;
+ @NonNull
private final WeakReference<InstallSessionHelper> mWeakHelper;
+
+ @NonNull
private final WeakReference<Callback> mWeakCallback;
+
+ @NonNull
private final PackageInstaller mInstaller;
+
+ @Nullable
private final LauncherApps mLauncherApps;
- InstallSessionTracker(InstallSessionHelper installerCompat, Callback callback,
- PackageInstaller installer, LauncherApps launcherApps) {
+ InstallSessionTracker(@Nullable final InstallSessionHelper installerCompat,
+ @Nullable final Callback callback, @NonNull final PackageInstaller installer,
+ @Nullable LauncherApps launcherApps) {
mWeakHelper = new WeakReference<>(installerCompat);
mWeakCallback = new WeakReference<>(callback);
mInstaller = installer;
@@ -56,7 +67,7 @@
}
@Override
- public void onCreated(int sessionId) {
+ public void onCreated(final int sessionId) {
InstallSessionHelper helper = mWeakHelper.get();
Callback callback = mWeakCallback.get();
if (TestProtocol.sDebugTracing) {
@@ -80,7 +91,7 @@
}
@Override
- public void onFinished(int sessionId, boolean success) {
+ public void onFinished(final int sessionId, final boolean success) {
InstallSessionHelper helper = mWeakHelper.get();
Callback callback = mWeakCallback.get();
if (callback == null || helper == null) {
@@ -108,7 +119,7 @@
}
@Override
- public void onProgressChanged(int sessionId, float progress) {
+ public void onProgressChanged(final int sessionId, final float progress) {
InstallSessionHelper helper = mWeakHelper.get();
Callback callback = mWeakCallback.get();
if (callback == null || helper == null) {
@@ -121,10 +132,10 @@
}
@Override
- public void onActiveChanged(int sessionId, boolean active) { }
+ public void onActiveChanged(final int sessionId, final boolean active) { }
@Override
- public void onBadgingChanged(int sessionId) {
+ public void onBadgingChanged(final int sessionId) {
InstallSessionHelper helper = mWeakHelper.get();
Callback callback = mWeakCallback.get();
if (callback == null || helper == null) {
@@ -136,8 +147,9 @@
}
}
- private SessionInfo pushSessionDisplayToLauncher(
- int sessionId, InstallSessionHelper helper, Callback callback) {
+ @Nullable
+ private SessionInfo pushSessionDisplayToLauncher(final int sessionId,
+ @NonNull final InstallSessionHelper helper, @NonNull final Callback callback) {
SessionInfo session = helper.getVerifiedSessionInfo(sessionId);
if (session != null && session.getAppPackageName() != null) {
PackageUserKey key =
@@ -149,7 +161,9 @@
return null;
}
- private SparseArray<PackageUserKey> getActiveSessionMap(InstallSessionHelper helper) {
+ @NonNull
+ private SparseArray<PackageUserKey> getActiveSessionMap(
+ @NonNull final InstallSessionHelper helper) {
if (mActiveSessions == null) {
mActiveSessions = new SparseArray<>();
helper.getActiveSessions().forEach(
@@ -162,7 +176,8 @@
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
mInstaller.registerSessionCallback(this, MODEL_EXECUTOR.getHandler());
} else {
- mLauncherApps.registerPackageInstallerSessionCallback(MODEL_EXECUTOR, this);
+ Objects.requireNonNull(mLauncherApps).registerPackageInstallerSessionCallback(
+ MODEL_EXECUTOR, this);
}
}
@@ -170,18 +185,18 @@
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
mInstaller.unregisterSessionCallback(this);
} else {
- mLauncherApps.unregisterPackageInstallerSessionCallback(this);
+ Objects.requireNonNull(mLauncherApps).unregisterPackageInstallerSessionCallback(this);
}
}
public interface Callback {
- void onSessionFailure(String packageName, UserHandle user);
+ void onSessionFailure(@NonNull String packageName, @NonNull UserHandle user);
- void onUpdateSessionDisplay(PackageUserKey key, SessionInfo info);
+ void onUpdateSessionDisplay(@NonNull PackageUserKey key, @NonNull SessionInfo info);
- void onPackageStateChanged(PackageInstallInfo info);
+ void onPackageStateChanged(@NonNull PackageInstallInfo info);
- void onInstallSessionCreated(PackageInstallInfo info);
+ void onInstallSessionCreated(@NonNull PackageInstallInfo info);
}
}
diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java
index 48b3acf..95ac7b0 100644
--- a/src/com/android/launcher3/provider/RestoreDbTask.java
+++ b/src/com/android/launcher3/provider/RestoreDbTask.java
@@ -47,6 +47,7 @@
import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.LogConfig;
+import com.android.launcher3.widget.LauncherWidgetHolder;
import java.io.InvalidObjectException;
import java.util.Arrays;
@@ -353,9 +354,12 @@
private void restoreAppWidgetIdsIfExists(Context context) {
SharedPreferences prefs = Utilities.getPrefs(context);
if (prefs.contains(APPWIDGET_OLD_IDS) && prefs.contains(APPWIDGET_IDS)) {
+ LauncherWidgetHolder holder = new LauncherWidgetHolder(context);
AppWidgetsRestoredReceiver.restoreAppWidgetIds(context,
IntArray.fromConcatString(prefs.getString(APPWIDGET_OLD_IDS, "")).toArray(),
- IntArray.fromConcatString(prefs.getString(APPWIDGET_IDS, "")).toArray());
+ IntArray.fromConcatString(prefs.getString(APPWIDGET_IDS, "")).toArray(),
+ holder);
+ holder.destroy();
} else {
FileLog.d(TAG, "No app widget ids to restore.");
}
diff --git a/src/com/android/launcher3/search/SearchCallback.java b/src/com/android/launcher3/search/SearchCallback.java
index 495a303..cf7ab10 100644
--- a/src/com/android/launcher3/search/SearchCallback.java
+++ b/src/com/android/launcher3/search/SearchCallback.java
@@ -24,6 +24,11 @@
*/
public interface SearchCallback<T> {
+ // Search Result Codes
+ int UNKNOWN = 0;
+ int INTERMEDIATE = 1;
+ int FINAL = 2;
+
/**
* Called when the search from primary source is complete.
*
@@ -32,6 +37,17 @@
void onSearchResult(String query, ArrayList<T> items);
/**
+ * Called when the search from primary source is complete.
+ *
+ * @param items list of search results
+ * @param searchResultCode indicates if the result is final or intermediate for a given query
+ * since we can get search results from multiple sources.
+ */
+ default void onSearchResult(String query, ArrayList<T> items, int searchResultCode) {
+ onSearchResult(query, items);
+ }
+
+ /**
* Called when the search results should be cleared.
*/
void clearSearchResult();
diff --git a/src/com/android/launcher3/secondarydisplay/PinnedAppsAdapter.java b/src/com/android/launcher3/secondarydisplay/PinnedAppsAdapter.java
index a0ed77e..f03c62a 100644
--- a/src/com/android/launcher3/secondarydisplay/PinnedAppsAdapter.java
+++ b/src/com/android/launcher3/secondarydisplay/PinnedAppsAdapter.java
@@ -168,15 +168,18 @@
mPrefs.unregisterOnSharedPreferenceChangeListener(this);
}
- private void update(ItemInfo info, Function<ComponentKey, Boolean> op) {
+ /**
+ * Pins or unpins apps from home screen
+ */
+ public void update(ItemInfo info, Function<ComponentKey, Boolean> op) {
ComponentKey key = new ComponentKey(info.getTargetComponent(), info.user);
if (op.apply(key)) {
createFilteredAppsList();
Set<ComponentKey> copy = new HashSet<>(mPinnedApps);
Executors.MODEL_EXECUTOR.submit(() ->
mPrefs.edit().putStringSet(PINNED_APPS_KEY,
- copy.stream().map(this::encode).collect(Collectors.toSet()))
- .apply());
+ copy.stream().map(this::encode).collect(Collectors.toSet()))
+ .apply());
}
}
@@ -210,6 +213,13 @@
mPinnedApps.contains(new ComponentKey(info.getTargetComponent(), info.user)));
}
+ /**
+ * Pins app to home screen
+ */
+ public void addPinnedApp(ItemInfo info) {
+ update(info, mPinnedApps::add);
+ }
+
private class PinUnPinShortcut extends SystemShortcut<SecondaryDisplayLauncher> {
private final boolean mIsPinned;
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
index a2ab7f9..7b32d8b 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
@@ -18,6 +18,9 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.content.Intent;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
@@ -26,11 +29,21 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.DragSource;
+import com.android.launcher3.DropTarget;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel;
+import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.ActivityAllAppsContainerView;
+import com.android.launcher3.dragndrop.DragController;
+import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.dragndrop.DraggableView;
+import com.android.launcher3.graphics.DragPreviewProvider;
+import com.android.launcher3.icons.FastBitmapDrawable;
import com.android.launcher3.model.BgDataModel;
import com.android.launcher3.model.StringCache;
import com.android.launcher3.model.data.AppInfo;
@@ -39,6 +52,8 @@
import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.popup.PopupDataProvider;
import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.IntSet;
+import com.android.launcher3.util.OnboardingPrefs;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.BaseDragLayer;
@@ -48,11 +63,11 @@
* Launcher activity for secondary displays
*/
public class SecondaryDisplayLauncher extends BaseDraggingActivity
- implements BgDataModel.Callbacks {
+ implements BgDataModel.Callbacks, DragController.DragListener {
private LauncherModel mModel;
-
private BaseDragLayer mDragLayer;
+ private SecondaryDragController mDragController;
private ActivityAllAppsContainerView<SecondaryDisplayLauncher> mAppsView;
private View mAppsButton;
@@ -61,11 +76,19 @@
private boolean mAppDrawerShown = false;
private StringCache mStringCache;
+ private OnboardingPrefs<?> mOnboardingPrefs;
+ private boolean mBindingItems = false;
+ private SecondaryDisplayPredictions mSecondaryDisplayPredictions;
+
+ private final int[] mTempXY = new int[2];
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mModel = LauncherAppState.getInstance(this).getModel();
+ mDragController = new SecondaryDragController(this);
+ mOnboardingPrefs = new OnboardingPrefs<>(this, Utilities.getPrefs(this));
+ mSecondaryDisplayPredictions = SecondaryDisplayPredictions.newInstance(this);
if (getWindow().getDecorView().isAttachedToWindow()) {
initUi();
}
@@ -77,6 +100,12 @@
initUi();
}
+ @Override
+ public void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ this.getDragController().removeDragListener(this);
+ }
+
private void initUi() {
if (mDragLayer != null) {
return;
@@ -97,6 +126,7 @@
mAppsView = findViewById(R.id.apps_view);
mAppsButton = findViewById(R.id.all_apps_button);
+ mDragController.addDragListener(this);
mPopupDataProvider = new PopupDataProvider(
mAppsView.getAppsStore()::updateNotificationDots);
@@ -104,6 +134,12 @@
}
@Override
+ protected void onPause() {
+ super.onPause();
+ mDragController.cancelDrag();
+ }
+
+ @Override
public void onNewIntent(Intent intent) {
super.onNewIntent(intent);
@@ -120,12 +156,21 @@
showAppDrawer(false);
}
+ public DragController getDragController() {
+ return mDragController;
+ }
+
@Override
public void onBackPressed() {
if (finishAutoCancelActionMode()) {
return;
}
+ if (mDragController.isDragging()) {
+ mDragController.cancelDrag();
+ return;
+ }
+
// Note: There should be at most one log per method call. This is enforced implicitly
// by using if-else statements.
AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(this);
@@ -193,7 +238,7 @@
float closeR = Themes.getDialogCornerRadius(this);
float startR = mAppsButton.getWidth() / 2f;
- float[] buttonPos = new float[] { startR, startR};
+ float[] buttonPos = new float[]{startR, startR};
mDragLayer.getDescendantCoordRelativeToSelf(mAppsButton, buttonPos);
mDragLayer.mapCoordInSelfToDescendant(mAppsView, buttonPos);
final Animator animator = ViewAnimationUtils.createCircularReveal(mAppsView,
@@ -204,6 +249,7 @@
mAppDrawerShown = true;
mAppsView.setVisibility(View.VISIBLE);
mAppsButton.setVisibility(View.INVISIBLE);
+ mSecondaryDisplayPredictions.updateAppDivider();
} else {
mAppDrawerShown = false;
animator.addListener(new AnimatorListenerAdapter() {
@@ -219,6 +265,27 @@
}
@Override
+ public OnboardingPrefs<?> getOnboardingPrefs() {
+ return mOnboardingPrefs;
+ }
+
+ @Override
+ public void startBinding() {
+ mBindingItems = true;
+ mDragController.cancelDrag();
+ }
+
+ @Override
+ public boolean isBindingItems() {
+ return mBindingItems;
+ }
+
+ @Override
+ public void finishBindingItems(IntSet pagesBoundFirst) {
+ mBindingItems = false;
+ }
+
+ @Override
public void bindDeepShortcutMap(HashMap<ComponentKey, Integer> deepShortcutMap) {
mPopupDataProvider.setDeepShortcutMap(deepShortcutMap);
}
@@ -230,6 +297,17 @@
}
@Override
+ public void bindExtraContainerItems(BgDataModel.FixedContainerItems item) {
+ if (item.containerId == LauncherSettings.Favorites.CONTAINER_PREDICTION) {
+ mSecondaryDisplayPredictions.setPredictedApps(item);
+ }
+ }
+
+ public SecondaryDisplayPredictions getSecondaryDisplayPredictions() {
+ return mSecondaryDisplayPredictions;
+ }
+
+ @Override
public StringCache getStringCache() {
return mStringCache;
}
@@ -259,7 +337,7 @@
Intent intent;
if (item instanceof ItemInfoWithIcon
&& (((ItemInfoWithIcon) item).runtimeStatusFlags
- & ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0) {
+ & ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0) {
ItemInfoWithIcon appInfo = (ItemInfoWithIcon) item;
intent = appInfo.getMarketIntent(this);
} else {
@@ -271,4 +349,101 @@
startActivitySafely(v, intent, item);
}
}
+
+ /**
+ * Core functionality for beginning a drag operation for an item that will be dropped within
+ * the secondary display grid home screen
+ */
+ public void beginDragShared(View child, DragSource source, DragOptions options) {
+ Object dragObject = child.getTag();
+ if (!(dragObject instanceof ItemInfo)) {
+ String msg = "Drag started with a view that has no tag set. This "
+ + "will cause a crash (issue 11627249) down the line. "
+ + "View: " + child + " tag: " + child.getTag();
+ throw new IllegalStateException(msg);
+ }
+ beginDragShared(child, source, (ItemInfo) dragObject,
+ new DragPreviewProvider(child), options);
+ }
+
+ private void beginDragShared(View child, DragSource source,
+ ItemInfo dragObject, DragPreviewProvider previewProvider, DragOptions options) {
+
+ float iconScale = 1f;
+ if (child instanceof BubbleTextView) {
+ FastBitmapDrawable icon = ((BubbleTextView) child).getIcon();
+ if (icon != null) {
+ iconScale = icon.getAnimatedScale();
+ }
+ }
+
+ // clear pressed state if necessary
+ child.clearFocus();
+ child.setPressed(false);
+ if (child instanceof BubbleTextView) {
+ BubbleTextView icon = (BubbleTextView) child;
+ icon.clearPressedBackground();
+ }
+
+ DraggableView draggableView = null;
+ if (child instanceof DraggableView) {
+ draggableView = (DraggableView) child;
+ }
+
+ final View contentView = previewProvider.getContentView();
+ final float scale;
+ // The draggable drawable follows the touch point around on the screen
+ final Drawable drawable;
+ if (contentView == null) {
+ drawable = previewProvider.createDrawable();
+ scale = previewProvider.getScaleAndPosition(drawable, mTempXY);
+ } else {
+ drawable = null;
+ scale = previewProvider.getScaleAndPosition(contentView, mTempXY);
+ }
+ int halfPadding = previewProvider.previewPadding / 2;
+ int dragLayerX = mTempXY[0];
+ int dragLayerY = mTempXY[1];
+
+ Point dragVisualizeOffset = null;
+ Rect dragRect = new Rect();
+ if (draggableView != null) {
+ draggableView.getSourceVisualDragBounds(dragRect);
+ dragLayerY += dragRect.top;
+ dragVisualizeOffset = new Point(-halfPadding, halfPadding);
+ }
+ if (contentView != null) {
+ mDragController.startDrag(
+ contentView,
+ draggableView,
+ dragLayerX,
+ dragLayerY,
+ source,
+ dragObject,
+ dragVisualizeOffset,
+ dragRect,
+ scale * iconScale,
+ scale,
+ options);
+ } else {
+ mDragController.startDrag(
+ drawable,
+ draggableView,
+ dragLayerX,
+ dragLayerY,
+ source,
+ dragObject,
+ dragVisualizeOffset,
+ dragRect,
+ scale * iconScale,
+ scale,
+ options);
+ }
+ }
+
+ @Override
+ public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) { }
+
+ @Override
+ public void onDragEnd() { }
}
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayPredictions.java b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayPredictions.java
new file mode 100644
index 0000000..21c50d3
--- /dev/null
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayPredictions.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.secondarydisplay;
+
+import android.content.Context;
+import android.view.View;
+
+import com.android.launcher3.R;
+import com.android.launcher3.allapps.ActivityAllAppsContainerView;
+import com.android.launcher3.model.BgDataModel;
+import com.android.launcher3.util.ResourceBasedOverride;
+
+/**
+ * Exposes Quickstep app prediction row APIs to {@link SecondaryDisplayLauncher}.
+ */
+public class SecondaryDisplayPredictions implements ResourceBasedOverride {
+ /**
+ * Creates a {@link SecondaryDisplayPredictions} instance.
+ */
+ static SecondaryDisplayPredictions newInstance(Context context) {
+ return Overrides.getObject(
+ SecondaryDisplayPredictions.class, context,
+ R.string.secondary_display_predictions_class);
+ }
+
+ /**
+ * Setup/update app divider separating app predictions from All Apps.
+ */
+ void updateAppDivider() {
+ }
+
+ /**
+ * Set predicted apps in top of app drawer.
+ */
+ public void setPredictedApps(BgDataModel.FixedContainerItems item) {
+ }
+
+ /**
+ * Set long click listener for predicted apps in top of app drawer.
+ */
+ public void setLongClickListener(
+ ActivityAllAppsContainerView<?> appsView,
+ View.OnLongClickListener onIconLongClickListener) {
+ }
+}
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDragController.java b/src/com/android/launcher3/secondarydisplay/SecondaryDragController.java
new file mode 100644
index 0000000..9bf2764
--- /dev/null
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDragController.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.secondarydisplay;
+
+import android.content.res.Resources;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.util.Log;
+import android.view.HapticFeedbackConstants;
+import android.view.View;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.DragSource;
+import com.android.launcher3.DropTarget;
+import com.android.launcher3.R;
+import com.android.launcher3.accessibility.DragViewStateAnnouncer;
+import com.android.launcher3.dragndrop.DragController;
+import com.android.launcher3.dragndrop.DragDriver;
+import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.dragndrop.DragView;
+import com.android.launcher3.dragndrop.DraggableView;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.testing.shared.TestProtocol;
+
+/**
+ * Drag controller for Secondary Launcher activity
+ */
+public class SecondaryDragController extends DragController<SecondaryDisplayLauncher> {
+
+ private static final boolean PROFILE_DRAWING_DURING_DRAG = false;
+
+ public SecondaryDragController(SecondaryDisplayLauncher secondaryLauncher) {
+ super(secondaryLauncher);
+ }
+
+ @Override
+ protected DragView startDrag(@Nullable Drawable drawable, @Nullable View view,
+ DraggableView originalView, int dragLayerX, int dragLayerY, DragSource source,
+ ItemInfo dragInfo, Point dragOffset, Rect dragRegion, float initialDragViewScale,
+ float dragViewScaleOnDrop, DragOptions options) {
+
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.NO_DROP_TARGET, "5");
+ }
+
+ if (PROFILE_DRAWING_DURING_DRAG) {
+ android.os.Debug.startMethodTracing("Launcher");
+ }
+ mActivity.hideKeyboard();
+
+ mOptions = options;
+ if (mOptions.simulatedDndStartPoint != null) {
+ mLastTouch.x = mMotionDown.x = mOptions.simulatedDndStartPoint.x;
+ mLastTouch.y = mMotionDown.y = mOptions.simulatedDndStartPoint.y;
+ }
+
+ final int registrationX = mMotionDown.x - dragLayerX;
+ final int registrationY = mMotionDown.y - dragLayerY;
+
+ final int dragRegionLeft = dragRegion == null ? 0 : dragRegion.left;
+ final int dragRegionTop = dragRegion == null ? 0 : dragRegion.top;
+
+ mLastDropTarget = null;
+
+ mDragObject = new DropTarget.DragObject(mActivity.getApplicationContext());
+ mDragObject.originalView = originalView;
+
+ mIsInPreDrag = mOptions.preDragCondition != null
+ && !mOptions.preDragCondition.shouldStartDrag(0);
+
+ final Resources res = mActivity.getResources();
+ final float scaleDps = mIsInPreDrag
+ ? res.getDimensionPixelSize(R.dimen.pre_drag_view_scale) : 0f;
+
+ final DragView dragView = mDragObject.dragView = drawable != null
+ ? new SecondaryDragView(
+ mActivity,
+ drawable,
+ registrationX,
+ registrationY,
+ initialDragViewScale,
+ dragViewScaleOnDrop,
+ scaleDps)
+ : new SecondaryDragView(
+ mActivity,
+ view,
+ view.getMeasuredWidth(),
+ view.getMeasuredHeight(),
+ registrationX,
+ registrationY,
+ initialDragViewScale,
+ dragViewScaleOnDrop,
+ scaleDps);
+ dragView.setItemInfo(dragInfo);
+ mDragObject.dragComplete = false;
+
+ mDragObject.xOffset = mMotionDown.x - (dragLayerX + dragRegionLeft);
+ mDragObject.yOffset = mMotionDown.y - (dragLayerY + dragRegionTop);
+
+ mDragDriver = DragDriver.create(this, mOptions, ev -> {
+ });
+ if (!mOptions.isAccessibleDrag) {
+ mDragObject.stateAnnouncer = DragViewStateAnnouncer.createFor(dragView);
+ }
+
+ mDragObject.dragSource = source;
+ mDragObject.dragInfo = dragInfo;
+ mDragObject.originalDragInfo = mDragObject.dragInfo.makeShallowCopy();
+
+ if (dragOffset != null) {
+ dragView.setDragVisualizeOffset(new Point(dragOffset));
+ }
+ if (dragRegion != null) {
+ dragView.setDragRegion(new Rect(dragRegion));
+ }
+
+ mActivity.getDragLayer().performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+ dragView.show(mLastTouch.x, mLastTouch.y);
+ mDistanceSinceScroll = 0;
+
+ if (!mIsInPreDrag) {
+ callOnDragStart();
+ } else if (mOptions.preDragCondition != null) {
+ mOptions.preDragCondition.onPreDragStart(mDragObject);
+ }
+
+ handleMoveEvent(mLastTouch.x, mLastTouch.y);
+ return dragView;
+ }
+
+ @Override
+ protected void exitDrag() { }
+
+ @Override
+ protected DropTarget getDefaultDropTarget(int[] dropCoordinates) {
+ DropTarget target = new DropTarget() {
+ @Override
+ public boolean isDropEnabled() {
+ return true;
+ }
+
+ @Override
+ public void onDrop(DragObject dragObject, DragOptions options) {
+ ((SecondaryDragLayer) mActivity.getDragLayer()).getPinnedAppsAdapter().addPinnedApp(
+ dragObject.dragInfo);
+ dragObject.dragView.remove();
+ }
+
+ @Override
+ public void onDragEnter(DragObject dragObject) {
+ if (getDistanceDragged() > mActivity.getResources().getDimensionPixelSize(
+ R.dimen.drag_distanceThreshold)) {
+ mActivity.showAppDrawer(false);
+ AbstractFloatingView.closeAllOpenViews(mActivity);
+ }
+ }
+
+ @Override
+ public void onDragOver(DragObject dragObject) { }
+
+ @Override
+ public void onDragExit(DragObject dragObject) { }
+
+ @Override
+ public boolean acceptDrop(DragObject dragObject) {
+ return true;
+ }
+
+ @Override
+ public void prepareAccessibilityDrop() { }
+
+ @Override
+ public void getHitRectRelativeToDragLayer(Rect outRect) { }
+ };
+ return target;
+ }
+}
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java b/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
index c79d70d..c8455b8 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
@@ -29,17 +29,23 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.DropTarget;
import com.android.launcher3.R;
import com.android.launcher3.allapps.ActivityAllAppsContainerView;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.popup.PopupDataProvider;
+import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.util.ShortcutUtil;
import com.android.launcher3.util.TouchController;
import com.android.launcher3.views.BaseDragLayer;
-import java.util.Arrays;
+import java.util.ArrayList;
import java.util.Collections;
+import java.util.List;
/**
* DragLayer for Secondary launcher
@@ -59,7 +65,8 @@
@Override
public void recreateControllers() {
- mControllers = new TouchController[] {new CloseAllAppsTouchController()};
+ mControllers = new TouchController[]{new CloseAllAppsTouchController(),
+ mActivity.getDragController()};
}
/**
@@ -72,7 +79,8 @@
mAppsView = findViewById(R.id.apps_view);
mAppsView.setOnIconLongClickListener(this::onIconLongClicked);
-
+ mActivity.getSecondaryDisplayPredictions()
+ .setLongClickListener(mAppsView, this::onIconLongClicked);
// Setup workspace
mWorkspace = findViewById(R.id.workspace_grid);
mPinnedAppsAdapter = new PinnedAppsAdapter(mActivity, mAppsView.getAppsStore(),
@@ -166,6 +174,10 @@
}
}
+ public PinnedAppsAdapter getPinnedAppsAdapter() {
+ return mPinnedAppsAdapter;
+ }
+
private boolean onIconLongClicked(View v) {
if (!(v instanceof BubbleTextView)) {
return false;
@@ -183,16 +195,58 @@
if (popupDataProvider == null) {
return false;
}
+
+ List<SystemShortcut> systemShortcuts = new ArrayList<>();
+
+ // Hide redundant pin shortcut for app drawer icons if drag-n-drop is enabled.
+ if (!FeatureFlags.SECONDARY_DRAG_N_DROP_TO_PIN.get() || !mActivity.isAppDrawerShown()) {
+ systemShortcuts.add(mPinnedAppsAdapter.getSystemShortcut(item, v));
+ }
+ systemShortcuts.add(APP_INFO.getShortcut(mActivity, item, v));
+
final PopupContainerWithArrow container =
(PopupContainerWithArrow) mActivity.getLayoutInflater().inflate(
R.layout.popup_container, mActivity.getDragLayer(), false);
container.populateAndShow((BubbleTextView) v,
popupDataProvider.getShortcutCountForItem(item),
- Collections.emptyList(),
- Arrays.asList(mPinnedAppsAdapter.getSystemShortcut(item, v),
- APP_INFO.getShortcut(mActivity, item, v)));
- v.getParent().requestDisallowInterceptTouchEvent(true);
+ Collections.emptyList(), systemShortcuts);
+ container.requestFocus();
+
+ if (!FeatureFlags.SECONDARY_DRAG_N_DROP_TO_PIN.get() || !mActivity.isAppDrawerShown()) {
+ return true;
+ }
+
+ DragOptions options = new DragOptions();
+ DeviceProfile grid = mActivity.getDeviceProfile();
+ options.intrinsicIconScaleFactor = (float) grid.allAppsIconSizePx / grid.iconSizePx;
+ options.preDragCondition = container.createPreDragCondition(false);
+ if (options.preDragCondition == null) {
+ options.preDragCondition = new DragOptions.PreDragCondition() {
+ private DragView<SecondaryDisplayLauncher> mDragView;
+
+ @Override
+ public boolean shouldStartDrag(double distanceDragged) {
+ return mDragView != null && mDragView.isAnimationFinished();
+ }
+
+ @Override
+ public void onPreDragStart(DropTarget.DragObject dragObject) {
+ mDragView = dragObject.dragView;
+ if (!shouldStartDrag(0)) {
+ mDragView.setOnAnimationEndCallback(() -> {
+ mActivity.beginDragShared(v, mActivity.getAppsView(), options);
+ });
+ }
+ }
+
+ @Override
+ public void onPreDragEnd(DropTarget.DragObject dragObject, boolean dragStarted) {
+ mDragView = null;
+ }
+ };
+ }
+ mActivity.beginDragShared(v, mActivity.getAppsView(), options);
return true;
}
}
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDragView.java b/src/com/android/launcher3/secondarydisplay/SecondaryDragView.java
new file mode 100644
index 0000000..0168b8f
--- /dev/null
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDragView.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.secondarydisplay;
+
+import android.graphics.drawable.Drawable;
+import android.view.View;
+
+import com.android.launcher3.R;
+import com.android.launcher3.dragndrop.DragView;
+
+/**
+ * A DragView drawn/used by the Secondary Launcher activity.
+ */
+public class SecondaryDragView extends DragView<SecondaryDisplayLauncher> {
+
+ public SecondaryDragView(SecondaryDisplayLauncher launcher,
+ Drawable drawable,
+ int registrationX, int registrationY, float initialScale, float scaleOnDrop,
+ float finalScaleDps) {
+ super(launcher, drawable, registrationX, registrationY, initialScale, scaleOnDrop,
+ finalScaleDps);
+ }
+
+ public SecondaryDragView(SecondaryDisplayLauncher launcher, View content, int width, int height,
+ int registrationX, int registrationY, float initialScale, float scaleOnDrop,
+ float finalScaleDps) {
+ super(launcher, content, width, height, registrationX, registrationY, initialScale,
+ scaleOnDrop, finalScaleDps);
+ }
+
+ @Override
+ public void animateTo(int toTouchX, int toTouchY, Runnable onCompleteRunnable, int duration) {
+ Runnable onAnimationEnd = () -> {
+ if (onCompleteRunnable != null) {
+ onCompleteRunnable.run();
+ }
+ mActivity.getDragLayer().removeView(this);
+ };
+
+ duration = Math.max(duration,
+ getResources().getInteger(R.integer.config_dropAnimMinDuration));
+
+ animate()
+ .translationX(toTouchX - mRegistrationX)
+ .translationY(toTouchY - mRegistrationY)
+ .scaleX(mScaleOnDrop)
+ .scaleY(mScaleOnDrop)
+ .withEndAction(onAnimationEnd)
+ .setDuration(duration)
+ .start();
+ }
+}
diff --git a/src/com/android/launcher3/settings/DeveloperOptionsFragment.java b/src/com/android/launcher3/settings/DeveloperOptionsFragment.java
index b06b8a1..6057586 100644
--- a/src/com/android/launcher3/settings/DeveloperOptionsFragment.java
+++ b/src/com/android/launcher3/settings/DeveloperOptionsFragment.java
@@ -63,6 +63,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.config.FlagTogglerPrefUi;
+import com.android.launcher3.secondarydisplay.SecondaryDisplayLauncher;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.launcher3.util.OnboardingPrefs;
@@ -369,6 +370,16 @@
return true;
});
sandboxCategory.addPreference(launchSandboxModeTutorialPreference);
+
+ Preference launchSecondaryDisplayPreference = new Preference(context);
+ launchSecondaryDisplayPreference.setKey("launchSecondaryDisplay");
+ launchSecondaryDisplayPreference.setTitle("Launch Secondary Display");
+ launchSecondaryDisplayPreference.setSummary("Launch secondary display activity");
+ launchSecondaryDisplayPreference.setOnPreferenceClickListener(preference -> {
+ startActivity(new Intent(context, SecondaryDisplayLauncher.class));
+ return true;
+ });
+ sandboxCategory.addPreference(launchSecondaryDisplayPreference);
}
private void addOnboardingPrefsCatergory() {
diff --git a/src/com/android/launcher3/settings/SettingsActivity.java b/src/com/android/launcher3/settings/SettingsActivity.java
index 49d27b7..70956a3 100644
--- a/src/com/android/launcher3/settings/SettingsActivity.java
+++ b/src/com/android/launcher3/settings/SettingsActivity.java
@@ -18,6 +18,7 @@
import static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS;
+import static com.android.launcher3.config.FeatureFlags.IS_STUDIO_BUILD;
import static com.android.launcher3.states.RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY;
import android.content.Intent;
@@ -207,7 +208,11 @@
PreferenceScreen screen = getPreferenceScreen();
for (int i = screen.getPreferenceCount() - 1; i >= 0; i--) {
Preference preference = screen.getPreference(i);
- if (!initPreference(preference)) {
+ if (initPreference(preference)) {
+ if (IS_STUDIO_BUILD && preference == mDeveloperOptionPref) {
+ preference.setOrder(0);
+ }
+ } else {
screen.removePreference(preference);
}
}
diff --git a/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java b/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java
index c166bfc..0306730 100644
--- a/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java
+++ b/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java
@@ -16,7 +16,6 @@
package com.android.launcher3.shortcuts;
-import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Point;
import android.graphics.Rect;
@@ -24,7 +23,6 @@
import android.view.View;
import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.graphics.DragPreviewProvider;
import com.android.launcher3.icons.BitmapRenderer;
import com.android.launcher3.icons.FastBitmapDrawable;
@@ -44,33 +42,13 @@
@Override
public Drawable createDrawable() {
- if (FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) {
- int size = ActivityContext.lookupContext(mView.getContext())
- .getDeviceProfile().iconSizePx;
- return new FastBitmapDrawable(
- BitmapRenderer.createHardwareBitmap(
- size + blurSizeOutline,
- size + blurSizeOutline,
- (c) -> drawDragViewOnBackground(c, size)));
- } else {
- return new FastBitmapDrawable(createDragBitmapLegacy());
- }
- }
-
- private Bitmap createDragBitmapLegacy() {
- Drawable d = mView.getBackground();
- Rect bounds = getDrawableBounds(d);
- int size = ActivityContext.lookupContext(mView.getContext()).getDeviceProfile().iconSizePx;
- final Bitmap b = Bitmap.createBitmap(
- size + blurSizeOutline,
- size + blurSizeOutline,
- Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(b);
- canvas.translate(blurSizeOutline / 2, blurSizeOutline / 2);
- canvas.scale(((float) size) / bounds.width(), ((float) size) / bounds.height(), 0, 0);
- canvas.translate(bounds.left, bounds.top);
- d.draw(canvas);
- return b;
+ int size = ActivityContext.lookupContext(mView.getContext())
+ .getDeviceProfile().iconSizePx;
+ return new FastBitmapDrawable(
+ BitmapRenderer.createHardwareBitmap(
+ size + blurSizeOutline,
+ size + blurSizeOutline,
+ (c) -> drawDragViewOnBackground(c, size)));
}
private void drawDragViewOnBackground(Canvas canvas, float size) {
diff --git a/src/com/android/launcher3/statemanager/BaseState.java b/src/com/android/launcher3/statemanager/BaseState.java
index f9a36ad..32378b8 100644
--- a/src/com/android/launcher3/statemanager/BaseState.java
+++ b/src/com/android/launcher3/statemanager/BaseState.java
@@ -63,4 +63,11 @@
default boolean displayOverviewTasksAsGrid(DeviceProfile deviceProfile) {
return false;
}
+
+ /**
+ * For this state, whether tasks should show the thumbnail splash.
+ */
+ default boolean showTaskThumbnailSplash() {
+ return false;
+ }
}
diff --git a/src/com/android/launcher3/statemanager/StateManager.java b/src/com/android/launcher3/statemanager/StateManager.java
index 2aa9dde..86277a7 100644
--- a/src/com/android/launcher3/statemanager/StateManager.java
+++ b/src/com/android/launcher3/statemanager/StateManager.java
@@ -342,6 +342,7 @@
public void onAnimationSuccess(Animator animator) {
onStateTransitionEnd(state);
}
+
};
}
diff --git a/src/com/android/launcher3/statemanager/StatefulActivity.java b/src/com/android/launcher3/statemanager/StatefulActivity.java
index 2a890c3..520f33c 100644
--- a/src/com/android/launcher3/statemanager/StatefulActivity.java
+++ b/src/com/android/launcher3/statemanager/StatefulActivity.java
@@ -231,4 +231,10 @@
* etc.)
*/
protected abstract void onHandleConfigurationChanged();
+
+ /**
+ * Enter staged split directly from the current running app.
+ * @param leftOrTop if the staged split will be positioned left or top.
+ */
+ public void enterStageSplitFromRunningApp(boolean leftOrTop) { }
}
diff --git a/src/com/android/launcher3/states/RotationHelper.java b/src/com/android/launcher3/states/RotationHelper.java
index fd8b2e5..b94ea07 100644
--- a/src/com/android/launcher3/states/RotationHelper.java
+++ b/src/com/android/launcher3/states/RotationHelper.java
@@ -21,17 +21,23 @@
import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE;
import static com.android.launcher3.Utilities.dpiFromPx;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.launcher3.util.window.WindowManagerProxy.MIN_TABLET_WIDTH;
+import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+import android.os.Handler;
+import android.os.Message;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.WorkerThread;
import com.android.launcher3.BaseActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.DisplayController;
-import com.android.launcher3.util.UiThreadHelper;
/**
* Utility class to manage launcher rotation
@@ -39,8 +45,6 @@
public class RotationHelper implements OnSharedPreferenceChangeListener,
DisplayController.DisplayInfoChangeListener {
- private static final String TAG = "RotationHelper";
-
public static final String ALLOW_ROTATION_PREFERENCE_KEY = "pref_allowRotation";
/**
@@ -58,8 +62,10 @@
public static final int REQUEST_ROTATE = 1;
public static final int REQUEST_LOCK = 2;
+ @Nullable
private BaseActivity mActivity;
private SharedPreferences mSharedPrefs = null;
+ private final Handler mRequestOrientationHandler;
private boolean mIgnoreAutoRotateSettings;
private boolean mForceAllowRotationForTesting;
@@ -89,6 +95,8 @@
public RotationHelper(BaseActivity activity) {
mActivity = activity;
+ mRequestOrientationHandler =
+ new Handler(UI_HELPER_EXECUTOR.getLooper(), this::setOrientationAsync);
}
private void setIgnoreAutoRotateSettings(boolean ignoreAutoRotateSettings) {
@@ -202,10 +210,19 @@
}
if (activityFlags != mLastActivityFlags) {
mLastActivityFlags = activityFlags;
- UiThreadHelper.setOrientationAsync(mActivity, activityFlags);
+ mRequestOrientationHandler.sendEmptyMessage(activityFlags);
}
}
+ @WorkerThread
+ private boolean setOrientationAsync(Message msg) {
+ Activity activity = mActivity;
+ if (activity != null) {
+ activity.setRequestedOrientation(msg.what);
+ }
+ return true;
+ }
+
/**
* @return how many factors {@param newRotation} is rotated 90 degrees clockwise.
* E.g. 1->Rotated by 90 degrees clockwise, 2->Rotated 180 clockwise...
diff --git a/src/com/android/launcher3/states/SpringLoadedState.java b/src/com/android/launcher3/states/SpringLoadedState.java
index b63715c..f5d511c 100644
--- a/src/com/android/launcher3/states/SpringLoadedState.java
+++ b/src/com/android/launcher3/states/SpringLoadedState.java
@@ -15,6 +15,7 @@
*/
package com.android.launcher3.states;
+import static com.android.launcher3.config.FeatureFlags.SHOW_HOME_GARDENING;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
import android.content.Context;
@@ -31,8 +32,7 @@
private static final int STATE_FLAGS = FLAG_MULTI_PAGE
| FLAG_WORKSPACE_INACCESSIBLE | FLAG_DISABLE_RESTORE
- | FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED | FLAG_WORKSPACE_HAS_BACKGROUNDS
- | FLAG_HIDE_BACK_BUTTON;
+ | FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED | FLAG_WORKSPACE_HAS_BACKGROUNDS;
public SpringLoadedState(int id) {
super(id, LAUNCHER_STATE_HOME, STATE_FLAGS);
@@ -45,6 +45,11 @@
@Override
public ScaleAndTranslation getWorkspaceScaleAndTranslation(Launcher launcher) {
+
+ if (SHOW_HOME_GARDENING.get()) {
+ return super.getWorkspaceScaleAndTranslation(launcher);
+ }
+
DeviceProfile grid = launcher.getDeviceProfile();
Workspace<?> ws = launcher.getWorkspace();
if (ws.getChildCount() == 0) {
@@ -63,6 +68,9 @@
@Override
protected float getDepthUnchecked(Context context) {
+ if (SHOW_HOME_GARDENING.get()) {
+ return 0;
+ }
return 0.5f;
}
@@ -73,6 +81,10 @@
@Override
public float getWorkspaceBackgroundAlpha(Launcher launcher) {
+ if (SHOW_HOME_GARDENING.get()) {
+ return 0;
+ }
+
return 0.2f;
}
}
diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java
index d3c9bc9..acb7eb3 100644
--- a/src/com/android/launcher3/testing/TestInformationHandler.java
+++ b/src/com/android/launcher3/testing/TestInformationHandler.java
@@ -47,7 +47,9 @@
import com.android.launcher3.util.ResourceBasedOverride;
import com.android.launcher3.widget.picker.WidgetsFullSheet;
+import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
import java.util.function.Function;
import java.util.function.Supplier;
@@ -112,12 +114,12 @@
case TestProtocol.REQUEST_APPS_LIST_SCROLL_Y: {
return getLauncherUIProperty(Bundle::putInt,
- l -> l.getAppsView().getActiveRecyclerView().getCurrentScrollY());
+ l -> l.getAppsView().getActiveRecyclerView().computeVerticalScrollOffset());
}
case TestProtocol.REQUEST_WIDGETS_SCROLL_Y: {
return getLauncherUIProperty(Bundle::putInt,
- l -> WidgetsFullSheet.getWidgetsView(l).getCurrentScrollY());
+ l -> WidgetsFullSheet.getWidgetsView(l).computeVerticalScrollOffset());
}
case TestProtocol.REQUEST_TARGET_INSETS: {
@@ -214,11 +216,15 @@
}
case TestProtocol.REQUEST_HAS_TIS: {
- response.putBoolean(
- TestProtocol.REQUEST_HAS_TIS, false);
+ response.putBoolean(TestProtocol.REQUEST_HAS_TIS, false);
return response;
}
+ case TestProtocol.REQUEST_ALL_APPS_TOP_PADDING: {
+ return getLauncherUIProperty(Bundle::putInt,
+ l -> l.getAppsView().getActiveRecyclerView().getClipBounds().top);
+ }
+
default:
return null;
}
@@ -261,17 +267,24 @@
*/
private static <S, T> Bundle getUIProperty(
BundleSetter<T> bundleSetter, Function<S, T> provider, Supplier<S> targetSupplier) {
+ return getFromExecutorSync(MAIN_EXECUTOR, () -> {
+ S target = targetSupplier.get();
+ if (target == null) {
+ return null;
+ }
+ T value = provider.apply(target);
+ Bundle response = new Bundle();
+ bundleSetter.set(response, TestProtocol.TEST_INFO_RESPONSE_FIELD, value);
+ return response;
+ });
+ }
+
+ /**
+ * Executes the callback on the executor and waits for the result
+ */
+ protected static <T> T getFromExecutorSync(ExecutorService executor, Callable<T> callback) {
try {
- return MAIN_EXECUTOR.submit(() -> {
- S target = targetSupplier.get();
- if (target == null) {
- return null;
- }
- T value = provider.apply(target);
- Bundle response = new Bundle();
- bundleSetter.set(response, TestProtocol.TEST_INFO_RESPONSE_FIELD, value);
- return response;
- }).get();
+ return executor.submit(callback).get();
} catch (ExecutionException | InterruptedException e) {
throw new RuntimeException(e);
}
diff --git a/src/com/android/launcher3/testing/TestInformationProvider.java b/src/com/android/launcher3/testing/TestInformationProvider.java
index bcc7c2d..5444d92 100644
--- a/src/com/android/launcher3/testing/TestInformationProvider.java
+++ b/src/com/android/launcher3/testing/TestInformationProvider.java
@@ -21,10 +21,14 @@
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
+import android.util.Log;
import com.android.launcher3.Utilities;
public class TestInformationProvider extends ContentProvider {
+
+ private static final String TAG = "TestInformationProvider";
+
@Override
public boolean onCreate() {
return true;
@@ -60,7 +64,13 @@
if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
TestInformationHandler handler = TestInformationHandler.newInstance(getContext());
handler.init(getContext());
- return handler.call(method, arg, extras);
+
+ Bundle response = handler.call(method, arg, extras);
+ if (response == null) {
+ Log.e(TAG, "Couldn't handle method: " + method + "; current handler="
+ + handler.getClass().getSimpleName());
+ }
+ return response;
}
return null;
}
diff --git a/src/com/android/launcher3/testing/shared/ResourceUtils.java b/src/com/android/launcher3/testing/shared/ResourceUtils.java
index 551aeaf..d0ae258 100644
--- a/src/com/android/launcher3/testing/shared/ResourceUtils.java
+++ b/src/com/android/launcher3/testing/shared/ResourceUtils.java
@@ -36,6 +36,8 @@
public static final String STATUS_BAR_HEIGHT_LANDSCAPE = "status_bar_height_landscape";
public static final String STATUS_BAR_HEIGHT_PORTRAIT = "status_bar_height_portrait";
+ public static final String NAV_BAR_INTERACTION_MODE_RES_NAME = "config_navBarInteractionMode";
+
public static int getNavbarSize(String resName, Resources res) {
return getDimenByName(resName, res, DEFAULT_NAVBAR_VALUE);
}
diff --git a/src/com/android/launcher3/testing/shared/TestProtocol.java b/src/com/android/launcher3/testing/shared/TestProtocol.java
index 67efb58..46e5891 100644
--- a/src/com/android/launcher3/testing/shared/TestProtocol.java
+++ b/src/com/android/launcher3/testing/shared/TestProtocol.java
@@ -84,8 +84,13 @@
public static final String REQUEST_UNFREEZE_APP_LIST = "unfreeze-app-list";
public static final String REQUEST_ENABLE_MANUAL_TASKBAR_STASHING = "enable-taskbar-stashing";
public static final String REQUEST_DISABLE_MANUAL_TASKBAR_STASHING = "disable-taskbar-stashing";
+ public static final String REQUEST_ENABLE_BLOCK_TIMEOUT = "enable-block-timeout";
+ public static final String REQUEST_DISABLE_BLOCK_TIMEOUT = "disable-block-timeout";
+ public static final String REQUEST_ENABLE_TRANSIENT_TASKBAR = "enable-transient-taskbar";
+ public static final String REQUEST_DISABLE_TRANSIENT_TASKBAR = "disable-transient-taskbar";
public static final String REQUEST_UNSTASH_TASKBAR_IF_STASHED = "unstash-taskbar-if-stashed";
public static final String REQUEST_STASHED_TASKBAR_HEIGHT = "stashed-taskbar-height";
+ public static final String REQUEST_RECREATE_TASKBAR = "recreate-taskbar";
public static final String REQUEST_APP_LIST_FREEZE_FLAGS = "app-list-freeze-flags";
public static final String REQUEST_APPS_LIST_SCROLL_Y = "apps-list-scroll-y";
public static final String REQUEST_WIDGETS_SCROLL_Y = "widgets-scroll-y";
@@ -111,6 +116,9 @@
"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_TASKBAR_ALL_APPS_TOP_PADDING =
+ "taskbar-all-apps-top-padding";
+ public static final String REQUEST_ALL_APPS_TOP_PADDING = "all-apps-top-padding";
public static final String REQUEST_WORKSPACE_CELL_LAYOUT_SIZE = "workspace-cell-layout-size";
public static final String REQUEST_WORKSPACE_CELL_CENTER = "workspace-cell-center";
@@ -123,6 +131,8 @@
"get-grid-task-size-rect-for-tablet";
public static final String REQUEST_GET_OVERVIEW_PAGE_SPACING = "get-overview-page-spacing";
public static final String REQUEST_ENABLE_ROTATION = "enable_rotation";
+ public static final String REQUEST_ENABLE_SUGGESTION = "enable-suggestion";
+ public static final String REQUEST_MODEL_QUEUE_CLEARED = "model-queue-cleared";
public static boolean sDebugTracing = false;
public static final String REQUEST_ENABLE_DEBUG_TRACING = "enable-debug-tracing";
@@ -138,6 +148,7 @@
public static final String MISSING_PROMISE_ICON = "b/202985412";
public static final String TASKBAR_IN_APP_STATE = "b/227657604";
public static final String INCORRECT_INFO_UPDATED = "b/239465630";
+ public static final String NPE_TRANSIENT_TASKBAR = "b/257549303";
public static final String REQUEST_EMULATE_DISPLAY = "emulate-display";
public static final String REQUEST_STOP_EMULATE_DISPLAY = "stop-emulate-display";
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index 09b8228..8f7a4ec 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -32,6 +32,7 @@
import android.animation.Animator.AnimatorListener;
import android.animation.ValueAnimator;
+import android.util.Log;
import android.view.MotionEvent;
import com.android.launcher3.Launcher;
@@ -211,6 +212,10 @@
mFlingBlockCheck.blockFling();
}
}
+ if (mFromState == LauncherState.ALL_APPS) {
+ mAllAppsOvershootStarted = true;
+ mLauncher.getAppsView().onPull(-progress , -progress);
+ }
} else if (progress >= 1) {
if (reinitCurrentAnimation(true, isDragTowardPositive)) {
mDisplacementShift = displacement;
diff --git a/src/com/android/launcher3/touch/AllAppsSwipeController.java b/src/com/android/launcher3/touch/AllAppsSwipeController.java
index 37b76fb..5279dec 100644
--- a/src/com/android/launcher3/touch/AllAppsSwipeController.java
+++ b/src/com/android/launcher3/touch/AllAppsSwipeController.java
@@ -18,6 +18,7 @@
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.anim.Interpolators.DECELERATED_EASE;
+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;
@@ -199,8 +200,10 @@
Interpolators.reverse(ALL_APPS_SCRIM_RESPONDER));
config.setInterpolator(ANIM_ALL_APPS_FADE, FINAL_FRAME);
if (!config.userControlled) {
- config.setInterpolator(ANIM_VERTICAL_PROGRESS, EMPHASIZED_ACCELERATE);
+ config.setInterpolator(ANIM_VERTICAL_PROGRESS, EMPHASIZED);
}
+ config.setInterpolator(ANIM_WORKSPACE_SCALE, EMPHASIZED);
+ config.setInterpolator(ANIM_DEPTH, EMPHASIZED);
} else {
if (config.userControlled) {
config.setInterpolator(ANIM_DEPTH, Interpolators.reverse(BLUR_MANUAL));
@@ -238,8 +241,10 @@
config.setInterpolator(ANIM_ALL_APPS_FADE, INSTANT);
config.setInterpolator(ANIM_SCRIM_FADE, ALL_APPS_SCRIM_RESPONDER);
if (!config.userControlled) {
- config.setInterpolator(ANIM_VERTICAL_PROGRESS, EMPHASIZED_DECELERATE);
+ config.setInterpolator(ANIM_VERTICAL_PROGRESS, EMPHASIZED);
}
+ config.setInterpolator(ANIM_WORKSPACE_SCALE, EMPHASIZED);
+ config.setInterpolator(ANIM_DEPTH, EMPHASIZED);
} else {
config.setInterpolator(ANIM_DEPTH, config.userControlled ? BLUR_MANUAL : BLUR_ATOMIC);
config.setInterpolator(ANIM_WORKSPACE_FADE,
diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java
index 5fa30bc..b4be061 100644
--- a/src/com/android/launcher3/touch/ItemClickHandler.java
+++ b/src/com/android/launcher3/touch/ItemClickHandler.java
@@ -248,13 +248,19 @@
final Launcher launcher = Launcher.getLauncher(context);
if (shortcut.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
&& shortcut.isDisabledVersionLower()) {
+ final Intent marketIntent = shortcut.getMarketIntent(context);
+ // No market intent means no target package for the shortcut, which should be an
+ // issue. Falling back to showing toast messages.
+ if (marketIntent == null) {
+ return false;
+ }
new AlertDialog.Builder(context)
.setTitle(R.string.dialog_update_title)
.setMessage(R.string.dialog_update_message)
.setPositiveButton(R.string.dialog_update, (d, i) -> {
// Direct the user to the play store to update the app
- context.startActivity(shortcut.getMarketIntent(context));
+ context.startActivity(marketIntent);
})
.setNeutralButton(R.string.dialog_remove, (d, i) -> {
// Remove the icon if launcher is successfully initialized
diff --git a/src/com/android/launcher3/touch/ItemLongClickListener.java b/src/com/android/launcher3/touch/ItemLongClickListener.java
index 73f994f..1421ece 100644
--- a/src/com/android/launcher3/touch/ItemLongClickListener.java
+++ b/src/com/android/launcher3/touch/ItemLongClickListener.java
@@ -27,7 +27,6 @@
import android.view.View.OnLongClickListener;
import com.android.launcher3.CellLayout;
-import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DropTarget;
import com.android.launcher3.Launcher;
import com.android.launcher3.dragndrop.DragController;
@@ -118,10 +117,7 @@
}
});
- DeviceProfile grid = launcher.getDeviceProfile();
- DragOptions options = new DragOptions();
- options.intrinsicIconScaleFactor = (float) grid.allAppsIconSizePx / grid.iconSizePx;
- launcher.getWorkspace().beginDragShared(v, launcher.getAppsView(), options);
+ launcher.getWorkspace().beginDragShared(v, launcher.getAppsView(), new DragOptions());
return false;
}
diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
index 2a0fe3a..820162c 100644
--- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
@@ -30,6 +30,7 @@
import static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_TYPE_MAIN;
import android.content.res.Resources;
@@ -265,20 +266,32 @@
}
@Override
- public float getTaskMenuX(float x, View thumbnailView, int overScroll,
- DeviceProfile deviceProfile) {
- return thumbnailView.getMeasuredWidth() + x;
+ public float getTaskMenuX(float x, View thumbnailView,
+ DeviceProfile deviceProfile, float taskInsetMargin) {
+ return thumbnailView.getMeasuredWidth() + x - taskInsetMargin;
}
@Override
- public float getTaskMenuY(float y, View thumbnailView, int overScroll) {
- return y + overScroll +
- (thumbnailView.getMeasuredHeight() - thumbnailView.getMeasuredWidth()) / 2f;
+ public float getTaskMenuY(float y, View thumbnailView, int stagePosition,
+ View taskMenuView, float taskInsetMargin) {
+ BaseDragLayer.LayoutParams lp = (BaseDragLayer.LayoutParams) taskMenuView.getLayoutParams();
+ int taskMenuWidth = lp.width;
+ if (stagePosition == STAGE_POSITION_UNDEFINED) {
+ return y + taskInsetMargin
+ + (thumbnailView.getMeasuredHeight() - taskMenuWidth) / 2f;
+ } else {
+ return y + taskInsetMargin;
+ }
}
@Override
- public int getTaskMenuWidth(View view, DeviceProfile deviceProfile) {
- return view.getMeasuredWidth();
+ public int getTaskMenuWidth(View thumbnailView, DeviceProfile deviceProfile,
+ @StagePosition int stagePosition) {
+ if (stagePosition == SplitConfigurationOptions.STAGE_POSITION_UNDEFINED) {
+ return thumbnailView.getMeasuredWidth();
+ } else {
+ return thumbnailView.getMeasuredHeight();
+ }
}
@Override
@@ -300,17 +313,6 @@
}
@Override
- public void setTaskMenuAroundTaskView(LinearLayout taskView, float margin) {
- BaseDragLayer.LayoutParams lp = (BaseDragLayer.LayoutParams) taskView.getLayoutParams();
- lp.topMargin += margin;
- }
-
- @Override
- public PointF getAdditionalInsetForTaskMenu(float margin) {
- return new PointF(margin, 0);
- }
-
- @Override
public Pair<Float, Float> getDwbLayoutTranslations(int taskViewWidth,
int taskViewHeight, SplitBounds splitBounds, DeviceProfile deviceProfile,
View[] thumbnailViews, int desiredTaskId, View banner) {
@@ -411,8 +413,8 @@
// In fake land/seascape, the placeholder always needs to go to the "top" of the device,
// which is the same bounds as 0 rotation.
int width = dp.widthPx;
- int insetThickness = dp.getInsets().top;
- out.set(0, 0, width, placeholderHeight + insetThickness);
+ int insetSizeAdjustment = getPlaceholderSizeAdjustment(dp);
+ out.set(0, 0, width, placeholderHeight + insetSizeAdjustment);
out.inset(placeholderInset, 0);
// Adjust the top to account for content off screen. This will help to animate the view in
@@ -429,13 +431,21 @@
float onScreenRectCenterY, float fullscreenScaleX, float fullscreenScaleY,
int drawableWidth, int drawableHeight, DeviceProfile dp,
@StagePosition int stagePosition) {
- float inset = dp.getInsets().top;
+ float insetAdjustment = getPlaceholderSizeAdjustment(dp) / 2f;
out.setX(Math.round(onScreenRectCenterX / fullscreenScaleX
- 1.0f * drawableWidth / 2));
- out.setY(Math.round((onScreenRectCenterY + (inset / 2f)) / fullscreenScaleY
+ out.setY(Math.round((onScreenRectCenterY + insetAdjustment) / fullscreenScaleY
- 1.0f * drawableHeight / 2));
}
+ /**
+ * The split placeholder comes with a default inset to buffer the icon from the top of the
+ * screen. But if the device already has a large inset (from cutouts etc), use that instead.
+ */
+ private int getPlaceholderSizeAdjustment(DeviceProfile dp) {
+ return Math.max(dp.getInsets().top - dp.splitPlaceholderInset, 0);
+ }
+
@Override
public void setSplitInstructionsParams(View out, DeviceProfile dp, int splitInstructionsHeight,
int splitInstructionsWidth, int threeButtonNavShift) {
@@ -491,9 +501,9 @@
DeviceProfile dp, boolean isRtl) {
int spaceAboveSnapshot = dp.overviewTaskThumbnailTopMarginPx;
int totalThumbnailHeight = parentHeight - spaceAboveSnapshot;
- int dividerBar = splitBoundsConfig.appsStackedVertically
- ? splitBoundsConfig.visualDividerBounds.height()
- : splitBoundsConfig.visualDividerBounds.width();
+ int dividerBar = Math.round(totalThumbnailHeight * (splitBoundsConfig.appsStackedVertically
+ ? splitBoundsConfig.dividerHeightPercent
+ : splitBoundsConfig.dividerWidthPercent));
int primarySnapshotHeight;
int primarySnapshotWidth;
int secondarySnapshotHeight;
diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java
index 1a8d355..6234462 100644
--- a/src/com/android/launcher3/touch/PagedOrientationHandler.java
+++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java
@@ -184,9 +184,12 @@
* taskMenu width is the same size as the thumbnail width (what got set below in
* getTaskMenuWidth()), so we directly use that in the calculations.
*/
- float getTaskMenuX(float x, View thumbnailView, int overScroll, DeviceProfile deviceProfile);
- float getTaskMenuY(float y, View thumbnailView, int overScroll);
- int getTaskMenuWidth(View view, DeviceProfile deviceProfile);
+ float getTaskMenuX(float x, View thumbnailView, DeviceProfile deviceProfile,
+ float taskInsetMargin);
+ float getTaskMenuY(float y, View thumbnailView, int stagePosition,
+ View taskMenuView, float taskInsetMargin);
+ int getTaskMenuWidth(View thumbnailView, DeviceProfile deviceProfile,
+ @StagePosition int stagePosition);
/**
* Sets linear layout orientation for {@link com.android.launcher3.popup.SystemShortcut} items
* inside task menu view.
@@ -200,16 +203,6 @@
*/
void setLayoutParamsForTaskMenuOptionItem(LinearLayout.LayoutParams lp,
LinearLayout viewGroup, DeviceProfile deviceProfile);
- /**
- * Adjusts margins for the entire task menu view itself, which comprises of both app title and
- * shortcut options.
- */
- void setTaskMenuAroundTaskView(LinearLayout taskView, float margin);
- /**
- * Since the task menu layout is manually positioned on top of recents view, this method returns
- * additional adjustments to the positioning based on fake land/seascape
- */
- PointF getAdditionalInsetForTaskMenu(float margin);
/**
* Calculates the position where a Digital Wellbeing Banner should be placed on its parent
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index f89c0e5..78e17d8 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -27,7 +27,7 @@
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
import static com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL;
-import static com.android.launcher3.util.DisplayController.NavigationMode.THREE_BUTTONS;
+import static com.android.launcher3.util.NavigationMode.THREE_BUTTONS;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
@@ -50,12 +50,12 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds;
import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
-import com.android.launcher3.views.BaseDragLayer;
import java.util.List;
@@ -264,26 +264,28 @@
}
@Override
- public float getTaskMenuX(float x, View thumbnailView, int overScroll,
- DeviceProfile deviceProfile) {
+ public float getTaskMenuX(float x, View thumbnailView,
+ DeviceProfile deviceProfile, float taskInsetMargin) {
if (deviceProfile.isLandscape) {
- return x + overScroll
+ return x + taskInsetMargin
+ (thumbnailView.getMeasuredWidth() - thumbnailView.getMeasuredHeight()) / 2f;
} else {
- return x + overScroll;
+ return x + taskInsetMargin;
}
}
@Override
- public float getTaskMenuY(float y, View thumbnailView, int overScroll) {
- return y;
+ public float getTaskMenuY(float y, View thumbnailView, int stagePosition,
+ View taskMenuView, float taskInsetMargin) {
+ return y + taskInsetMargin;
}
@Override
- public int getTaskMenuWidth(View view, DeviceProfile deviceProfile) {
+ public int getTaskMenuWidth(View thumbnailView, DeviceProfile deviceProfile,
+ @StagePosition int stagePosition) {
return deviceProfile.isLandscape && !deviceProfile.isTablet
- ? view.getMeasuredHeight()
- : view.getMeasuredWidth();
+ ? thumbnailView.getMeasuredHeight()
+ : thumbnailView.getMeasuredWidth();
}
@Override
@@ -304,18 +306,6 @@
}
@Override
- public void setTaskMenuAroundTaskView(LinearLayout taskView, float margin) {
- BaseDragLayer.LayoutParams lp = (BaseDragLayer.LayoutParams) taskView.getLayoutParams();
- lp.topMargin += margin;
- lp.leftMargin += margin;
- }
-
- @Override
- public PointF getAdditionalInsetForTaskMenu(float margin) {
- return new PointF(0, 0);
- }
-
- @Override
public Pair<Float, Float> getDwbLayoutTranslations(int taskViewWidth,
int taskViewHeight, SplitBounds splitBounds, DeviceProfile deviceProfile,
View[] thumbnailViews, int desiredTaskId, View banner) {
@@ -424,13 +414,9 @@
int screenWidth = dp.widthPx;
int screenHeight = dp.heightPx;
boolean pinToRight = stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT;
- int insetThickness;
- if (!dp.isLandscape) {
- insetThickness = dp.getInsets().top;
- } else {
- insetThickness = pinToRight ? dp.getInsets().right : dp.getInsets().left;
- }
- out.set(0, 0, screenWidth, placeholderHeight + insetThickness);
+ int insetSizeAdjustment = getPlaceholderSizeAdjustment(dp, pinToRight);
+
+ out.set(0, 0, screenWidth, placeholderHeight + insetSizeAdjustment);
if (!dp.isLandscape) {
// portrait, phone or tablet - spans width of screen, nothing else to do
out.inset(placeholderInset, 0);
@@ -475,20 +461,18 @@
int drawableWidth, int drawableHeight, DeviceProfile dp,
@StagePosition int stagePosition) {
boolean pinToRight = stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT;
+ float insetAdjustment = getPlaceholderSizeAdjustment(dp, pinToRight) / 2f;
if (!dp.isLandscape) {
- float inset = dp.getInsets().top;
out.setX(Math.round(onScreenRectCenterX / fullscreenScaleX
- 1.0f * drawableWidth / 2));
- out.setY(Math.round((onScreenRectCenterY + (inset / 2f)) / fullscreenScaleY
+ out.setY(Math.round((onScreenRectCenterY + insetAdjustment) / fullscreenScaleY
- 1.0f * drawableHeight / 2));
} else {
if (pinToRight) {
- float inset = dp.getInsets().right;
- out.setX(Math.round((onScreenRectCenterX - (inset / 2f)) / fullscreenScaleX
+ out.setX(Math.round((onScreenRectCenterX - insetAdjustment) / fullscreenScaleX
- 1.0f * drawableWidth / 2));
} else {
- float inset = dp.getInsets().left;
- out.setX(Math.round((onScreenRectCenterX + (inset / 2f)) / fullscreenScaleX
+ out.setX(Math.round((onScreenRectCenterX + insetAdjustment) / fullscreenScaleX
- 1.0f * drawableWidth / 2));
}
out.setY(Math.round(onScreenRectCenterY / fullscreenScaleY
@@ -496,6 +480,20 @@
}
}
+ /**
+ * The split placeholder comes with a default inset to buffer the icon from the top of the
+ * screen. But if the device already has a large inset (from cutouts etc), use that instead.
+ */
+ private int getPlaceholderSizeAdjustment(DeviceProfile dp, boolean pinToRight) {
+ int insetThickness;
+ if (!dp.isLandscape) {
+ insetThickness = dp.getInsets().top;
+ } else {
+ insetThickness = pinToRight ? dp.getInsets().right : dp.getInsets().left;
+ }
+ return Math.max(insetThickness - dp.splitPlaceholderInset, 0);
+ }
+
@Override
public void setSplitInstructionsParams(View out, DeviceProfile dp, int splitInstructionsHeight,
int splitInstructionsWidth, int threeButtonNavShift) {
@@ -504,7 +502,9 @@
out.setRotation(getDegreesRotated());
int distanceToEdge;
if ((DisplayController.getNavigationMode(out.getContext()) == THREE_BUTTONS)
- && (dp.isTwoPanels || dp.isTablet)) {
+ && (dp.isTwoPanels || dp.isTablet)
+ // If taskbar is in overview, overview action has dedicated space above nav buttons
+ && !FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get()) {
// If 3-button nav is active, align the splitInstructionsView with it.
distanceToEdge = dp.getTaskbarOffsetY()
+ ((dp.taskbarSize - splitInstructionsHeight) / 2);
@@ -541,8 +541,12 @@
int insetCorrectionX = (dp.getInsets().right - dp.getInsets().left) / 2;
// Adjust for any insets on the bottom edge
int insetCorrectionY = dp.getInsets().bottom;
+ // Adjust for taskbar in overview
+ int taskbarCorrectionY =
+ dp.isTaskbarPresent && FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get()
+ ? dp.taskbarSize : 0;
out.setTranslationX(insetCorrectionX + threeButtonNavShift);
- out.setTranslationY(-distanceToEdge + insetCorrectionY);
+ out.setTranslationY(-distanceToEdge + insetCorrectionY - taskbarCorrectionY);
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) out.getLayoutParams();
lp.gravity = CENTER_HORIZONTAL | BOTTOM;
out.setLayoutParams(lp);
@@ -581,7 +585,6 @@
@Override
public void setSplitTaskSwipeRect(DeviceProfile dp, Rect outRect,
SplitBounds splitInfo, int desiredStagePosition) {
- boolean isLandscape = dp.isLandscape;
float topLeftTaskPercent = splitInfo.appsStackedVertically
? splitInfo.topTaskPercent
: splitInfo.leftTaskPercent;
@@ -589,17 +592,25 @@
? splitInfo.dividerHeightPercent
: splitInfo.dividerWidthPercent;
+ int deviceHeightWithoutTaskbar = dp.availableHeightPx - dp.taskbarSize;
+ float scale = (float) outRect.height() / deviceHeightWithoutTaskbar;
+ float topTaskHeight = dp.availableHeightPx * topLeftTaskPercent;
+ float scaledTopTaskHeight = topTaskHeight * scale;
+ float dividerHeight = dp.availableHeightPx * dividerBarPercent;
+ float scaledDividerHeight = dividerHeight * scale;
+
if (desiredStagePosition == SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT) {
- if (isLandscape) {
- outRect.right = outRect.left + (int) (outRect.width() * topLeftTaskPercent);
+ if (splitInfo.appsStackedVertically) {
+ outRect.bottom = Math.round(outRect.top + scaledTopTaskHeight);
} else {
- outRect.bottom = outRect.top + (int) (outRect.height() * topLeftTaskPercent);
+ outRect.right = outRect.left + Math.round(outRect.width() * topLeftTaskPercent);
}
} else {
- if (isLandscape) {
- outRect.left += (int) (outRect.width() * (topLeftTaskPercent + dividerBarPercent));
+ if (splitInfo.appsStackedVertically) {
+ outRect.top += Math.round(scaledTopTaskHeight + scaledDividerHeight);
} else {
- outRect.top += (int) (outRect.height() * (topLeftTaskPercent + dividerBarPercent));
+ outRect.left += Math.round(outRect.width()
+ * (topLeftTaskPercent + dividerBarPercent));
}
}
}
@@ -610,9 +621,9 @@
DeviceProfile dp, boolean isRtl) {
int spaceAboveSnapshot = dp.overviewTaskThumbnailTopMarginPx;
int totalThumbnailHeight = parentHeight - spaceAboveSnapshot;
- int dividerBar = splitBoundsConfig.appsStackedVertically
- ? (int) (splitBoundsConfig.dividerHeightPercent * parentHeight)
- : (int) (splitBoundsConfig.dividerWidthPercent * parentWidth);
+ int dividerBar = Math.round(splitBoundsConfig.appsStackedVertically
+ ? splitBoundsConfig.dividerHeightPercent * dp.availableHeightPx
+ : splitBoundsConfig.dividerWidthPercent * parentWidth);
int primarySnapshotHeight;
int primarySnapshotWidth;
int secondarySnapshotHeight;
@@ -621,7 +632,7 @@
splitBoundsConfig.topTaskPercent : splitBoundsConfig.leftTaskPercent;
if (dp.isLandscape) {
primarySnapshotHeight = totalThumbnailHeight;
- primarySnapshotWidth = (int) (parentWidth * taskPercent);
+ primarySnapshotWidth = Math.round(parentWidth * taskPercent);
secondarySnapshotHeight = totalThumbnailHeight;
secondarySnapshotWidth = parentWidth - primarySnapshotWidth - dividerBar;
@@ -635,13 +646,29 @@
}
secondarySnapshot.setTranslationY(spaceAboveSnapshot);
} else {
+ int deviceHeightWithoutTaskbar = dp.availableHeightPx - dp.taskbarSize;
+ float scale = (float) totalThumbnailHeight / deviceHeightWithoutTaskbar;
+ float topTaskHeight = dp.availableHeightPx * taskPercent;
+ float finalDividerHeight = dividerBar * scale;
+ float scaledTopTaskHeight = topTaskHeight * scale;
primarySnapshotWidth = parentWidth;
- primarySnapshotHeight = (int) (totalThumbnailHeight * taskPercent);
+ primarySnapshotHeight = Math.round(scaledTopTaskHeight);
secondarySnapshotWidth = parentWidth;
- secondarySnapshotHeight = totalThumbnailHeight - primarySnapshotHeight - dividerBar;
- int translationY = primarySnapshotHeight + spaceAboveSnapshot + dividerBar;
+ secondarySnapshotHeight = Math.round(totalThumbnailHeight - primarySnapshotHeight
+ - finalDividerHeight);
+ float translationY = primarySnapshotHeight + spaceAboveSnapshot + finalDividerHeight;
secondarySnapshot.setTranslationY(translationY);
+
+ FrameLayout.LayoutParams primaryParams =
+ (FrameLayout.LayoutParams) primarySnapshot.getLayoutParams();
+ FrameLayout.LayoutParams secondaryParams =
+ (FrameLayout.LayoutParams) secondarySnapshot.getLayoutParams();
+ secondaryParams.topMargin = 0;
+ primaryParams.topMargin = spaceAboveSnapshot;
+
+ // Reset unused translations
+ primarySnapshot.setTranslationY(0);
secondarySnapshot.setTranslationX(0);
primarySnapshot.setTranslationX(0);
}
diff --git a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
index 55bb5e8..05683bd 100644
--- a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
@@ -24,6 +24,7 @@
import static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL;
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.STAGE_TYPE_MAIN;
import android.content.res.Resources;
@@ -33,7 +34,6 @@
import android.view.Surface;
import android.view.View;
import android.widget.FrameLayout;
-import android.widget.LinearLayout;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
@@ -85,26 +85,22 @@
}
@Override
- public float getTaskMenuX(float x, View thumbnailView, int overScroll,
- DeviceProfile deviceProfile) {
- return x;
+ public float getTaskMenuX(float x, View thumbnailView,
+ DeviceProfile deviceProfile, float taskInsetMargin) {
+ return x + taskInsetMargin;
}
@Override
- public float getTaskMenuY(float y, View thumbnailView, int overScroll) {
- return y + overScroll +
- (thumbnailView.getMeasuredHeight() + thumbnailView.getMeasuredWidth()) / 2f;
- }
-
- @Override
- public void setTaskMenuAroundTaskView(LinearLayout taskView, float margin) {
- BaseDragLayer.LayoutParams lp = (BaseDragLayer.LayoutParams) taskView.getLayoutParams();
- lp.bottomMargin += margin;
- }
-
- @Override
- public PointF getAdditionalInsetForTaskMenu(float margin) {
- return new PointF(-margin, margin);
+ public float getTaskMenuY(float y, View thumbnailView, int stagePosition,
+ View taskMenuView, float taskInsetMargin) {
+ BaseDragLayer.LayoutParams lp = (BaseDragLayer.LayoutParams) taskMenuView.getLayoutParams();
+ int taskMenuWidth = lp.width;
+ if (stagePosition == STAGE_POSITION_UNDEFINED) {
+ return y + taskInsetMargin
+ + (thumbnailView.getMeasuredHeight() + taskMenuWidth) / 2f;
+ } else {
+ return y + taskMenuWidth + taskInsetMargin;
+ }
}
@Override
@@ -121,9 +117,9 @@
// the screen. This is to preserve consistency when the user rotates: From the user's POV,
// the primary should always be on the left.
if (desiredStagePosition == SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT) {
- outRect.top += (int) (outRect.height() * (topLeftTaskPercent + dividerBarPercent));
+ outRect.top += (int) (outRect.height() * ((1 - topLeftTaskPercent)));
} else {
- outRect.bottom = outRect.top + (int) (outRect.height() * topLeftTaskPercent);
+ outRect.bottom -= (int) (outRect.height() * (topLeftTaskPercent + dividerBarPercent));
}
}
@@ -266,6 +262,49 @@
secondaryIconView.setLayoutParams(secondaryIconParams);
}
+ @Override
+ public void measureGroupedTaskViewThumbnailBounds(View primarySnapshot, View secondarySnapshot,
+ int parentWidth, int parentHeight, SplitBounds splitBoundsConfig, DeviceProfile dp,
+ boolean isRtl) {
+ FrameLayout.LayoutParams primaryParams =
+ (FrameLayout.LayoutParams) primarySnapshot.getLayoutParams();
+ FrameLayout.LayoutParams secondaryParams =
+ (FrameLayout.LayoutParams) secondarySnapshot.getLayoutParams();
+
+ // Swap the margins that are set in TaskView#setRecentsOrientedState()
+ secondaryParams.topMargin = dp.overviewTaskThumbnailTopMarginPx;
+ primaryParams.topMargin = 0;
+
+ // Measure and layout the thumbnails bottom up, since the primary is on the visual left
+ // (portrait bottom) and secondary is on the right (portrait top)
+ int spaceAboveSnapshot = dp.overviewTaskThumbnailTopMarginPx;
+ int totalThumbnailHeight = parentHeight - spaceAboveSnapshot;
+ int dividerBar = Math.round(totalThumbnailHeight * (splitBoundsConfig.appsStackedVertically
+ ? splitBoundsConfig.dividerHeightPercent
+ : splitBoundsConfig.dividerWidthPercent));
+ int primarySnapshotHeight;
+ int primarySnapshotWidth;
+ int secondarySnapshotHeight;
+ int secondarySnapshotWidth;
+
+ float taskPercent = splitBoundsConfig.appsStackedVertically ?
+ splitBoundsConfig.topTaskPercent : splitBoundsConfig.leftTaskPercent;
+ primarySnapshotWidth = parentWidth;
+ primarySnapshotHeight = (int) (totalThumbnailHeight * (taskPercent));
+
+ secondarySnapshotWidth = parentWidth;
+ secondarySnapshotHeight = totalThumbnailHeight - primarySnapshotHeight - dividerBar;
+ secondarySnapshot.setTranslationY(0);
+ primarySnapshot.setTranslationY(secondarySnapshotHeight + spaceAboveSnapshot + dividerBar);
+ primarySnapshot.measure(
+ View.MeasureSpec.makeMeasureSpec(primarySnapshotWidth, View.MeasureSpec.EXACTLY),
+ View.MeasureSpec.makeMeasureSpec(primarySnapshotHeight, View.MeasureSpec.EXACTLY));
+ secondarySnapshot.measure(
+ View.MeasureSpec.makeMeasureSpec(secondarySnapshotWidth, View.MeasureSpec.EXACTLY),
+ View.MeasureSpec.makeMeasureSpec(secondarySnapshotHeight,
+ View.MeasureSpec.EXACTLY));
+ }
+
/* ---------- The following are only used by TaskViewTouchHandler. ---------- */
@Override
diff --git a/src/com/android/launcher3/util/BgObjectWithLooper.java b/src/com/android/launcher3/util/BgObjectWithLooper.java
index 1483c43..adc3c7d 100644
--- a/src/com/android/launcher3/util/BgObjectWithLooper.java
+++ b/src/com/android/launcher3/util/BgObjectWithLooper.java
@@ -15,10 +15,15 @@
*/
package com.android.launcher3.util;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
import android.os.Looper;
import androidx.annotation.WorkerThread;
+import java.util.function.Consumer;
+
/**
* Utility class to define an object which does most of it's processing on a
* dedicated background thread.
@@ -43,4 +48,16 @@
*/
@WorkerThread
protected abstract void onInitialized(Looper looper);
+
+ /**
+ * Helper method to create a content provider
+ */
+ protected static ContentObserver newContentObserver(Handler handler, Consumer<Uri> command) {
+ return new ContentObserver(handler) {
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ command.accept(uri);
+ }
+ };
+ }
}
diff --git a/src/com/android/launcher3/util/DimensionUtils.kt b/src/com/android/launcher3/util/DimensionUtils.kt
new file mode 100644
index 0000000..758b3a9
--- /dev/null
+++ b/src/com/android/launcher3/util/DimensionUtils.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.launcher3.util
+
+import android.content.res.Resources
+import android.graphics.Point
+import android.view.ViewGroup
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.R
+
+object DimensionUtils {
+ /**
+ * Point where x is width, and y is height of taskbar based on provided [deviceProfile]
+ * x or y could also be -1 to indicate there is no dimension specified
+ */
+ @JvmStatic
+ fun getTaskbarPhoneDimensions(deviceProfile: DeviceProfile, res: Resources,
+ isPhoneMode: Boolean): Point {
+ val p = Point()
+ // Taskbar for large screen
+ if (!isPhoneMode) {
+ p.x = ViewGroup.LayoutParams.MATCH_PARENT
+ p.y = deviceProfile.taskbarSize
+ return p
+ }
+
+ // Taskbar on phone using gesture nav, it will always be stashed
+ if (deviceProfile.isGestureMode) {
+ p.x = ViewGroup.LayoutParams.MATCH_PARENT
+ p.y = res.getDimensionPixelSize(R.dimen.taskbar_stashed_size)
+ return p
+ }
+
+ // Taskbar on phone, portrait
+ if (!deviceProfile.isLandscape) {
+ p.x = ViewGroup.LayoutParams.MATCH_PARENT
+ p.y = res.getDimensionPixelSize(R.dimen.taskbar_size)
+ return p
+ }
+
+ // Taskbar on phone, landscape
+ p.x = res.getDimensionPixelSize(R.dimen.taskbar_size)
+ p.y = ViewGroup.LayoutParams.MATCH_PARENT
+ return p
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java
index 94f9f25..226f2d9 100644
--- a/src/com/android/launcher3/util/DisplayController.java
+++ b/src/com/android/launcher3/util/DisplayController.java
@@ -19,11 +19,8 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
-import static com.android.launcher3.testing.shared.ResourceUtils.INVALID_RESOURCE_HANDLE;
import static com.android.launcher3.Utilities.dpiFromPx;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NAVIGATION_MODE_2_BUTTON;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NAVIGATION_MODE_3_BUTTON;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NAVIGATION_MODE_GESTURE_BUTTON;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_TRANSIENT_TASKBAR;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.PackageManagerHelper.getPackageFilter;
import static com.android.launcher3.util.window.WindowManagerProxy.MIN_TABLET_WIDTH;
@@ -45,10 +42,9 @@
import androidx.annotation.AnyThread;
import androidx.annotation.UiThread;
+import androidx.annotation.VisibleForTesting;
-import com.android.launcher3.testing.shared.ResourceUtils;
import com.android.launcher3.Utilities;
-import com.android.launcher3.logging.StatsLogManager.LauncherEvent;
import com.android.launcher3.util.window.CachedDisplayInfo;
import com.android.launcher3.util.window.WindowManagerProxy;
@@ -56,6 +52,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.Map;
import java.util.Objects;
import java.util.Set;
@@ -67,6 +64,7 @@
private static final String TAG = "DisplayController";
private static final boolean DEBUG = false;
+ private static boolean sTransientTaskbarStatusForTests;
public static final MainThreadInitializedObject<DisplayController> INSTANCE =
new MainThreadInitializedObject<>(DisplayController::new);
@@ -81,7 +79,6 @@
| CHANGE_DENSITY | CHANGE_SUPPORTED_BOUNDS | CHANGE_NAVIGATION_MODE;
private static final String ACTION_OVERLAY_CHANGED = "android.intent.action.OVERLAY_CHANGED";
- private static final String NAV_BAR_INTERACTION_MODE_RES_NAME = "config_navBarInteractionMode";
private static final String TARGET_OVERLAY_PACKAGE = "android";
private final Context mContext;
@@ -129,6 +126,24 @@
return INSTANCE.get(context).getInfo().navigationMode;
}
+ /**
+ * Returns whether taskbar is transient.
+ */
+ public static boolean isTransientTaskbar(Context context) {
+ return getNavigationMode(context) == NavigationMode.NO_BUTTON
+ && (Utilities.IS_RUNNING_IN_TEST_HARNESS
+ ? sTransientTaskbarStatusForTests
+ : ENABLE_TRANSIENT_TASKBAR.get());
+ }
+
+ /**
+ * Enables transient taskbar status for tests.
+ */
+ @VisibleForTesting
+ public static void enableTransientTaskbarForTests(boolean enable) {
+ sTransientTaskbarStatusForTests = enable;
+ }
+
@Override
public void close() {
mDestroyed = true;
@@ -294,7 +309,7 @@
// Used for testing
public Info(Context displayInfoContext,
WindowManagerProxy wmProxy,
- ArrayMap<CachedDisplayInfo, WindowBounds[]> perDisplayBoundsCache) {
+ Map<CachedDisplayInfo, WindowBounds[]> perDisplayBoundsCache) {
CachedDisplayInfo displayInfo = wmProxy.getDisplayInfo(displayInfoContext);
normalizedDisplayInfo = displayInfo.normalize();
rotation = displayInfo.rotation;
@@ -305,7 +320,7 @@
fontScale = config.fontScale;
densityDpi = config.densityDpi;
mScreenSizeDp = new PortraitSize(config.screenHeightDp, config.screenWidthDp);
- navigationMode = parseNavigationMode(displayInfoContext);
+ navigationMode = wmProxy.getNavigationMode(displayInfoContext);
mPerDisplayBounds.putAll(perDisplayBoundsCache);
WindowBounds[] cachedValue = mPerDisplayBounds.get(normalizedDisplayInfo);
@@ -405,35 +420,4 @@
}
}
- public enum NavigationMode {
- THREE_BUTTONS(false, 0, LAUNCHER_NAVIGATION_MODE_3_BUTTON),
- TWO_BUTTONS(true, 1, LAUNCHER_NAVIGATION_MODE_2_BUTTON),
- NO_BUTTON(true, 2, LAUNCHER_NAVIGATION_MODE_GESTURE_BUTTON);
-
- public final boolean hasGestures;
- public final int resValue;
- public final LauncherEvent launcherEvent;
-
- NavigationMode(boolean hasGestures, int resValue, LauncherEvent launcherEvent) {
- this.hasGestures = hasGestures;
- this.resValue = resValue;
- this.launcherEvent = launcherEvent;
- }
- }
-
- private static NavigationMode parseNavigationMode(Context context) {
- int modeInt = ResourceUtils.getIntegerByName(NAV_BAR_INTERACTION_MODE_RES_NAME,
- context.getResources(), INVALID_RESOURCE_HANDLE);
-
- if (modeInt == INVALID_RESOURCE_HANDLE) {
- Log.e(TAG, "Failed to get system resource ID. Incompatible framework version?");
- } else {
- for (NavigationMode m : NavigationMode.values()) {
- if (m.resValue == modeInt) {
- return m;
- }
- }
- }
- return Utilities.ATLEAST_S ? NavigationMode.NO_BUTTON : NavigationMode.THREE_BUTTONS;
- }
}
diff --git a/src/com/android/launcher3/util/LogConfig.java b/src/com/android/launcher3/util/LogConfig.java
index 6bc26e7..5abf95c 100644
--- a/src/com/android/launcher3/util/LogConfig.java
+++ b/src/com/android/launcher3/util/LogConfig.java
@@ -40,4 +40,19 @@
* When turned on, we enable suggest related logging.
*/
public static final String SEARCH_LOGGING = "SearchLogging";
+
+ /**
+ * When turned on, we enable IME related latency related logging.
+ */
+ public static final String IME_LATENCY_LOGGING = "ImeLatencyLogging";
+
+ /**
+ * When turned on, we enable web suggest appSearch related logging.
+ */
+ public static final String WEB_APP_SEARCH_LOGGING = "WebAppSearchLogging";
+
+ /**
+ * When turned on, we enable quick launch v2 related logging.
+ */
+ public static final String QUICK_LAUNCH_V2 = "QuickLaunchV2";
}
diff --git a/src/com/android/launcher3/util/MultiAdditivePropertyFactory.java b/src/com/android/launcher3/util/MultiAdditivePropertyFactory.java
deleted file mode 100644
index 50f7027..0000000
--- a/src/com/android/launcher3/util/MultiAdditivePropertyFactory.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.util;
-
-import android.util.ArrayMap;
-import android.util.FloatProperty;
-import android.util.Log;
-import android.util.Property;
-import android.view.View;
-
-/**
- * Allows to combine multiple values set by several sources.
- *
- * The various sources are meant to use [set], providing different `setterIndex` params. When it is
- * not set, 0 is used. This is meant to cover the case multiple animations are going on at the same
- * time.
- *
- * This class behaves similarly to [MultiValueAlpha], but is meant to be more abstract and reusable.
- * It sets the addition of all values.
- *
- * @param <T> Type where to apply the property.
- */
-public class MultiAdditivePropertyFactory<T extends View> {
-
- private static final boolean DEBUG = false;
- private static final String TAG = "MultiAdditivePropertyFactory";
- private final String mName;
- private final ArrayMap<Integer, MultiAdditiveProperty> mProperties =
- new ArrayMap<>();
-
- // This is an optimization for cases when set is called repeatedly with the same setterIndex.
- private float mAggregationOfOthers = 0f;
- private Integer mLastIndexSet = -1;
- private final Property<View, Float> mProperty;
-
- public MultiAdditivePropertyFactory(String name, Property<View, Float> property) {
- mName = name;
- mProperty = property;
- }
-
- /** Returns the [MultiFloatProperty] associated with [inx], creating it if not present. */
- public MultiAdditiveProperty get(Integer index) {
- return mProperties.computeIfAbsent(index,
- (k) -> new MultiAdditiveProperty(index, mName + "_" + index));
- }
-
- /**
- * Each [setValue] will be aggregated with the other properties values created by the
- * corresponding factory.
- */
- class MultiAdditiveProperty extends FloatProperty<T> {
- private final int mInx;
- private float mValue = 0f;
-
- MultiAdditiveProperty(int inx, String name) {
- super(name);
- mInx = inx;
- }
-
- @Override
- public void setValue(T obj, float newValue) {
- if (mLastIndexSet != mInx) {
- mAggregationOfOthers = 0f;
- mProperties.forEach((key, property) -> {
- if (key != mInx) {
- mAggregationOfOthers += property.mValue;
- }
- });
- mLastIndexSet = mInx;
- }
- float lastAggregatedValue = mAggregationOfOthers + newValue;
- mValue = newValue;
- apply(obj, lastAggregatedValue);
-
- if (DEBUG) {
- Log.d(TAG, "name=" + mName
- + " newValue=" + newValue + " mInx=" + mInx
- + " aggregated=" + lastAggregatedValue + " others= " + mProperties);
- }
- }
-
- @Override
- public Float get(T view) {
- // The scale of the view should match mLastAggregatedValue. Still, if it has been
- // changed without using this property, it can differ. As this get method is usually
- // used to set the starting point on an animation, this would result in some jumps
- // when the view scale is different than the last aggregated value. To stay on the
- // safe side, let's return the real view scale.
- return mProperty.get(view);
- }
-
- @Override
- public String toString() {
- return String.valueOf(mValue);
- }
- }
-
- protected void apply(View view, float value) {
- mProperty.set(view, value);
- }
-}
diff --git a/src/com/android/launcher3/util/MultiPropertyFactory.java b/src/com/android/launcher3/util/MultiPropertyFactory.java
new file mode 100644
index 0000000..f34c4c2
--- /dev/null
+++ b/src/com/android/launcher3/util/MultiPropertyFactory.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.util;
+
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
+import android.util.FloatProperty;
+import android.util.Log;
+
+import java.io.PrintWriter;
+import java.util.Arrays;
+
+/**
+ * Allows to combine multiple values set by several sources.
+ *
+ * The various sources are meant to use [set], providing different `setterIndex` params. When it is
+ * not set, 0 is used. This is meant to cover the case multiple animations are going on at the same
+ * time.
+ *
+ * This class behaves similarly to [MultiValueAlpha], but is meant to be more abstract and reusable.
+ * It aggregate all values using the provided [aggregator].
+ *
+ * @param <T> Type where to apply the property.
+ */
+public class MultiPropertyFactory<T> {
+
+ public static final FloatProperty<MultiPropertyFactory<?>.MultiProperty> MULTI_PROPERTY_VALUE =
+ new FloatProperty<MultiPropertyFactory<?>.MultiProperty>("value") {
+
+ @Override
+ public Float get(MultiPropertyFactory<?>.MultiProperty property) {
+ return property.mValue;
+ }
+
+ @Override
+ public void setValue(MultiPropertyFactory<?>.MultiProperty property, float value) {
+ property.setValue(value);
+ }
+ };
+
+ private static final boolean DEBUG = false;
+ private static final String TAG = "MultiPropertyFactory";
+ private final MultiPropertyFactory<?>.MultiProperty[] mProperties;
+
+ // This is an optimization for cases when set is called repeatedly with the same setterIndex.
+ private float mAggregationOfOthers = 0f;
+ private int mLastIndexSet = -1;
+
+ protected final T mTarget;
+ private final FloatProperty<T> mProperty;
+ private final FloatBiFunction mAggregator;
+
+ /**
+ * Represents a function that accepts two float and produces a float.
+ */
+ public interface FloatBiFunction {
+ /**
+ * Applies this function to the given arguments.
+ */
+ float apply(float a, float b);
+ }
+
+ public MultiPropertyFactory(T target, FloatProperty<T> property, int size,
+ FloatBiFunction aggregator) {
+ this(target, property, size, aggregator, 0);
+ }
+
+ public MultiPropertyFactory(T target, FloatProperty<T> property, int size,
+ FloatBiFunction aggregator, float defaultPropertyValue) {
+ mTarget = target;
+ mProperty = property;
+ mAggregator = aggregator;
+
+ mProperties = new MultiPropertyFactory<?>.MultiProperty[size];
+ for (int i = 0; i < size; i++) {
+ mProperties[i] = new MultiProperty(i, defaultPropertyValue);
+ }
+ }
+
+ /** Returns the [MultiFloatProperty] associated with [inx], creating it if not present. */
+ public MultiProperty get(int index) {
+ return (MultiProperty) mProperties[index];
+ }
+
+ @Override
+ public String toString() {
+ return Arrays.deepToString(mProperties);
+ }
+
+ /**
+ * Dumps the alpha channel values to the given PrintWriter
+ *
+ * @param prefix String to be used before every line
+ * @param pw PrintWriter where the logs should be dumped
+ * @param label String used to help identify this object
+ * @param alphaIndexLabels Strings that represent each alpha channel, these should be entered
+ * in the order of the indexes they represent, starting from 0.
+ */
+ public void dump(String prefix, PrintWriter pw, String label, String... alphaIndexLabels) {
+ pw.println(prefix + label);
+
+ String innerPrefix = prefix + '\t';
+ for (int i = 0; i < alphaIndexLabels.length; i++) {
+ if (i >= mProperties.length) {
+ pw.println(innerPrefix + alphaIndexLabels[i] + " given for alpha index " + i
+ + " however there are only " + mProperties.length + " alpha channels.");
+ continue;
+ }
+ pw.println(innerPrefix + alphaIndexLabels[i] + "=" + get(i).getValue());
+ }
+ }
+
+ /**
+ * Each [setValue] will be aggregated with the other properties values created by the
+ * corresponding factory.
+ */
+ public class MultiProperty {
+
+ private final int mInx;
+ private final float mDefaultValue;
+ private float mValue;
+
+ MultiProperty(int inx, float defaultValue) {
+ mInx = inx;
+ mDefaultValue = defaultValue;
+ mValue = defaultValue;
+ }
+
+ public void setValue(float newValue) {
+ if (mLastIndexSet != mInx) {
+ mAggregationOfOthers = mDefaultValue;
+ for (MultiPropertyFactory<?>.MultiProperty other : mProperties) {
+ if (other.mInx != mInx) {
+ mAggregationOfOthers =
+ mAggregator.apply(mAggregationOfOthers, other.mValue);
+ }
+ }
+
+ mLastIndexSet = mInx;
+ }
+ float lastAggregatedValue = mAggregator.apply(mAggregationOfOthers, newValue);
+ mValue = newValue;
+ apply(lastAggregatedValue);
+
+ if (DEBUG) {
+ Log.d(TAG, "name=" + mProperty.getName()
+ + " target=" + mTarget.getClass()
+ + " newValue=" + newValue
+ + " mInx=" + mInx
+ + " aggregated=" + lastAggregatedValue
+ + " others= " + Arrays.deepToString(mProperties));
+ }
+ }
+
+ public float getValue() {
+ return mValue;
+ }
+
+ @Override
+ public String toString() {
+ return String.valueOf(mValue);
+ }
+
+ /**
+ * Creates and returns an Animator from the current value to the given value. Future
+ * animator on the same target automatically cancels the previous one.
+ */
+ public Animator animateToValue(float value) {
+ ObjectAnimator animator = ObjectAnimator.ofFloat(this, MULTI_PROPERTY_VALUE, value);
+ animator.setAutoCancel(true);
+ return animator;
+ }
+ }
+
+ protected void apply(float value) {
+ mProperty.set(mTarget, value);
+ }
+}
diff --git a/src/com/android/launcher3/util/MultiValueAlpha.java b/src/com/android/launcher3/util/MultiValueAlpha.java
index 4b46a0a..ac016a8 100644
--- a/src/com/android/launcher3/util/MultiValueAlpha.java
+++ b/src/com/android/launcher3/util/MultiValueAlpha.java
@@ -16,62 +16,24 @@
package com.android.launcher3.util;
-import android.animation.Animator;
-import android.animation.ObjectAnimator;
-import android.util.FloatProperty;
+import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
+
import android.view.View;
import com.android.launcher3.anim.AlphaUpdateListener;
-import java.io.PrintWriter;
-import java.util.Arrays;
-import java.util.function.Consumer;
-
/**
* Utility class to handle separating a single value as a factor of multiple values
*/
-public class MultiValueAlpha {
+public class MultiValueAlpha extends MultiPropertyFactory<View> {
- public static final FloatProperty<AlphaProperty> VALUE =
- new FloatProperty<AlphaProperty>("value") {
+ private static final FloatBiFunction ALPHA_AGGREGATOR = (a, b) -> a * b;
- @Override
- public Float get(AlphaProperty alphaProperty) {
- return alphaProperty.mValue;
- }
-
- @Override
- public void setValue(AlphaProperty object, float value) {
- object.setValue(value);
- }
- };
-
- private final View mView;
- private final AlphaProperty[] mMyProperties;
-
- private int mValidMask;
// Whether we should change from INVISIBLE to VISIBLE and vice versa at low alpha values.
private boolean mUpdateVisibility;
public MultiValueAlpha(View view, int size) {
- mView = view;
- mMyProperties = new AlphaProperty[size];
-
- mValidMask = 0;
- for (int i = 0; i < size; i++) {
- int myMask = 1 << i;
- mValidMask |= myMask;
- mMyProperties[i] = new AlphaProperty(myMask);
- }
- }
-
- @Override
- public String toString() {
- return Arrays.toString(mMyProperties);
- }
-
- public AlphaProperty getProperty(int index) {
- return mMyProperties[index];
+ super(view, VIEW_ALPHA, size, ALPHA_AGGREGATOR, 1f);
}
/** Sets whether we should update between INVISIBLE and VISIBLE based on alpha. */
@@ -79,97 +41,11 @@
mUpdateVisibility = updateVisibility;
}
- /**
- * Dumps the alpha channel values to the given PrintWriter
- *
- * @param prefix String to be used before every line
- * @param pw PrintWriter where the logs should be dumped
- * @param label String used to help identify this object
- * @param alphaIndexLabels Strings that represent each alpha channel, these should be entered
- * in the order of the indexes they represent, starting from 0.
- */
- public void dump(String prefix, PrintWriter pw, String label, String... alphaIndexLabels) {
- pw.println(prefix + label);
-
- String innerPrefix = prefix + '\t';
- for (int i = 0; i < alphaIndexLabels.length; i++) {
- if (i >= mMyProperties.length) {
- pw.println(innerPrefix + alphaIndexLabels[i] + " given for alpha index " + i
- + " however there are only " + mMyProperties.length + " alpha channels.");
- continue;
- }
- pw.println(innerPrefix + alphaIndexLabels[i] + "=" + getProperty(i).getValue());
- }
- }
-
- public class AlphaProperty {
-
- private final int mMyMask;
-
- private float mValue = 1;
- // Factor of all other alpha channels, only valid if mMyMask is present in mValidMask.
- private float mOthers = 1;
-
- private Consumer<Float> mConsumer;
-
- AlphaProperty(int myMask) {
- mMyMask = myMask;
- }
-
- public void setValue(float value) {
- if (mValue == value) {
- return;
- }
-
- if ((mValidMask & mMyMask) == 0) {
- // Our cache value is not correct, recompute it.
- mOthers = 1;
- for (AlphaProperty prop : mMyProperties) {
- if (prop != this) {
- mOthers *= prop.mValue;
- }
- }
- }
-
- // Since we have changed our value, all other caches except our own need to be
- // recomputed. Change mValidMask to indicate the new valid caches (only our own).
- mValidMask = mMyMask;
- mValue = value;
-
- final float alpha = mOthers * mValue;
- mView.setAlpha(alpha);
- if (mUpdateVisibility) {
- AlphaUpdateListener.updateVisibility(mView);
- }
- if (mConsumer != null) {
- mConsumer.accept(mValue);
- }
- }
-
- public float getValue() {
- return mValue;
- }
-
- public void setConsumer(Consumer<Float> consumer) {
- mConsumer = consumer;
- if (mConsumer != null) {
- mConsumer.accept(mValue);
- }
- }
-
- @Override
- public String toString() {
- return Float.toString(mValue);
- }
-
- /**
- * Creates and returns an Animator from the current value to the given value. Future
- * animator on the same target automatically cancels the previous one.
- */
- public Animator animateToValue(float value) {
- ObjectAnimator animator = ObjectAnimator.ofFloat(this, VALUE, value);
- animator.setAutoCancel(true);
- return animator;
+ @Override
+ protected void apply(float value) {
+ super.apply(value);
+ if (mUpdateVisibility) {
+ AlphaUpdateListener.updateVisibility(mTarget);
}
}
}
diff --git a/src/com/android/launcher3/util/NavigationMode.java b/src/com/android/launcher3/util/NavigationMode.java
new file mode 100644
index 0000000..37dd41c
--- /dev/null
+++ b/src/com/android/launcher3/util/NavigationMode.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.util;
+
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NAVIGATION_MODE_2_BUTTON;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NAVIGATION_MODE_3_BUTTON;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NAVIGATION_MODE_GESTURE_BUTTON;
+
+import com.android.launcher3.logging.StatsLogManager;
+
+/**
+ * Navigation mode used in the device.
+ */
+public enum NavigationMode {
+ THREE_BUTTONS(false, 0, LAUNCHER_NAVIGATION_MODE_3_BUTTON),
+ TWO_BUTTONS(true, 1, LAUNCHER_NAVIGATION_MODE_2_BUTTON),
+ NO_BUTTON(true, 2, LAUNCHER_NAVIGATION_MODE_GESTURE_BUTTON);
+
+ public final boolean hasGestures;
+ public final int resValue;
+ public final StatsLogManager.LauncherEvent launcherEvent;
+
+ NavigationMode(boolean hasGestures, int resValue, StatsLogManager.LauncherEvent launcherEvent) {
+ this.hasGestures = hasGestures;
+ this.resValue = resValue;
+ this.launcherEvent = launcherEvent;
+ }
+}
diff --git a/src/com/android/launcher3/util/OnboardingPrefs.java b/src/com/android/launcher3/util/OnboardingPrefs.java
index f4cf21e..d942b7a 100644
--- a/src/com/android/launcher3/util/OnboardingPrefs.java
+++ b/src/com/android/launcher3/util/OnboardingPrefs.java
@@ -43,13 +43,14 @@
public static final String SEARCH_ONBOARDING_COUNT = "launcher.search_onboarding_count";
public static final String TASKBAR_EDU_SEEN = "launcher.taskbar_edu_seen";
public static final String ALL_APPS_VISITED_COUNT = "launcher.all_apps_visited_count";
+ public static final String QSB_SEARCH_ONBOARDING_CARD_DISMISSED = "launcher.qsb_edu_dismiss";
// When adding a new key, add it here as well, to be able to reset it from Developer Options.
public static final Map<String, String[]> ALL_PREF_KEYS = Map.of(
"All Apps Bounce", new String[] { HOME_BOUNCE_SEEN, HOME_BOUNCE_COUNT },
"Hybrid Hotseat Education", new String[] { HOTSEAT_DISCOVERY_TIP_COUNT,
HOTSEAT_LONGPRESS_TIP_SEEN },
"Search Education", new String[] { SEARCH_KEYBOARD_EDU_SEEN, SEARCH_SNACKBAR_COUNT,
- SEARCH_ONBOARDING_COUNT},
+ SEARCH_ONBOARDING_COUNT, QSB_SEARCH_ONBOARDING_CARD_DISMISSED},
"Taskbar Education", new String[] { TASKBAR_EDU_SEEN },
"All Apps Visited Count", new String[] {ALL_APPS_VISITED_COUNT}
);
@@ -61,7 +62,8 @@
HOME_BOUNCE_SEEN,
HOTSEAT_LONGPRESS_TIP_SEEN,
SEARCH_KEYBOARD_EDU_SEEN,
- TASKBAR_EDU_SEEN
+ TASKBAR_EDU_SEEN,
+ QSB_SEARCH_ONBOARDING_CARD_DISMISSED
})
@Retention(RetentionPolicy.SOURCE)
public @interface EventBoolKey {}
diff --git a/src/com/android/launcher3/util/PackageManagerHelper.java b/src/com/android/launcher3/util/PackageManagerHelper.java
index f42d304..12e8b54 100644
--- a/src/com/android/launcher3/util/PackageManagerHelper.java
+++ b/src/com/android/launcher3/util/PackageManagerHelper.java
@@ -43,6 +43,9 @@
import android.util.Pair;
import android.widget.Toast;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.PendingAddItemInfo;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
@@ -54,6 +57,7 @@
import java.net.URISyntaxException;
import java.util.List;
+import java.util.Objects;
/**
* Utility methods using package manager
@@ -62,22 +66,28 @@
private static final String TAG = "PackageManagerHelper";
+ @NonNull
private final Context mContext;
+
+ @NonNull
private final PackageManager mPm;
+
+ @NonNull
private final LauncherApps mLauncherApps;
- public PackageManagerHelper(Context context) {
+ public PackageManagerHelper(@NonNull final Context context) {
mContext = context;
mPm = context.getPackageManager();
- mLauncherApps = context.getSystemService(LauncherApps.class);
+ mLauncherApps = Objects.requireNonNull(context.getSystemService(LauncherApps.class));
}
/**
* Returns true if the app can possibly be on the SDCard. This is just a workaround and doesn't
* guarantee that the app is on SD card.
*/
- public boolean isAppOnSdcard(String packageName, UserHandle user) {
- ApplicationInfo info = getApplicationInfo(
+ public boolean isAppOnSdcard(@NonNull final String packageName,
+ @NonNull final UserHandle user) {
+ final ApplicationInfo info = getApplicationInfo(
packageName, user, PackageManager.MATCH_UNINSTALLED_PACKAGES);
return info != null && (info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
}
@@ -86,23 +96,27 @@
* Returns whether the target app is suspended for a given user as per
* {@link android.app.admin.DevicePolicyManager#isPackageSuspended}.
*/
- public boolean isAppSuspended(String packageName, UserHandle user) {
- ApplicationInfo info = getApplicationInfo(packageName, user, 0);
+ public boolean isAppSuspended(@NonNull final String packageName,
+ @NonNull final UserHandle user) {
+ final ApplicationInfo info = getApplicationInfo(packageName, user, 0);
return info != null && isAppSuspended(info);
}
/**
* Returns whether the target app is installed for a given user
*/
- public boolean isAppInstalled(String packageName, UserHandle user) {
- ApplicationInfo info = getApplicationInfo(packageName, user, 0);
+ public boolean isAppInstalled(@NonNull final String packageName,
+ @NonNull final UserHandle user) {
+ final ApplicationInfo info = getApplicationInfo(packageName, user, 0);
return info != null;
}
/**
* Returns the application info for the provided package or null
*/
- public ApplicationInfo getApplicationInfo(String packageName, UserHandle user, int flags) {
+ @Nullable
+ public ApplicationInfo getApplicationInfo(@NonNull final String packageName,
+ @NonNull final UserHandle user, final int flags) {
try {
ApplicationInfo info = mLauncherApps.getApplicationInfo(packageName, flags, user);
return (info.flags & ApplicationInfo.FLAG_INSTALLED) == 0 || !info.enabled
@@ -116,7 +130,8 @@
return mPm.isSafeMode();
}
- public Intent getAppLaunchIntent(String pkg, UserHandle user) {
+ @Nullable
+ public Intent getAppLaunchIntent(@Nullable final String pkg, @NonNull final UserHandle user) {
List<LauncherActivityInfo> activities = mLauncherApps.getActivityList(pkg, user);
return activities.isEmpty() ? null :
AppInfo.makeLaunchIntent(activities.get(0));
@@ -251,7 +266,8 @@
return packageFilter;
}
- public static boolean isSystemApp(Context context, Intent intent) {
+ public static boolean isSystemApp(@NonNull final Context context,
+ @NonNull final Intent intent) {
PackageManager pm = context.getPackageManager();
ComponentName cn = intent.getComponent();
String packageName = null;
diff --git a/src/com/android/launcher3/util/PendingSplitSelectInfo.java b/src/com/android/launcher3/util/PendingSplitSelectInfo.java
index ed02465..58c3be5 100644
--- a/src/com/android/launcher3/util/PendingSplitSelectInfo.java
+++ b/src/com/android/launcher3/util/PendingSplitSelectInfo.java
@@ -16,6 +16,7 @@
package com.android.launcher3.util;
+import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
/**
@@ -28,10 +29,13 @@
private final int mStagedTaskId;
private final int mStagePosition;
+ private final StatsLogManager.EventEnum mSource;
- public PendingSplitSelectInfo(int stagedTaskId, int stagePosition) {
+ public PendingSplitSelectInfo(int stagedTaskId, int stagePosition,
+ StatsLogManager.EventEnum source) {
this.mStagedTaskId = stagedTaskId;
this.mStagePosition = stagePosition;
+ this.mSource = source;
}
public int getStagedTaskId() {
@@ -41,4 +45,8 @@
public @StagePosition int getStagePosition() {
return mStagePosition;
}
+
+ public StatsLogManager.EventEnum getSource() {
+ return mSource;
+ }
}
diff --git a/src/com/android/launcher3/util/ScrollableLayoutManager.java b/src/com/android/launcher3/util/ScrollableLayoutManager.java
new file mode 100644
index 0000000..9bc4ddc
--- /dev/null
+++ b/src/com/android/launcher3/util/ScrollableLayoutManager.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.util;
+
+import android.content.Context;
+import android.util.SparseIntArray;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.RecyclerView.Adapter;
+import androidx.recyclerview.widget.RecyclerView.State;
+import androidx.recyclerview.widget.RecyclerView.ViewHolder;
+
+/**
+ * Extension of {@link GridLayoutManager} with support for smooth scrolling
+ */
+public class ScrollableLayoutManager extends GridLayoutManager {
+
+ // keyed on item type
+ protected final SparseIntArray mCachedSizes = new SparseIntArray();
+
+ private RecyclerView mRv;
+
+ /**
+ * Precalculated total height keyed on the item position. This is always incremental.
+ * Subclass can override {@link #incrementTotalHeight} to incorporate the layout logic.
+ * For example all-apps should have same values for items in same row,
+ * sample values: 0, 10, 10, 10, 10, 20, 20, 20, 20
+ * whereas widgets will have strictly increasing values
+ * sample values: 0, 10, 50, 60, 110
+ */
+ private int[] mTotalHeightCache = new int[1];
+ private int mLastValidHeightIndex = 0;
+
+ public ScrollableLayoutManager(Context context) {
+ super(context, 1, GridLayoutManager.VERTICAL, false);
+ }
+
+ @Override
+ public void onAttachedToWindow(RecyclerView view) {
+ super.onAttachedToWindow(view);
+ mRv = view;
+ }
+
+ @Override
+ public void layoutDecorated(@NonNull View child, int left, int top, int right, int bottom) {
+ super.layoutDecorated(child, left, top, right, bottom);
+ updateCachedSize(child);
+ }
+
+ @Override
+ public void layoutDecoratedWithMargins(@NonNull View child, int left, int top, int right,
+ int bottom) {
+ super.layoutDecoratedWithMargins(child, left, top, right, bottom);
+ updateCachedSize(child);
+ }
+
+ private void updateCachedSize(@NonNull View child) {
+ int viewType = mRv.getChildViewHolder(child).getItemViewType();
+ int size = child.getMeasuredHeight();
+ if (mCachedSizes.get(viewType, -1) != size) {
+ invalidateScrollCache();
+ }
+ mCachedSizes.put(viewType, size);
+ }
+
+ @Override
+ public int computeVerticalScrollExtent(State state) {
+ return mRv == null ? 0 : mRv.getHeight();
+ }
+
+ @Override
+ public int computeVerticalScrollOffset(State state) {
+ Adapter adapter = mRv == null ? null : mRv.getAdapter();
+ if (adapter == null) {
+ return 0;
+ }
+ if (adapter.getItemCount() == 0 || getChildCount() == 0) {
+ return 0;
+ }
+ View child = getChildAt(0);
+ ViewHolder holder = mRv.findContainingViewHolder(child);
+ if (holder == null) {
+ return 0;
+ }
+ int itemPosition = holder.getLayoutPosition();
+ if (itemPosition < 0) {
+ return 0;
+ }
+ return getPaddingTop() + getItemsHeight(adapter, itemPosition) - getDecoratedTop(child);
+ }
+
+ @Override
+ public int computeVerticalScrollRange(State state) {
+ Adapter adapter = mRv == null ? null : mRv.getAdapter();
+ return adapter == null ? 0 : getItemsHeight(adapter, adapter.getItemCount());
+ }
+
+ /**
+ * Returns the sum of the height, in pixels, of this list adapter's items from index
+ * 0 (inclusive) until {@code untilIndex} (exclusive). If untilIndex is same as the itemCount,
+ * it returns the full height of all the items.
+ *
+ * <p>If the untilIndex is larger than the total number of items in this adapter, returns the
+ * sum of all items' height.
+ */
+ private int getItemsHeight(Adapter adapter, int untilIndex) {
+ final int totalItems = adapter.getItemCount();
+ if (mTotalHeightCache.length < (totalItems + 1)) {
+ mTotalHeightCache = new int[totalItems + 1];
+ mLastValidHeightIndex = 0;
+ }
+ if (untilIndex > totalItems) {
+ untilIndex = totalItems;
+ } else if (untilIndex < 0) {
+ untilIndex = 0;
+ }
+ if (untilIndex <= mLastValidHeightIndex) {
+ return mTotalHeightCache[untilIndex];
+ }
+
+ int totalItemsHeight = mTotalHeightCache[mLastValidHeightIndex];
+ for (int i = mLastValidHeightIndex; i < untilIndex; i++) {
+ totalItemsHeight = incrementTotalHeight(adapter, i, totalItemsHeight);
+ mTotalHeightCache[i + 1] = totalItemsHeight;
+ }
+ mLastValidHeightIndex = untilIndex;
+ return totalItemsHeight;
+ }
+
+ /**
+ * The current implementation assumes a linear list with every item taking up the whole row.
+ * Subclasses should override this method to account for any spanning logic
+ */
+ protected int incrementTotalHeight(Adapter adapter, int position, int heightUntilLastPos) {
+ return heightUntilLastPos + mCachedSizes.get(adapter.getItemViewType(position));
+ }
+
+ private void invalidateScrollCache() {
+ mLastValidHeightIndex = 0;
+ }
+
+ @Override
+ public void onItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount) {
+ super.onItemsAdded(recyclerView, positionStart, itemCount);
+ invalidateScrollCache();
+ }
+
+ @Override
+ public void onItemsChanged(RecyclerView recyclerView) {
+ super.onItemsChanged(recyclerView);
+ invalidateScrollCache();
+ }
+
+ @Override
+ public void onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount) {
+ super.onItemsRemoved(recyclerView, positionStart, itemCount);
+ invalidateScrollCache();
+ }
+
+ @Override
+ public void onItemsMoved(RecyclerView recyclerView, int from, int to, int itemCount) {
+ super.onItemsMoved(recyclerView, from, to, itemCount);
+ invalidateScrollCache();
+ }
+
+ @Override
+ public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount,
+ Object payload) {
+ super.onItemsUpdated(recyclerView, positionStart, itemCount, payload);
+ invalidateScrollCache();
+ }
+}
diff --git a/src/com/android/launcher3/util/SimpleBroadcastReceiver.java b/src/com/android/launcher3/util/SimpleBroadcastReceiver.java
index 4dfa5cc..0a23506 100644
--- a/src/com/android/launcher3/util/SimpleBroadcastReceiver.java
+++ b/src/com/android/launcher3/util/SimpleBroadcastReceiver.java
@@ -52,4 +52,15 @@
}
context.registerReceiver(this, filter, flags);
}
+
+ /**
+ * Unregisters the receiver ignoring any errors
+ */
+ public void unregisterReceiverSafely(Context context) {
+ try {
+ context.unregisterReceiver(this);
+ } catch (IllegalArgumentException e) {
+ // It was probably never registered or already unregistered. Ignore.
+ }
+ }
}
diff --git a/src/com/android/launcher3/util/SplitConfigurationOptions.java b/src/com/android/launcher3/util/SplitConfigurationOptions.java
index f14d985..19a3948 100644
--- a/src/com/android/launcher3/util/SplitConfigurationOptions.java
+++ b/src/com/android/launcher3/util/SplitConfigurationOptions.java
@@ -16,12 +16,17 @@
package com.android.launcher3.util;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_ICON_MENU_SPLIT_LEFT_TOP;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_ICON_MENU_SPLIT_RIGHT_BOTTOM;
+
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.graphics.Rect;
import androidx.annotation.IntDef;
+import com.android.launcher3.logging.StatsLogManager;
+
import java.lang.annotation.Retention;
public final class SplitConfigurationOptions {
@@ -95,6 +100,7 @@
* with the same name/functionality in wm.shell.util (which launcher3 cannot be built against)
*
* If you make changes here, consider making the same changes there
+ * TODO(b/254378592): We really need to consolidate this
*/
public static class SplitBounds {
public final Rect leftTopBounds;
@@ -170,4 +176,18 @@
@StageType
public int stageType = STAGE_TYPE_UNDEFINED;
}
+
+ public static StatsLogManager.EventEnum getLogEventForPosition(@StagePosition int position) {
+ return position == STAGE_POSITION_TOP_OR_LEFT
+ ? LAUNCHER_APP_ICON_MENU_SPLIT_LEFT_TOP
+ : LAUNCHER_APP_ICON_MENU_SPLIT_RIGHT_BOTTOM;
+ }
+
+ public static @StagePosition int getOppositeStagePosition(@StagePosition int position) {
+ if (position == STAGE_POSITION_UNDEFINED) {
+ return position;
+ }
+ return position == STAGE_POSITION_TOP_OR_LEFT ? STAGE_POSITION_BOTTOM_OR_RIGHT
+ : STAGE_POSITION_TOP_OR_LEFT;
+ }
}
diff --git a/src/com/android/launcher3/util/Themes.java b/src/com/android/launcher3/util/Themes.java
index 53a584d..1728f4d 100644
--- a/src/com/android/launcher3/util/Themes.java
+++ b/src/com/android/launcher3/util/Themes.java
@@ -32,7 +32,6 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.GraphicsUtils;
/**
@@ -74,8 +73,7 @@
* Returns true if workspace icon theming is enabled
*/
public static boolean isThemedIconEnabled(Context context) {
- return FeatureFlags.ENABLE_THEMED_ICONS.get()
- && Utilities.getPrefs(context).getBoolean(KEY_THEMED_ICONS, false);
+ return Utilities.getPrefs(context).getBoolean(KEY_THEMED_ICONS, false);
}
public static String getDefaultBodyFont(Context context) {
diff --git a/src/com/android/launcher3/util/TouchController.java b/src/com/android/launcher3/util/TouchController.java
index 9c397c0..fc1d819 100644
--- a/src/com/android/launcher3/util/TouchController.java
+++ b/src/com/android/launcher3/util/TouchController.java
@@ -32,10 +32,5 @@
*/
boolean onControllerInterceptTouchEvent(MotionEvent ev);
- /**
- * Called when one handed mode state changed
- */
- default void onOneHandedModeStateChanged(boolean activated) { }
-
default void dump(String prefix, PrintWriter writer) { }
}
diff --git a/src/com/android/launcher3/util/UiThreadHelper.java b/src/com/android/launcher3/util/UiThreadHelper.java
deleted file mode 100644
index 7e6711f..0000000
--- a/src/com/android/launcher3/util/UiThreadHelper.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.util;
-
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_KEYBOARD_CLOSED;
-import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
-
-import android.annotation.SuppressLint;
-import android.app.Activity;
-import android.content.Context;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Message;
-import android.util.Log;
-import android.view.View;
-import android.view.WindowInsets;
-import android.view.WindowInsetsController;
-import android.view.inputmethod.InputMethodManager;
-
-import com.android.launcher3.Utilities;
-import com.android.launcher3.views.ActivityContext;
-
-/**
- * Utility class for offloading some class from UI thread
- */
-public class UiThreadHelper {
-
- private static final MainThreadInitializedObject<Handler> HANDLER =
- new MainThreadInitializedObject<>(
- c -> new Handler(UI_HELPER_EXECUTOR.getLooper(), new UiCallbacks(c)));
-
- private static final int MSG_HIDE_KEYBOARD = 1;
- private static final int MSG_SET_ORIENTATION = 2;
- private static final int MSG_RUN_COMMAND = 3;
- private static final String STATS_LOGGER_KEY = "STATS_LOGGER_KEY";
-
- @SuppressLint("NewApi")
- public static void hideKeyboardAsync(ActivityContext activityContext, IBinder token) {
- View root = activityContext.getDragLayer();
-
- if (Utilities.ATLEAST_R) {
- Preconditions.assertUIThread();
- // Hide keyboard with WindowInsetsController if could. In case
- // hideSoftInputFromWindow may get ignored by input connection being finished
- // when the screen is off.
- //
- // In addition, inside IMF, the keyboards are closed asynchronously that launcher no
- // longer need to post to the message queue.
- final WindowInsetsController wic = root.getWindowInsetsController();
- WindowInsets insets = root.getRootWindowInsets();
- boolean isImeShown = insets != null && insets.isVisible(WindowInsets.Type.ime());
- if (wic != null && isImeShown) {
- // this method cannot be called cross threads
- wic.hide(WindowInsets.Type.ime());
- activityContext.getStatsLogManager().logger()
- .log(LAUNCHER_ALLAPPS_KEYBOARD_CLOSED);
- return;
- }
- }
- // Since the launcher context cannot be accessed directly from callback, adding secondary
- // message to log keyboard close event asynchronously.
- Bundle mHideKeyboardLoggerMsg = new Bundle();
- mHideKeyboardLoggerMsg.putParcelable(
- STATS_LOGGER_KEY,
- Message.obtain(
- HANDLER.get(root.getContext()),
- () -> activityContext
- .getStatsLogManager()
- .logger()
- .log(LAUNCHER_ALLAPPS_KEYBOARD_CLOSED)
- )
- );
-
- Message mHideKeyboardMsg = Message.obtain(HANDLER.get(root.getContext()), MSG_HIDE_KEYBOARD,
- token);
- mHideKeyboardMsg.setData(mHideKeyboardLoggerMsg);
- mHideKeyboardMsg.sendToTarget();
- }
-
- public static void setOrientationAsync(Activity activity, int orientation) {
- Message.obtain(HANDLER.get(activity), MSG_SET_ORIENTATION, orientation, 0, activity)
- .sendToTarget();
- }
-
- public static void setBackButtonAlphaAsync(Context context, AsyncCommand command, float alpha,
- boolean animate) {
- runAsyncCommand(context, command, Float.floatToIntBits(alpha), animate ? 1 : 0);
- }
-
- public static void runAsyncCommand(Context context, AsyncCommand command, int arg1, int arg2) {
- Message.obtain(HANDLER.get(context), MSG_RUN_COMMAND, arg1, arg2, command).sendToTarget();
- }
-
- private static class UiCallbacks implements Handler.Callback {
-
- private final Context mContext;
- private final InputMethodManager mIMM;
-
- UiCallbacks(Context context) {
- mContext = context;
- mIMM = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
- }
-
- @Override
- public boolean handleMessage(Message message) {
- switch (message.what) {
- case MSG_HIDE_KEYBOARD:
- if (mIMM.hideSoftInputFromWindow((IBinder) message.obj, 0)) {
- // log keyboard close event only when keyboard is actually closed
- ((Message) message.getData().getParcelable(STATS_LOGGER_KEY))
- .sendToTarget();
- }
- return true;
- case MSG_SET_ORIENTATION:
- ((Activity) message.obj).setRequestedOrientation(message.arg1);
- return true;
- case MSG_RUN_COMMAND:
- ((AsyncCommand) message.obj).execute(mContext, message.arg1, message.arg2);
- return true;
- }
- return false;
- }
- }
-
- public interface AsyncCommand {
- void execute(Context proxy, int arg1, int arg2);
- }
-}
diff --git a/src/com/android/launcher3/util/ViewCapture.java b/src/com/android/launcher3/util/ViewCapture.java
deleted file mode 100644
index cf4e84a..0000000
--- a/src/com/android/launcher3/util/ViewCapture.java
+++ /dev/null
@@ -1,323 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.util;
-
-import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
-
-import android.content.res.Resources;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Trace;
-import android.util.Base64;
-import android.util.Base64OutputStream;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewTreeObserver.OnDrawListener;
-
-import androidx.annotation.UiThread;
-import androidx.annotation.WorkerThread;
-
-import com.android.launcher3.view.ViewCaptureData.ExportedData;
-import com.android.launcher3.view.ViewCaptureData.FrameData;
-import com.android.launcher3.view.ViewCaptureData.ViewNode;
-
-import java.io.FileDescriptor;
-import java.io.FileOutputStream;
-import java.io.OutputStream;
-import java.util.concurrent.Future;
-
-/**
- * Utility class for capturing view data every frame
- */
-public class ViewCapture implements OnDrawListener {
-
- private static final String TAG = "ViewCapture";
-
- // Number of frames to keep in memory
- private static final int MEMORY_SIZE = 2000;
- // Initial size of the reference pool. This is at least be 5 * total number of views in
- // Launcher. This allows the first free frames avoid object allocation during view capture.
- private static final int INIT_POOL_SIZE = 300;
-
- private final View mRoot;
- private final Resources mResources;
-
- private final Handler mHandler;
- private final ViewRef mViewRef = new ViewRef();
-
- private int mFrameIndexBg = -1;
- private final long[] mFrameTimesBg = new long[MEMORY_SIZE];
- private final ViewPropertyRef[] mNodesBg = new ViewPropertyRef[MEMORY_SIZE];
-
- // Pool used for capturing view tree on the UI thread.
- private ViewRef mPool = new ViewRef();
-
- /**
- * @param root the root view for the capture data
- */
- public ViewCapture(View root) {
- mRoot = root;
- mResources = root.getResources();
- mHandler = new Handler(UI_HELPER_EXECUTOR.getLooper(), this::captureViewPropertiesBg);
- }
-
- /**
- * Attaches the ViewCapture to the root
- */
- public void attach() {
- mHandler.post(this::initPool);
- }
-
- @Override
- public void onDraw() {
- Trace.beginSection("view_capture");
- captureViewTree(mRoot, mViewRef);
- Message m = Message.obtain(mHandler);
- m.obj = mViewRef.next;
- mHandler.sendMessage(m);
- Trace.endSection();
- }
-
- /**
- * Captures the View property on the background thread, and transfer all the ViewRef objects
- * back to the pool
- */
- @WorkerThread
- private boolean captureViewPropertiesBg(Message msg) {
- ViewRef start = (ViewRef) msg.obj;
- long time = msg.getWhen();
- if (start == null) {
- return false;
- }
- mFrameIndexBg++;
- if (mFrameIndexBg >= MEMORY_SIZE) {
- mFrameIndexBg = 0;
- }
- mFrameTimesBg[mFrameIndexBg] = time;
-
- ViewPropertyRef recycle = mNodesBg[mFrameIndexBg];
-
- ViewPropertyRef result = null;
- ViewPropertyRef resultEnd = null;
-
- ViewRef current = start;
- ViewRef last = start;
- while (current != null) {
- ViewPropertyRef propertyRef = recycle;
- if (propertyRef == null) {
- propertyRef = new ViewPropertyRef();
- } else {
- recycle = recycle.next;
- propertyRef.next = null;
- }
-
- propertyRef.transfer(current);
- last = current;
- current = current.next;
-
- if (result == null) {
- result = propertyRef;
- resultEnd = result;
- } else {
- resultEnd.next = propertyRef;
- resultEnd = propertyRef;
- }
- }
- mNodesBg[mFrameIndexBg] = result;
- ViewRef end = last;
- Executors.MAIN_EXECUTOR.execute(() -> addToPool(start, end));
- return true;
- }
-
- @UiThread
- private void addToPool(ViewRef start, ViewRef end) {
- end.next = mPool;
- mPool = start;
- }
-
- @WorkerThread
- private void initPool() {
- ViewRef start = new ViewRef();
- ViewRef current = start;
-
- for (int i = 0; i < INIT_POOL_SIZE; i++) {
- current.next = new ViewRef();
- current = current.next;
- }
-
- ViewRef end = current;
- Executors.MAIN_EXECUTOR.execute(() -> {
- addToPool(start, end);
- if (mRoot.isAttachedToWindow()) {
- mRoot.getViewTreeObserver().addOnDrawListener(this);
- }
- });
- }
-
- /**
- * Creates a proto of all the data captured so far.
- */
- public void dump(FileDescriptor out) {
- Future<ExportedData> task = UI_HELPER_EXECUTOR.submit(this::dumpToProto);
- try (OutputStream os = new FileOutputStream(out)) {
- ExportedData data = task.get();
- Base64OutputStream encodedOS = new Base64OutputStream(os,
- Base64.NO_CLOSE | Base64.NO_PADDING | Base64.NO_WRAP);
- data.writeTo(encodedOS);
- encodedOS.close();
- os.flush();
- } catch (Exception e) {
- Log.e(TAG, "Error capturing proto", e);
- }
- }
-
- @WorkerThread
- private ExportedData dumpToProto() {
- ExportedData.Builder dataBuilder = ExportedData.newBuilder();
- Resources res = mResources;
-
- int size = (mNodesBg[MEMORY_SIZE - 1] == null) ? mFrameIndexBg + 1 : MEMORY_SIZE;
- for (int i = size - 1; i >= 0; i--) {
- int index = (MEMORY_SIZE + mFrameIndexBg - i) % MEMORY_SIZE;
- ViewNode.Builder nodeBuilder = ViewNode.newBuilder();
- mNodesBg[index].toProto(res, nodeBuilder);
- dataBuilder.addFrameData(FrameData.newBuilder()
- .setNode(nodeBuilder)
- .setTimestamp(mFrameTimesBg[index]));
- }
- return dataBuilder.build();
- }
-
- private ViewRef captureViewTree(View view, ViewRef start) {
- ViewRef ref;
- if (mPool != null) {
- ref = mPool;
- mPool = mPool.next;
- ref.next = null;
- } else {
- ref = new ViewRef();
- }
- ref.view = view;
- start.next = ref;
- if (view instanceof ViewGroup) {
- ViewRef result = ref;
- ViewGroup parent = (ViewGroup) view;
- int childCount = ref.childCount = parent.getChildCount();
- for (int i = 0; i < childCount; i++) {
- result = captureViewTree(parent.getChildAt(i), result);
- }
- return result;
- } else {
- ref.childCount = 0;
- return ref;
- }
- }
-
- private static class ViewPropertyRef {
- // We store reference in memory to avoid generating and storing too many strings
- public Class clazz;
- public int hashCode;
- public int childCount = 0;
-
- public int id;
- public int left, top, right, bottom;
- public int scrollX, scrollY;
-
- public float translateX, translateY;
- public float scaleX, scaleY;
- public float alpha;
-
- public int visibility;
- public boolean willNotDraw;
- public boolean clipChildren;
-
- public ViewPropertyRef next;
-
- public void transfer(ViewRef viewRef) {
- childCount = viewRef.childCount;
-
- View view = viewRef.view;
- viewRef.view = null;
-
- clazz = view.getClass();
- hashCode = view.hashCode();
- id = view.getId();
- left = view.getLeft();
- top = view.getTop();
- right = view.getRight();
- bottom = view.getBottom();
- scrollX = view.getScrollX();
- scrollY = view.getScrollY();
-
- translateX = view.getTranslationX();
- translateY = view.getTranslationY();
- scaleX = view.getScaleX();
- scaleY = view.getScaleY();
- alpha = view.getAlpha();
-
- visibility = view.getVisibility();
- willNotDraw = view.willNotDraw();
- }
-
- /**
- * Converts the data to the proto representation and returns the next property ref
- * at the end of the iteration.
- * @param res
- * @return
- */
- public ViewPropertyRef toProto(Resources res, ViewNode.Builder outBuilder) {
- String resolvedId;
- if (id >= 0) {
- try {
- resolvedId = res.getResourceTypeName(id) + '/' + res.getResourceEntryName(id);
- } catch (Resources.NotFoundException e) {
- resolvedId = "id/" + "0x" + Integer.toHexString(id).toUpperCase();
- }
- } else {
- resolvedId = "NO_ID";
- }
- outBuilder.setClassname(clazz.getName() + "@" + hashCode)
- .setId(resolvedId)
- .setLeft(left)
- .setTop(top)
- .setWidth(right - left)
- .setHeight(bottom - top)
- .setTranslationX(translateX)
- .setTranslationY(translateY)
- .setScaleX(scaleX)
- .setScaleY(scaleY)
- .setAlpha(alpha)
- .setVisibility(visibility)
- .setWillNotDraw(willNotDraw)
- .setClipChildren(clipChildren);
-
- ViewPropertyRef result = next;
- for (int i = 0; (i < childCount) && (result != null); i++) {
- ViewNode.Builder childBuilder = ViewNode.newBuilder();
- result = result.toProto(res, childBuilder);
- outBuilder.addChildren(childBuilder);
- }
- return result;
- }
- }
-
- private static class ViewRef {
- public View view;
- public int childCount = 0;
- public ViewRef next;
- }
-}
diff --git a/src/com/android/launcher3/util/window/WindowManagerProxy.java b/src/com/android/launcher3/util/window/WindowManagerProxy.java
index 582ff8d..fb2ae73 100644
--- a/src/com/android/launcher3/util/window/WindowManagerProxy.java
+++ b/src/com/android/launcher3/util/window/WindowManagerProxy.java
@@ -17,14 +17,15 @@
import static android.view.Display.DEFAULT_DISPLAY;
+import static com.android.launcher3.Utilities.dpiFromPx;
import static com.android.launcher3.testing.shared.ResourceUtils.INVALID_RESOURCE_HANDLE;
import static com.android.launcher3.testing.shared.ResourceUtils.NAVBAR_HEIGHT;
import static com.android.launcher3.testing.shared.ResourceUtils.NAVBAR_HEIGHT_LANDSCAPE;
import static com.android.launcher3.testing.shared.ResourceUtils.NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE;
+import static com.android.launcher3.testing.shared.ResourceUtils.NAV_BAR_INTERACTION_MODE_RES_NAME;
import static com.android.launcher3.testing.shared.ResourceUtils.STATUS_BAR_HEIGHT;
import static com.android.launcher3.testing.shared.ResourceUtils.STATUS_BAR_HEIGHT_LANDSCAPE;
import static com.android.launcher3.testing.shared.ResourceUtils.STATUS_BAR_HEIGHT_PORTRAIT;
-import static com.android.launcher3.Utilities.dpiFromPx;
import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
import static com.android.launcher3.util.RotationUtils.deltaRotation;
import static com.android.launcher3.util.RotationUtils.rotateRect;
@@ -40,6 +41,7 @@
import android.hardware.display.DisplayManager;
import android.os.Build;
import android.util.ArrayMap;
+import android.util.Log;
import android.view.Display;
import android.view.DisplayCutout;
import android.view.Surface;
@@ -48,9 +50,10 @@
import android.view.WindowMetrics;
import com.android.launcher3.R;
-import com.android.launcher3.testing.shared.ResourceUtils;
import com.android.launcher3.Utilities;
+import com.android.launcher3.testing.shared.ResourceUtils;
import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.launcher3.util.NavigationMode;
import com.android.launcher3.util.ResourceBasedOverride;
import com.android.launcher3.util.WindowBounds;
@@ -59,6 +62,7 @@
*/
public class WindowManagerProxy implements ResourceBasedOverride {
+ private static final String TAG = "WindowManagerProxy";
public static final int MIN_TABLET_WIDTH = 600;
public static final MainThreadInitializedObject<WindowManagerProxy> INSTANCE =
@@ -343,4 +347,24 @@
return displayInfoContext.getSystemService(DisplayManager.class).getDisplay(
DEFAULT_DISPLAY);
}
+
+ /**
+ * Returns the current navigation mode from resource.
+ */
+ public NavigationMode getNavigationMode(Context context) {
+ int modeInt = ResourceUtils.getIntegerByName(NAV_BAR_INTERACTION_MODE_RES_NAME,
+ context.getResources(), INVALID_RESOURCE_HANDLE);
+
+ if (modeInt == INVALID_RESOURCE_HANDLE) {
+ Log.e(TAG, "Failed to get system resource ID. Incompatible framework version?");
+ } else {
+ for (NavigationMode m : NavigationMode.values()) {
+ if (m.resValue == modeInt) {
+ return m;
+ }
+ }
+ }
+ return Utilities.ATLEAST_S ? NavigationMode.NO_BUTTON :
+ NavigationMode.THREE_BUTTONS;
+ }
}
diff --git a/src/com/android/launcher3/views/AbstractSlideInView.java b/src/com/android/launcher3/views/AbstractSlideInView.java
index 47503b1..f73347a 100644
--- a/src/com/android/launcher3/views/AbstractSlideInView.java
+++ b/src/com/android/launcher3/views/AbstractSlideInView.java
@@ -33,6 +33,8 @@
import android.view.ViewGroup;
import android.view.animation.Interpolator;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.Interpolators;
@@ -41,6 +43,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
/**
* Extension of {@link AbstractFloatingView} with common methods for sliding in from bottom.
@@ -79,6 +82,7 @@
protected float mTranslationShift = TRANSLATION_SHIFT_CLOSED;
protected boolean mNoIntercept;
+ protected @Nullable OnCloseListener mOnCloseBeginListener;
protected List<OnCloseListener> mOnCloseListeners = new ArrayList<>();
public AbstractSlideInView(Context context, AttributeSet attrs, int defStyleAttr) {
@@ -204,6 +208,11 @@
}
}
+ /** Callback invoked when the view is beginning to close (e.g. close animation is started). */
+ public void setOnCloseBeginListener(@Nullable OnCloseListener onCloseBeginListener) {
+ mOnCloseBeginListener = onCloseBeginListener;
+ }
+
/** Registers an {@link OnCloseListener}. */
public void addOnCloseListener(OnCloseListener listener) {
mOnCloseListeners.add(listener);
@@ -213,6 +222,8 @@
if (!mIsOpen) {
return;
}
+ Optional.ofNullable(mOnCloseBeginListener).ifPresent(OnCloseListener::onSlideInViewClosed);
+
if (!animate) {
mOpenCloseAnimator.cancel();
setTranslationShift(TRANSLATION_SHIFT_CLOSED);
diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java
index 93078e4..dd5b22e 100644
--- a/src/com/android/launcher3/views/ActivityContext.java
+++ b/src/com/android/launcher3/views/ActivityContext.java
@@ -15,16 +15,26 @@
*/
package com.android.launcher3.views;
+import static com.android.launcher3.logging.KeyboardStateManager.KeyboardState.HIDE;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_KEYBOARD_CLOSED;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+
import android.content.Context;
import android.content.ContextWrapper;
import android.graphics.Rect;
+import android.os.IBinder;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.AccessibilityDelegate;
+import android.view.WindowInsets;
+import android.view.WindowInsetsController;
+import android.view.inputmethod.InputMethodManager;
import androidx.annotation.Nullable;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.ActivityAllAppsContainerView;
import com.android.launcher3.allapps.search.SearchAdapterProvider;
import com.android.launcher3.dot.DotInfo;
@@ -36,6 +46,7 @@
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.popup.PopupDataProvider;
import com.android.launcher3.util.OnboardingPrefs;
+import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.ViewCache;
/**
@@ -141,6 +152,7 @@
default void applyOverwritesToLogItem(LauncherAtom.ItemInfo.Builder itemInfoBuilder) { }
/** Onboarding preferences for any onboarding data within this context. */
+ @Nullable
default OnboardingPrefs<?> getOnboardingPrefs() {
return null;
}
@@ -201,4 +213,47 @@
ActivityAllAppsContainerView<?> appsView) {
return null;
}
+
+ /**
+ * Hides the keyboard if it is visible
+ */
+ default void hideKeyboard() {
+ View root = getDragLayer();
+ if (root == null) {
+ return;
+ }
+ if (Utilities.ATLEAST_R) {
+ Preconditions.assertUIThread();
+ // Hide keyboard with WindowInsetsController if could. In case
+ // hideSoftInputFromWindow may get ignored by input connection being finished
+ // when the screen is off.
+ //
+ // In addition, inside IMF, the keyboards are closed asynchronously that launcher no
+ // longer need to post to the message queue.
+ final WindowInsetsController wic = root.getWindowInsetsController();
+ WindowInsets insets = root.getRootWindowInsets();
+ boolean isImeShown = insets != null && insets.isVisible(WindowInsets.Type.ime());
+ if (wic != null && isImeShown) {
+ StatsLogManager slm = getStatsLogManager();
+ slm.keyboardStateManager().setKeyboardState(HIDE);
+
+ // this method cannot be called cross threads
+ wic.hide(WindowInsets.Type.ime());
+ slm.logger().log(LAUNCHER_ALLAPPS_KEYBOARD_CLOSED);
+ return;
+ }
+ }
+
+ InputMethodManager imm = root.getContext().getSystemService(InputMethodManager.class);
+ IBinder token = root.getWindowToken();
+ if (imm != null && token != null) {
+ UI_HELPER_EXECUTOR.execute(() -> {
+ if (imm.hideSoftInputFromWindow(token, 0)) {
+ // log keyboard close event only when keyboard is actually closed
+ MAIN_EXECUTOR.execute(() ->
+ getStatsLogManager().logger().log(LAUNCHER_ALLAPPS_KEYBOARD_CLOSED));
+ }
+ });
+ }
+ }
}
diff --git a/src/com/android/launcher3/views/AllAppsButton.java b/src/com/android/launcher3/views/AllAppsButton.java
deleted file mode 100644
index b1e69c7..0000000
--- a/src/com/android/launcher3/views/AllAppsButton.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.views;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.util.AttributeSet;
-import android.view.ContextThemeWrapper;
-
-import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.R;
-import com.android.launcher3.icons.FastBitmapDrawable;
-
-/**
- * Button in Taskbar that opens All Apps.
- */
-public class AllAppsButton extends BubbleTextView {
-
- public AllAppsButton(Context context) {
- this(context, null);
- }
-
- public AllAppsButton(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public AllAppsButton(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
-
- Context theme = new ContextThemeWrapper(context, R.style.AllAppsButtonTheme);
- Bitmap bitmap = LauncherAppState.getInstance(context).getIconCache().getIconFactory()
- .createScaledBitmapWithShadow(theme.getDrawable(R.drawable.ic_all_apps_button));
- setIcon(new FastBitmapDrawable(bitmap));
- }
-}
diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java
index 800b1f6..8ff6888 100644
--- a/src/com/android/launcher3/views/BaseDragLayer.java
+++ b/src/com/android/launcher3/views/BaseDragLayer.java
@@ -22,7 +22,6 @@
import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
-import android.app.WallpaperManager;
import android.content.Context;
import android.graphics.Insets;
import android.graphics.Rect;
@@ -41,8 +40,8 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InsettableFrameLayout;
import com.android.launcher3.Utilities;
+import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
import com.android.launcher3.util.MultiValueAlpha;
-import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
import com.android.launcher3.util.TouchController;
import java.io.PrintWriter;
@@ -108,7 +107,6 @@
protected final T mActivity;
private final MultiValueAlpha mMultiValueAlpha;
- private final WallpaperManager mWallpaperManager;
// All the touch controllers for the view
protected TouchController[] mControllers;
@@ -121,9 +119,8 @@
public BaseDragLayer(Context context, AttributeSet attrs, int alphaChannelCount) {
super(context, attrs);
- mActivity = (T) ActivityContext.lookupContext(context);
+ mActivity = ActivityContext.lookupContext(context);
mMultiValueAlpha = new MultiValueAlpha(this, alphaChannelCount);
- mWallpaperManager = context.getSystemService(WallpaperManager.class);
}
/**
@@ -502,8 +499,8 @@
return new LayoutParams(p);
}
- public AlphaProperty getAlphaProperty(int index) {
- return mMultiValueAlpha.getProperty(index);
+ public MultiProperty getAlphaProperty(int index) {
+ return mMultiValueAlpha.get(index);
}
public void dump(String prefix, PrintWriter writer) {
diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
index efc83eb..55af622 100644
--- a/src/com/android/launcher3/views/FloatingIconView.java
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.views;
+import static android.view.Gravity.LEFT;
+
import static com.android.launcher3.Utilities.getBadge;
import static com.android.launcher3.Utilities.getFullDrawable;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
@@ -181,8 +183,10 @@
updatePosition(positionOut, lp);
setLayoutParams(lp);
- mClipIconView.setLayoutParams(new FrameLayout.LayoutParams(lp.width, lp.height));
- mBtvDrawable.setLayoutParams(new FrameLayout.LayoutParams(lp.width, lp.height));
+ // For code simplicity, we always layout the child views using Gravity.LEFT
+ // and manually handle RTL for FloatingIconView when positioning it on the screen.
+ mClipIconView.setLayoutParams(new FrameLayout.LayoutParams(lp.width, lp.height, LEFT));
+ mBtvDrawable.setLayoutParams(new FrameLayout.LayoutParams(lp.width, lp.height, LEFT));
}
private void updatePosition(RectF pos, InsettableFrameLayout.LayoutParams lp) {
diff --git a/src/com/android/launcher3/views/IconButtonView.java b/src/com/android/launcher3/views/IconButtonView.java
new file mode 100644
index 0000000..dd48c99
--- /dev/null
+++ b/src/com/android/launcher3/views/IconButtonView.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.views;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.BlendMode;
+import android.graphics.BlendModeColorFilter;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.util.AttributeSet;
+
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.icons.BaseIconFactory;
+import com.android.launcher3.icons.FastBitmapDrawable;
+import com.android.launcher3.icons.LauncherIcons;
+
+/**
+ * Button in Taskbar that shows a tinted background and foreground.
+ */
+public class IconButtonView extends BubbleTextView {
+
+ private static final int[] ATTRS = {android.R.attr.icon};
+
+ public IconButtonView(Context context) {
+ this(context, null);
+ }
+
+ public IconButtonView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public IconButtonView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ TypedArray a = context.obtainStyledAttributes(attrs, ATTRS, defStyle, 0);
+ Drawable fg = a.getDrawable(0);
+ a.recycle();
+
+ ColorStateList tintList = getBackgroundTintList();
+ int tint = tintList == null ? Color.WHITE : tintList.getDefaultColor();
+
+ if (fg == null) {
+ fg = new ColorDrawable(Color.TRANSPARENT);
+ }
+ try (BaseIconFactory factory = LauncherIcons.obtain(context)) {
+ setIcon(new IconDrawable(factory.getWhiteShadowLayer(), tint, fg));
+ }
+ }
+
+ private static class IconDrawable extends FastBitmapDrawable {
+
+ private final Drawable mFg;
+
+ @TargetApi(Build.VERSION_CODES.TIRAMISU)
+ IconDrawable(Bitmap b, int colorBg, Drawable fg) {
+ super(b);
+ mPaint.setColorFilter(new BlendModeColorFilter(colorBg, BlendMode.SRC_IN));
+ mFg = fg;
+ }
+
+ @Override
+ protected void drawInternal(Canvas canvas, Rect bounds) {
+ super.drawInternal(canvas, bounds);
+ mFg.draw(canvas);
+ }
+
+ @Override
+ protected void onBoundsChange(Rect bounds) {
+ super.onBoundsChange(bounds);
+ mFg.setBounds(bounds);
+ }
+ }
+}
diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java
index d301925..b3c376f 100644
--- a/src/com/android/launcher3/views/OptionsPopupView.java
+++ b/src/com/android/launcher3/views/OptionsPopupView.java
@@ -15,9 +15,6 @@
*/
package com.android.launcher3.views;
-import static com.android.launcher3.Utilities.EXTRA_WALLPAPER_FLAVOR;
-import static com.android.launcher3.Utilities.EXTRA_WALLPAPER_LAUNCH_SOURCE;
-import static com.android.launcher3.Utilities.EXTRA_WALLPAPER_OFFSET;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.IGNORE;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SETTINGS_BUTTON_TAP_OR_LONGPRESS;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGETSTRAY_BUTTON_TAP_OR_LONGPRESS;
@@ -51,6 +48,7 @@
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;
@@ -62,6 +60,13 @@
public class OptionsPopupView extends ArrowPopup<Launcher>
implements OnClickListener, OnLongClickListener {
+ // An intent extra to indicate the horizontal scroll of the wallpaper.
+ private static final String EXTRA_WALLPAPER_OFFSET = "com.android.launcher3.WALLPAPER_OFFSET";
+ private static final String EXTRA_WALLPAPER_FLAVOR = "com.android.launcher3.WALLPAPER_FLAVOR";
+ // An intent extra to indicate the launch source by launcher.
+ private static final String EXTRA_WALLPAPER_LAUNCH_SOURCE =
+ "com.android.wallpaper.LAUNCH_SOURCE";
+
private final ArrayMap<View, OptionItem> mItemMap = new ArrayMap<>();
private RectF mTargetRect;
private boolean mShouldAddArrow;
@@ -180,10 +185,11 @@
*/
public static ArrayList<OptionItem> getOptions(Launcher launcher) {
ArrayList<OptionItem> options = new ArrayList<>();
- int resString = Utilities.existsStyleWallpapers(launcher) ?
- R.string.styles_wallpaper_button_text : R.string.wallpaper_button_text;
- int resDrawable = Utilities.existsStyleWallpapers(launcher) ?
- R.drawable.ic_palette : R.drawable.ic_wallpaper;
+ 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,
@@ -251,7 +257,7 @@
.putExtra(EXTRA_WALLPAPER_OFFSET,
launcher.getWorkspace().getWallpaperOffsetForCenterPage())
.putExtra(EXTRA_WALLPAPER_LAUNCH_SOURCE, "app_launched_launcher");
- if (!Utilities.existsStyleWallpapers(launcher)) {
+ if (!styleWallpapersExists(launcher)) {
intent.putExtra(EXTRA_WALLPAPER_FLAVOR, "wallpaper_only");
} else {
intent.putExtra(EXTRA_WALLPAPER_FLAVOR, "focus_wallpaper");
@@ -299,4 +305,9 @@
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/RecyclerViewFastScroller.java b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
index 6e6512d..3af2e3c 100644
--- a/src/com/android/launcher3/views/RecyclerViewFastScroller.java
+++ b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
@@ -20,8 +20,6 @@
import static androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE;
-import static com.android.launcher3.util.UiThreadHelper.hideKeyboardAsync;
-
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.Resources;
@@ -121,7 +119,6 @@
// prevent jumping, this offset is applied as the user scrolls.
protected int mTouchOffsetY;
protected int mThumbOffsetY;
- protected int mRvOffsetY;
// Fast scroller popup
private TextView mPopupView;
@@ -209,16 +206,11 @@
public void setThumbOffsetY(int y) {
if (mThumbOffsetY == y) {
- int rvCurrentOffsetY = mRv.getCurrentScrollY();
- if (mRvOffsetY != rvCurrentOffsetY) {
- mRvOffsetY = mRv.getCurrentScrollY();
- }
return;
}
updatePopupY(y);
mThumbOffsetY = y;
invalidate();
- mRvOffsetY = mRv.getCurrentScrollY();
}
public int getThumbOffsetY() {
@@ -313,7 +305,7 @@
}
private void calcTouchOffsetAndPrepToFastScroll(int downY, int lastY) {
- hideKeyboardAsync(ActivityContext.lookupContext(getContext()), getWindowToken());
+ ActivityContext.lookupContext(getContext()).hideKeyboard();
mIsDragging = true;
if (mCanThumbDetach) {
mIsThumbDetached = true;
diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java
index 5cffd48..2ac1e94 100644
--- a/src/com/android/launcher3/widget/BaseWidgetSheet.java
+++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.widget;
+import static com.android.launcher3.anim.Interpolators.EMPHASIZED;
+
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
@@ -26,6 +28,7 @@
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import android.view.WindowInsets;
+import android.view.animation.Interpolator;
import android.widget.Toast;
import androidx.annotation.GuardedBy;
@@ -246,6 +249,12 @@
return true;
}
+ @Override
+ protected Interpolator getIdleInterpolator() {
+ return mActivityContext.getDeviceProfile().isTablet
+ ? EMPHASIZED : super.getIdleInterpolator();
+ }
+
//
// Drag related handling methods that implement {@link DragSource} interface.
//
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHost.java b/src/com/android/launcher3/widget/LauncherAppWidgetHost.java
index 5ce8fcf..9c21ea2 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetHost.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetHost.java
@@ -16,281 +16,87 @@
package com.android.launcher3.widget;
-import static android.app.Activity.RESULT_CANCELED;
+import static com.android.launcher3.widget.LauncherWidgetHolder.APPWIDGET_HOST_ID;
import android.appwidget.AppWidgetHost;
-import android.appwidget.AppWidgetHostView;
-import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
-import android.content.ActivityNotFoundException;
import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.Handler;
-import android.util.SparseArray;
-import android.widget.RemoteViews;
-import android.widget.Toast;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.launcher3.BaseActivity;
-import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.model.WidgetsModel;
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.testing.TestLogging;
-import com.android.launcher3.testing.shared.TestProtocol;
-import com.android.launcher3.widget.custom.CustomWidgetManager;
import java.util.ArrayList;
import java.util.function.IntConsumer;
-
/**
* Specific {@link AppWidgetHost} that creates our {@link LauncherAppWidgetHostView}
* which correctly captures all long-press events. This ensures that users can
* always pick up and move widgets.
*/
-public class LauncherAppWidgetHost extends AppWidgetHost {
+class LauncherAppWidgetHost extends AppWidgetHost {
+ @NonNull
+ private final ArrayList<LauncherWidgetHolder.ProviderChangedListener>
+ mProviderChangeListeners = new ArrayList<>();
- private static final int FLAG_LISTENING = 1;
- private static final int FLAG_STATE_IS_NORMAL = 1 << 1;
- private static final int FLAG_ACTIVITY_STARTED = 1 << 2;
- private static final int FLAG_ACTIVITY_RESUMED = 1 << 3;
- private static final int FLAGS_SHOULD_LISTEN =
- FLAG_STATE_IS_NORMAL | FLAG_ACTIVITY_STARTED | FLAG_ACTIVITY_RESUMED;
- // TODO(b/191735836): Replace with ActivityOptions.KEY_SPLASH_SCREEN_STYLE when un-hidden
- private static final String KEY_SPLASH_SCREEN_STYLE = "android.activity.splashScreenStyle";
- // TODO(b/191735836): Replace with SplashScreen.SPLASH_SCREEN_STYLE_EMPTY when un-hidden
- private static final int SPLASH_SCREEN_STYLE_EMPTY = 0;
-
- public static final int APPWIDGET_HOST_ID = 1024;
-
- private final ArrayList<ProviderChangedListener> mProviderChangeListeners = new ArrayList<>();
- private final SparseArray<LauncherAppWidgetHostView> mViews = new SparseArray<>();
- private final SparseArray<PendingAppWidgetHostView> mPendingViews = new SparseArray<>();
- private final SparseArray<LauncherAppWidgetHostView> mDeferredViews = new SparseArray<>();
- private final SparseArray<RemoteViews> mCachedRemoteViews = new SparseArray<>();
-
+ @NonNull
private final Context mContext;
- private int mFlags = FLAG_STATE_IS_NORMAL;
- private IntConsumer mAppWidgetRemovedCallback = null;
+ @Nullable
+ private final IntConsumer mAppWidgetRemovedCallback;
- public LauncherAppWidgetHost(Context context) {
- this(context, null);
- }
+ @NonNull
+ private final LauncherWidgetHolder mHolder;
- public LauncherAppWidgetHost(Context context,
- IntConsumer appWidgetRemovedCallback) {
+ public LauncherAppWidgetHost(@NonNull Context context,
+ @Nullable IntConsumer appWidgetRemovedCallback, @NonNull LauncherWidgetHolder holder) {
super(context, APPWIDGET_HOST_ID);
mContext = context;
mAppWidgetRemovedCallback = appWidgetRemovedCallback;
- }
-
- @Override
- protected LauncherAppWidgetHostView onCreateView(Context context, int appWidgetId,
- AppWidgetProviderInfo appWidget) {
- final LauncherAppWidgetHostView view;
- if (mPendingViews.get(appWidgetId) != null) {
- view = mPendingViews.get(appWidgetId);
- mPendingViews.remove(appWidgetId);
- } else if (mDeferredViews.get(appWidgetId) != null) {
- // In case the widget view is deferred, we will simply return the deferred view as
- // opposed to instantiate a new instance of LauncherAppWidgetHostView since launcher
- // already added the former to the workspace.
- view = mDeferredViews.get(appWidgetId);
- } else {
- view = new LauncherAppWidgetHostView(context);
- }
- mViews.put(appWidgetId, view);
- return view;
- }
-
- @Override
- public void startListening() {
- if (WidgetsModel.GO_DISABLE_WIDGETS) {
- return;
- }
- mFlags |= FLAG_LISTENING;
- try {
- super.startListening();
- } catch (Exception e) {
- if (!Utilities.isBinderSizeError(e)) {
- throw new RuntimeException(e);
- }
- // We're willing to let this slide. The exception is being caused by the list of
- // RemoteViews which is being passed back. The startListening relationship will
- // have been established by this point, and we will end up populating the
- // widgets upon bind anyway. See issue 14255011 for more context.
- }
-
- // We go in reverse order and inflate any deferred or cached widget
- for (int i = mViews.size() - 1; i >= 0; i--) {
- LauncherAppWidgetHostView view = mViews.valueAt(i);
- if (view instanceof DeferredAppWidgetHostView) {
- view.reInflate();
- }
- if (FeatureFlags.ENABLE_CACHED_WIDGET.get()) {
- final int appWidgetId = mViews.keyAt(i);
- if (view == mDeferredViews.get(appWidgetId)) {
- // If the widget view was deferred, we'll need to call super.createView here
- // to make the binder call to system process to fetch cumulative updates to this
- // widget, as well as setting up this view for future updates.
- super.createView(view.mLauncher, appWidgetId, view.getAppWidgetInfo());
- // At this point #onCreateView should have been called, which in turn returned
- // the deferred view. There's no reason to keep the reference anymore, so we
- // removed it here.
- mDeferredViews.remove(appWidgetId);
- }
- }
- }
- }
-
- @Override
- public void stopListening() {
- if (WidgetsModel.GO_DISABLE_WIDGETS) {
- return;
- }
- mFlags &= ~FLAG_LISTENING;
- super.stopListening();
- }
-
- public boolean isListening() {
- return (mFlags & FLAG_LISTENING) != 0;
+ mHolder = holder;
}
/**
- * Sets or unsets a flag the can change whether the widget host should be in the listening
- * state.
+ * Add a listener that is triggered when the providers of the widgets are changed
+ * @param listener The listener that notifies when the providers changed
*/
- private void setShouldListenFlag(int flag, boolean on) {
- if (on) {
- mFlags |= flag;
- } else {
- mFlags &= ~flag;
- }
-
- final boolean listening = isListening();
- if (!listening && (mFlags & FLAGS_SHOULD_LISTEN) == FLAGS_SHOULD_LISTEN) {
- // Postpone starting listening until all flags are on.
- startListening();
- } else if (listening && (mFlags & FLAG_ACTIVITY_STARTED) == 0) {
- // Postpone stopping listening until the activity is stopped.
- stopListening();
- }
+ public void addProviderChangeListener(
+ @NonNull LauncherWidgetHolder.ProviderChangedListener listener) {
+ mProviderChangeListeners.add(listener);
}
/**
- * Registers an "entering/leaving Normal state" event.
+ * Remove the specified listener from the host
+ * @param listener The listener that is to be removed from the host
*/
- public void setStateIsNormal(boolean isNormal) {
- setShouldListenFlag(FLAG_STATE_IS_NORMAL, isNormal);
- }
-
- /**
- * Registers an "activity started/stopped" event.
- */
- public void setActivityStarted(boolean isStarted) {
- setShouldListenFlag(FLAG_ACTIVITY_STARTED, isStarted);
- }
-
- /**
- * Registers an "activity paused/resumed" event.
- */
- public void setActivityResumed(boolean isResumed) {
- setShouldListenFlag(FLAG_ACTIVITY_RESUMED, isResumed);
+ public void removeProviderChangeListener(
+ LauncherWidgetHolder.ProviderChangedListener listener) {
+ mProviderChangeListeners.remove(listener);
}
@Override
- public int allocateAppWidgetId() {
- if (WidgetsModel.GO_DISABLE_WIDGETS) {
- return AppWidgetManager.INVALID_APPWIDGET_ID;
- }
-
- return super.allocateAppWidgetId();
- }
-
- public void addProviderChangeListener(ProviderChangedListener callback) {
- mProviderChangeListeners.add(callback);
- }
-
- public void removeProviderChangeListener(ProviderChangedListener callback) {
- mProviderChangeListeners.remove(callback);
- }
-
protected void onProvidersChanged() {
if (!mProviderChangeListeners.isEmpty()) {
- for (ProviderChangedListener callback : new ArrayList<>(mProviderChangeListeners)) {
+ for (LauncherWidgetHolder.ProviderChangedListener callback :
+ new ArrayList<>(mProviderChangeListeners)) {
callback.notifyWidgetProvidersChanged();
}
}
}
- public void addPendingView(int appWidgetId, PendingAppWidgetHostView view) {
- mPendingViews.put(appWidgetId, view);
- }
-
- public AppWidgetHostView createView(Context context, int appWidgetId,
- LauncherAppWidgetProviderInfo appWidget) {
- if (appWidget.isCustomWidget()) {
- LauncherAppWidgetHostView lahv = new LauncherAppWidgetHostView(context);
- lahv.setAppWidget(0, appWidget);
- CustomWidgetManager.INSTANCE.get(context).onViewCreated(lahv);
- return lahv;
- } else if ((mFlags & FLAG_LISTENING) == 0) {
- // Since the launcher hasn't started listening to widget updates, we can't simply call
- // super.createView here because the later will make a binder call to retrieve
- // RemoteViews from system process.
- // TODO: have launcher always listens to widget updates in background so that this
- // check can be removed altogether.
- if (FeatureFlags.ENABLE_CACHED_WIDGET.get()
- && mCachedRemoteViews.get(appWidgetId) != null) {
- // We've found RemoteViews from cache for this widget, so we will instantiate a
- // widget host view and populate it with the cached RemoteViews.
- final LauncherAppWidgetHostView view = new LauncherAppWidgetHostView(context);
- view.setAppWidget(appWidgetId, appWidget);
- view.updateAppWidget(mCachedRemoteViews.get(appWidgetId));
- mDeferredViews.put(appWidgetId, view);
- mViews.put(appWidgetId, view);
- return view;
- } else {
- // When cache misses, a placeholder for the widget will be returned instead.
- DeferredAppWidgetHostView view = new DeferredAppWidgetHostView(context);
- view.setAppWidget(appWidgetId, appWidget);
- mViews.put(appWidgetId, view);
- return view;
- }
- } else {
- try {
- return super.createView(context, appWidgetId, appWidget);
- } catch (Exception e) {
- if (!Utilities.isBinderSizeError(e)) {
- throw new RuntimeException(e);
- }
-
- // If the exception was thrown while fetching the remote views, let the view stay.
- // This will ensure that if the widget posts a valid update later, the view
- // will update.
- LauncherAppWidgetHostView view = mViews.get(appWidgetId);
- if (view == null) {
- view = onCreateView(mContext, appWidgetId, appWidget);
- }
- view.setAppWidget(appWidgetId, appWidget);
- view.switchToErrorView();
- return view;
- }
- }
+ @Override
+ @NonNull
+ public LauncherAppWidgetHostView onCreateView(Context context, int appWidgetId,
+ AppWidgetProviderInfo appWidget) {
+ return mHolder.onCreateView(context, appWidgetId, appWidget);
}
/**
* Called when the AppWidget provider for a AppWidget has been upgraded to a new apk.
*/
@Override
- protected void onProviderChanged(int appWidgetId, AppWidgetProviderInfo appWidget) {
+ protected void onProviderChanged(int appWidgetId, @NonNull AppWidgetProviderInfo appWidget) {
LauncherAppWidgetProviderInfo info = LauncherAppWidgetProviderInfo.fromProviderInfo(
mContext, appWidget);
super.onProviderChanged(appWidgetId, info);
@@ -304,6 +110,7 @@
*
* @param appWidgetId TODO: make this override when SDK is updated
*/
+ @Override
public void onAppWidgetRemoved(int appWidgetId) {
if (mAppWidgetRemovedCallback == null) {
return;
@@ -311,91 +118,12 @@
mAppWidgetRemovedCallback.accept(appWidgetId);
}
- @Override
- public void deleteAppWidgetId(int appWidgetId) {
- super.deleteAppWidgetId(appWidgetId);
- mViews.remove(appWidgetId);
- }
-
+ /**
+ * The same as super.clearViews(), except with the scope exposed
+ */
@Override
public void clearViews() {
super.clearViews();
- if (FeatureFlags.ENABLE_CACHED_WIDGET.get()) {
- // First, we clear any previously cached content from existing widgets
- mCachedRemoteViews.clear();
- // Then we proceed to cache the content from the widgets
- for (int i = 0; i < mViews.size(); i++) {
- final int appWidgetId = mViews.keyAt(i);
- final LauncherAppWidgetHostView view = mViews.get(appWidgetId);
- mCachedRemoteViews.put(appWidgetId, view.mLastRemoteViews);
- }
- }
- mViews.clear();
}
- public void startBindFlow(BaseActivity activity,
- int appWidgetId, AppWidgetProviderInfo info, int requestCode) {
-
- if (WidgetsModel.GO_DISABLE_WIDGETS) {
- sendActionCancelled(activity, requestCode);
- return;
- }
-
- Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND)
- .putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
- .putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, info.provider)
- .putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER_PROFILE, info.getProfile());
- // TODO: we need to make sure that this accounts for the options bundle.
- // intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options);
- activity.startActivityForResult(intent, requestCode);
- }
-
- /**
- * Launches an app widget's configuration activity.
- * @param activity The activity from which to launch the configuration activity
- * @param widgetId The id of the bound app widget to be configured
- * @param requestCode An optional request code to be returned with the result
- */
- public void startConfigActivity(BaseDraggingActivity activity, int widgetId, int requestCode) {
- if (WidgetsModel.GO_DISABLE_WIDGETS) {
- sendActionCancelled(activity, requestCode);
- return;
- }
-
- try {
- TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "start: startConfigActivity");
- startAppWidgetConfigureActivityForResult(activity, widgetId, 0, requestCode,
- getConfigurationActivityOptions(activity, widgetId));
- } catch (ActivityNotFoundException | SecurityException e) {
- Toast.makeText(activity, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
- sendActionCancelled(activity, requestCode);
- }
- }
-
- /**
- * Returns an {@link android.app.ActivityOptions} bundle from the {code activity} for launching
- * the configuration of the {@code widgetId} app widget, or null of options cannot be produced.
- */
- @Nullable
- private Bundle getConfigurationActivityOptions(BaseDraggingActivity activity, int widgetId) {
- LauncherAppWidgetHostView view = mViews.get(widgetId);
- if (view == null) return null;
- Object tag = view.getTag();
- if (!(tag instanceof ItemInfo)) return null;
- Bundle bundle = activity.getActivityLaunchOptions(view, (ItemInfo) tag).toBundle();
- bundle.putInt(KEY_SPLASH_SCREEN_STYLE, SPLASH_SCREEN_STYLE_EMPTY);
- return bundle;
- }
-
- private void sendActionCancelled(final BaseActivity activity, final int requestCode) {
- new Handler().post(() -> activity.onActivityResult(requestCode, RESULT_CANCELED, null));
- }
-
- /**
- * Listener for getting notifications on provider changes.
- */
- public interface ProviderChangedListener {
-
- void notifyWidgetProvidersChanged();
- }
}
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
index fc1e880..bc3889f 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
@@ -92,10 +92,6 @@
// The following member variables are only used during drag-n-drop.
private boolean mIsInDragMode = false;
- /** The drag content width which is only set when the drag content scale is not 1f. */
- private int mDragContentWidth = 0;
- /** The drag content height which is only set when the drag content scale is not 1f. */
- private int mDragContentHeight = 0;
private boolean mTrackingWidgetUpdate = false;
@@ -314,27 +310,9 @@
}
}
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- if (mIsInDragMode && mDragContentWidth > 0 && mDragContentHeight > 0
- && getChildCount() == 1) {
- measureChild(getChildAt(0), MeasureSpec.getSize(mDragContentWidth),
- MeasureSpec.getSize(mDragContentHeight));
- }
- }
-
/** Starts the drag mode. */
public void startDrag() {
mIsInDragMode = true;
- // In the case of dragging a scaled preview from widgets picker, we should reuse the
- // previously measured dimension from WidgetCell#measureAndComputeWidgetPreviewScale, which
- // measures the dimension of a widget preview without its parent's bound before scaling
- // down.
- if ((getScaleX() != 1f || getScaleY() != 1f) && getChildCount() == 1) {
- mDragContentWidth = getChildAt(0).getMeasuredWidth();
- mDragContentHeight = getChildAt(0).getMeasuredHeight();
- }
}
/** Handles a drag event occurred on a workspace page corresponding to the {@code screenId}. */
@@ -347,8 +325,6 @@
/** Ends the drag mode. */
public void endDrag() {
mIsInDragMode = false;
- mDragContentWidth = 0;
- mDragContentHeight = 0;
requestLayout();
}
diff --git a/src/com/android/launcher3/widget/LauncherWidgetHolder.java b/src/com/android/launcher3/widget/LauncherWidgetHolder.java
new file mode 100644
index 0000000..5497729
--- /dev/null
+++ b/src/com/android/launcher3/widget/LauncherWidgetHolder.java
@@ -0,0 +1,508 @@
+/**
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.widget;
+
+import static android.app.Activity.RESULT_CANCELED;
+
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+
+import android.app.PendingIntent;
+import android.appwidget.AppWidgetHost;
+import android.appwidget.AppWidgetHostView;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.SparseArray;
+import android.view.View;
+import android.widget.RemoteViews;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.BaseActivity;
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.model.WidgetsModel;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.shared.TestProtocol;
+import com.android.launcher3.uioverrides.ApiWrapper;
+import com.android.launcher3.widget.custom.CustomWidgetManager;
+
+import java.util.function.IntConsumer;
+
+/**
+ * A wrapper for LauncherAppWidgetHost. This class is created so the AppWidgetHost could run in
+ * background.
+ */
+public class LauncherWidgetHolder {
+ public static final int APPWIDGET_HOST_ID = 1024;
+
+ private static final int FLAG_LISTENING = 1;
+ private static final int FLAG_STATE_IS_NORMAL = 1 << 1;
+ private static final int FLAG_ACTIVITY_STARTED = 1 << 2;
+ private static final int FLAG_ACTIVITY_RESUMED = 1 << 3;
+ private static final int FLAGS_SHOULD_LISTEN =
+ FLAG_STATE_IS_NORMAL | FLAG_ACTIVITY_STARTED | FLAG_ACTIVITY_RESUMED;
+
+ @NonNull
+ private final Context mContext;
+
+ @NonNull
+ private final AppWidgetHost mWidgetHost;
+
+ @NonNull
+ private final SparseArray<LauncherAppWidgetHostView> mViews = new SparseArray<>();
+ @NonNull
+ private final SparseArray<PendingAppWidgetHostView> mPendingViews = new SparseArray<>();
+ @NonNull
+ private final SparseArray<LauncherAppWidgetHostView> mDeferredViews = new SparseArray<>();
+ @NonNull
+ private final SparseArray<RemoteViews> mCachedRemoteViews = new SparseArray<>();
+
+ private int mFlags = FLAG_STATE_IS_NORMAL;
+
+ // TODO(b/191735836): Replace with ActivityOptions.KEY_SPLASH_SCREEN_STYLE when un-hidden
+ private static final String KEY_SPLASH_SCREEN_STYLE = "android.activity.splashScreenStyle";
+ // TODO(b/191735836): Replace with SplashScreen.SPLASH_SCREEN_STYLE_EMPTY when un-hidden
+ private static final int SPLASH_SCREEN_STYLE_EMPTY = 0;
+
+ public LauncherWidgetHolder(@NonNull Context context) {
+ this(context, null);
+ }
+
+ public LauncherWidgetHolder(@NonNull Context context,
+ @Nullable IntConsumer appWidgetRemovedCallback) {
+ mContext = context;
+ mWidgetHost = createHost(context, appWidgetRemovedCallback);
+ }
+
+ protected AppWidgetHost createHost(
+ Context context, @Nullable IntConsumer appWidgetRemovedCallback) {
+ return new LauncherAppWidgetHost(context, appWidgetRemovedCallback, this);
+ }
+
+ /**
+ * Starts listening to the widget updates from the server side
+ */
+ public void startListening() {
+ if (WidgetsModel.GO_DISABLE_WIDGETS) {
+ return;
+ }
+ setListeningFlag(true);
+ try {
+ mWidgetHost.startListening();
+ } catch (Exception e) {
+ if (!Utilities.isBinderSizeError(e)) {
+ throw new RuntimeException(e);
+ }
+ // We're willing to let this slide. The exception is being caused by the list of
+ // RemoteViews which is being passed back. The startListening relationship will
+ // have been established by this point, and we will end up populating the
+ // widgets upon bind anyway. See issue 14255011 for more context.
+ }
+
+ // We go in reverse order and inflate any deferred or cached widget
+ for (int i = mViews.size() - 1; i >= 0; i--) {
+ LauncherAppWidgetHostView view = mViews.valueAt(i);
+ if (view instanceof DeferredAppWidgetHostView) {
+ view.reInflate();
+ }
+ if (FeatureFlags.ENABLE_CACHED_WIDGET.get()) {
+ final int appWidgetId = mViews.keyAt(i);
+ if (view == mDeferredViews.get(appWidgetId)) {
+ // If the widget view was deferred, we'll need to call super.createView here
+ // to make the binder call to system process to fetch cumulative updates to this
+ // widget, as well as setting up this view for future updates.
+ mWidgetHost.createView(view.mLauncher, appWidgetId,
+ view.getAppWidgetInfo());
+ // At this point #onCreateView should have been called, which in turn returned
+ // the deferred view. There's no reason to keep the reference anymore, so we
+ // removed it here.
+ mDeferredViews.remove(appWidgetId);
+ }
+ }
+ }
+ }
+
+ /**
+ * Registers an "activity started/stopped" event.
+ */
+ public void setActivityStarted(boolean isStarted) {
+ setShouldListenFlag(FLAG_ACTIVITY_STARTED, isStarted);
+ }
+
+ /**
+ * Registers an "activity paused/resumed" event.
+ */
+ public void setActivityResumed(boolean isResumed) {
+ setShouldListenFlag(FLAG_ACTIVITY_RESUMED, isResumed);
+ }
+
+ /**
+ * Set the NORMAL state of the widget host
+ * @param isNormal True if setting the host to be in normal state, false otherwise
+ */
+ public void setStateIsNormal(boolean isNormal) {
+ setShouldListenFlag(FLAG_STATE_IS_NORMAL, isNormal);
+ }
+
+ /**
+ * Delete the specified app widget from the host
+ * @param appWidgetId The ID of the app widget to be deleted
+ */
+ public void deleteAppWidgetId(int appWidgetId) {
+ mWidgetHost.deleteAppWidgetId(appWidgetId);
+ mViews.remove(appWidgetId);
+ }
+
+ /**
+ * Add the pending view to the host for complete configuration in further steps
+ * @param appWidgetId The ID of the specified app widget
+ * @param view The {@link PendingAppWidgetHostView} of the app widget
+ */
+ public void addPendingView(int appWidgetId, @NonNull PendingAppWidgetHostView view) {
+ mPendingViews.put(appWidgetId, view);
+ }
+
+ /**
+ * @param appWidgetId The app widget id of the specified widget
+ * @return The {@link PendingAppWidgetHostView} of the widget if it exists, null otherwise
+ */
+ @Nullable
+ protected PendingAppWidgetHostView getPendingView(int appWidgetId) {
+ return mPendingViews.get(appWidgetId);
+ }
+
+ protected void removePendingView(int appWidgetId) {
+ mPendingViews.remove(appWidgetId);
+ }
+
+ /**
+ * Called when the launcher is destroyed
+ */
+ public void destroy() {
+ // No-op
+ }
+
+ /**
+ * @return The allocated app widget id if allocation is successful, returns -1 otherwise
+ */
+ public int allocateAppWidgetId() {
+ if (WidgetsModel.GO_DISABLE_WIDGETS) {
+ return AppWidgetManager.INVALID_APPWIDGET_ID;
+ }
+
+ return mWidgetHost.allocateAppWidgetId();
+ }
+
+ /**
+ * Add a listener that is triggered when the providers of the widgets are changed
+ * @param listener The listener that notifies when the providers changed
+ */
+ public void addProviderChangeListener(@NonNull ProviderChangedListener listener) {
+ LauncherAppWidgetHost tempHost = (LauncherAppWidgetHost) mWidgetHost;
+ tempHost.addProviderChangeListener(listener);
+ }
+
+ /**
+ * Remove the specified listener from the host
+ * @param listener The listener that is to be removed from the host
+ */
+ public void removeProviderChangeListener(ProviderChangedListener listener) {
+ LauncherAppWidgetHost tempHost = (LauncherAppWidgetHost) mWidgetHost;
+ tempHost.removeProviderChangeListener(listener);
+ }
+
+ /**
+ * Starts the configuration activity for the widget
+ * @param activity The activity in which to start the configuration page
+ * @param widgetId The ID of the widget
+ * @param requestCode The request code
+ */
+ public void startConfigActivity(@NonNull BaseDraggingActivity activity, int widgetId,
+ int requestCode) {
+ if (WidgetsModel.GO_DISABLE_WIDGETS) {
+ sendActionCancelled(activity, requestCode);
+ return;
+ }
+
+ try {
+ TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "start: startConfigActivity");
+ mWidgetHost.startAppWidgetConfigureActivityForResult(activity, widgetId, 0, requestCode,
+ getConfigurationActivityOptions(activity, widgetId));
+ } catch (ActivityNotFoundException | SecurityException e) {
+ Toast.makeText(activity, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
+ sendActionCancelled(activity, requestCode);
+ }
+ }
+
+ private void sendActionCancelled(final BaseActivity activity, final int requestCode) {
+ MAIN_EXECUTOR.execute(
+ () -> activity.onActivityResult(requestCode, RESULT_CANCELED, null));
+ }
+
+ /**
+ * Returns an {@link android.app.ActivityOptions} bundle from the {code activity} for launching
+ * the configuration of the {@code widgetId} app widget, or null of options cannot be produced.
+ */
+ @Nullable
+ protected Bundle getConfigurationActivityOptions(@NonNull BaseDraggingActivity activity,
+ int widgetId) {
+ LauncherAppWidgetHostView view = mViews.get(widgetId);
+ if (view == null) return null;
+ Object tag = view.getTag();
+ if (!(tag instanceof ItemInfo)) return null;
+ Bundle bundle = activity.getActivityLaunchOptions(view, (ItemInfo) tag).toBundle();
+ bundle.putInt(KEY_SPLASH_SCREEN_STYLE, SPLASH_SCREEN_STYLE_EMPTY);
+ return bundle;
+ }
+
+ /**
+ * Starts the binding flow for the widget
+ * @param activity The activity for which to bind the widget
+ * @param appWidgetId The ID of the widget
+ * @param info The {@link AppWidgetProviderInfo} of the widget
+ * @param requestCode The request code
+ */
+ public void startBindFlow(@NonNull BaseActivity activity,
+ int appWidgetId, @NonNull AppWidgetProviderInfo info, int requestCode) {
+ if (WidgetsModel.GO_DISABLE_WIDGETS) {
+ sendActionCancelled(activity, requestCode);
+ return;
+ }
+
+ Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND)
+ .putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
+ .putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, info.provider)
+ .putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER_PROFILE, info.getProfile());
+ // TODO: we need to make sure that this accounts for the options bundle.
+ // intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options);
+ activity.startActivityForResult(intent, requestCode);
+ }
+
+ /**
+ * Stop the host from listening to the widget updates
+ */
+ public void stopListening() {
+ if (WidgetsModel.GO_DISABLE_WIDGETS) {
+ return;
+ }
+
+ mWidgetHost.stopListening();
+ setListeningFlag(false);
+ }
+
+ protected void setListeningFlag(final boolean isListening) {
+ if (isListening) {
+ mFlags |= FLAG_LISTENING;
+ return;
+ }
+ mFlags &= ~FLAG_LISTENING;
+ }
+
+ /**
+ * Set the interaction handler for the widget host
+ * @param handler The interaction handler
+ */
+ public void setInteractionHandler(
+ @Nullable LauncherWidgetInteractionHandler handler) {
+ ApiWrapper.setHostInteractionHandler(mWidgetHost, handler);
+ }
+
+ /**
+ * Delete the host
+ */
+ public void deleteHost() {
+ mWidgetHost.deleteHost();
+ }
+
+ /**
+ * @return The app widget ids
+ */
+ @NonNull
+ public int[] getAppWidgetIds() {
+ return mWidgetHost.getAppWidgetIds();
+ }
+
+ /**
+ * Create a view for the specified app widget
+ * @param context The activity context for which the view is created
+ * @param appWidgetId The ID of the widget
+ * @param appWidget The {@link LauncherAppWidgetProviderInfo} of the widget
+ * @return A view for the widget
+ */
+ @NonNull
+ public AppWidgetHostView createView(@NonNull Context context, int appWidgetId,
+ @NonNull LauncherAppWidgetProviderInfo appWidget) {
+ if (appWidget.isCustomWidget()) {
+ LauncherAppWidgetHostView lahv = new LauncherAppWidgetHostView(context);
+ lahv.setAppWidget(0, appWidget);
+ CustomWidgetManager.INSTANCE.get(context).onViewCreated(lahv);
+ return lahv;
+ } else if ((mFlags & FLAG_LISTENING) == 0) {
+ // Since the launcher hasn't started listening to widget updates, we can't simply call
+ // super.createView here because the later will make a binder call to retrieve
+ // RemoteViews from system process.
+ // TODO: have launcher always listens to widget updates in background so that this
+ // check can be removed altogether.
+ if (FeatureFlags.ENABLE_CACHED_WIDGET.get()
+ && mCachedRemoteViews.get(appWidgetId) != null) {
+ // We've found RemoteViews from cache for this widget, so we will instantiate a
+ // widget host view and populate it with the cached RemoteViews.
+ final LauncherAppWidgetHostView view = new LauncherAppWidgetHostView(context);
+ view.setAppWidget(appWidgetId, appWidget);
+ view.updateAppWidget(mCachedRemoteViews.get(appWidgetId));
+ mDeferredViews.put(appWidgetId, view);
+ mViews.put(appWidgetId, view);
+ return view;
+ } else {
+ // When cache misses, a placeholder for the widget will be returned instead.
+ DeferredAppWidgetHostView view = new DeferredAppWidgetHostView(context);
+ view.setAppWidget(appWidgetId, appWidget);
+ mViews.put(appWidgetId, view);
+ return view;
+ }
+ } else {
+ try {
+ return mWidgetHost.createView(context, appWidgetId, appWidget);
+ } catch (Exception e) {
+ if (!Utilities.isBinderSizeError(e)) {
+ throw new RuntimeException(e);
+ }
+
+ // If the exception was thrown while fetching the remote views, let the view stay.
+ // This will ensure that if the widget posts a valid update later, the view
+ // will update.
+ LauncherAppWidgetHostView view = mViews.get(appWidgetId);
+ if (view == null) {
+ view = onCreateView(mContext, appWidgetId, appWidget);
+ }
+ view.setAppWidget(appWidgetId, appWidget);
+ view.switchToErrorView();
+ return view;
+ }
+ }
+ }
+
+ /**
+ * Listener for getting notifications on provider changes.
+ */
+ public interface ProviderChangedListener {
+ /**
+ * Notify the listener that the providers have changed
+ */
+ void notifyWidgetProvidersChanged();
+ }
+
+ /**
+ * Called to return a proper view when creating a view
+ * @param context The context for which the widget view is created
+ * @param appWidgetId The ID of the added widget
+ * @param appWidget The provider info of the added widget
+ * @return A view for the specified app widget
+ */
+ @NonNull
+ public LauncherAppWidgetHostView onCreateView(Context context, int appWidgetId,
+ AppWidgetProviderInfo appWidget) {
+ final LauncherAppWidgetHostView view;
+ if (getPendingView(appWidgetId) != null) {
+ view = getPendingView(appWidgetId);
+ removePendingView(appWidgetId);
+ } else if (mDeferredViews.get(appWidgetId) != null) {
+ // In case the widget view is deferred, we will simply return the deferred view as
+ // opposed to instantiate a new instance of LauncherAppWidgetHostView since launcher
+ // already added the former to the workspace.
+ view = mDeferredViews.get(appWidgetId);
+ } else {
+ view = new LauncherAppWidgetHostView(context);
+ }
+ mViews.put(appWidgetId, view);
+ return view;
+ }
+
+ /**
+ * Clears all the views from the host
+ */
+ public void clearViews() {
+ LauncherAppWidgetHost tempHost = (LauncherAppWidgetHost) mWidgetHost;
+ tempHost.clearViews();
+ if (FeatureFlags.ENABLE_CACHED_WIDGET.get()) {
+ // First, we clear any previously cached content from existing widgets
+ mCachedRemoteViews.clear();
+ mDeferredViews.clear();
+ // Then we proceed to cache the content from the widgets
+ for (int i = 0; i < mViews.size(); i++) {
+ final int appWidgetId = mViews.keyAt(i);
+ final LauncherAppWidgetHostView view = mViews.get(appWidgetId);
+ mCachedRemoteViews.put(appWidgetId, view.mLastRemoteViews);
+ }
+ }
+ mViews.clear();
+ }
+
+ /**
+ * @return True if the host is listening to the updates, false otherwise
+ */
+ public boolean isListening() {
+ return (mFlags & FLAG_LISTENING) != 0;
+ }
+
+ /**
+ * Sets or unsets a flag the can change whether the widget host should be in the listening
+ * state.
+ */
+ private void setShouldListenFlag(int flag, boolean on) {
+ if (on) {
+ mFlags |= flag;
+ } else {
+ mFlags &= ~flag;
+ }
+
+ final boolean listening = isListening();
+ if (!listening && (mFlags & FLAGS_SHOULD_LISTEN) == FLAGS_SHOULD_LISTEN) {
+ // Postpone starting listening until all flags are on.
+ startListening();
+ } else if (listening && (mFlags & FLAG_ACTIVITY_STARTED) == 0) {
+ // Postpone stopping listening until the activity is stopped.
+ stopListening();
+ }
+ }
+
+ /**
+ * Set as a substitution for the hidden interaction handler in RemoteViews
+ */
+ public interface LauncherWidgetInteractionHandler {
+ /**
+ * Invoked when the user performs an interaction on the View.
+ *
+ * @param view the View with which the user interacted
+ * @param pendingIntent the base PendingIntent associated with the view
+ * @param response the response to the interaction, which knows how to fill in the
+ * attached PendingIntent
+ */
+ boolean onInteraction(
+ View view,
+ PendingIntent pendingIntent,
+ RemoteViews.RemoteResponse response);
+ }
+}
diff --git a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
index 470a800..ccf4b2e 100644
--- a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
+++ b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
@@ -19,6 +19,9 @@
import android.content.Context;
import android.os.Bundle;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.PendingAddItemInfo;
import com.android.launcher3.logger.LauncherAtom;
@@ -66,8 +69,9 @@
return WidgetSizes.getWidgetSizeOptions(context, componentName, spanX, spanY);
}
+ @NonNull
@Override
- public LauncherAtom.ItemInfo buildProto(FolderInfo folderInfo) {
+ public LauncherAtom.ItemInfo buildProto(@Nullable FolderInfo folderInfo) {
LauncherAtom.ItemInfo info = super.buildProto(folderInfo);
return info.toBuilder()
.addItemAttributes(LauncherAppWidgetInfo.getAttribute(sourceContainer))
diff --git a/src/com/android/launcher3/widget/PendingItemDragHelper.java b/src/com/android/launcher3/widget/PendingItemDragHelper.java
index c8d528b..bbbc329 100644
--- a/src/com/android/launcher3/widget/PendingItemDragHelper.java
+++ b/src/com/android/launcher3/widget/PendingItemDragHelper.java
@@ -39,6 +39,7 @@
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.dragndrop.DraggableView;
import com.android.launcher3.graphics.DragPreviewProvider;
+import com.android.launcher3.icons.BaseIconFactory;
import com.android.launcher3.icons.FastBitmapDrawable;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.icons.RoundDrawableWrapper;
@@ -181,7 +182,8 @@
PendingAddShortcutInfo createShortcutInfo = (PendingAddShortcutInfo) mAddInfo;
Drawable icon = createShortcutInfo.activityInfo.getFullResIcon(app.getIconCache());
LauncherIcons li = LauncherIcons.obtain(launcher);
- preview = new FastBitmapDrawable(li.createScaledBitmapWithoutShadow(icon));
+ preview = new FastBitmapDrawable(
+ li.createScaledBitmap(icon, BaseIconFactory.MODE_DEFAULT));
previewWidth = preview.getIntrinsicWidth();
previewHeight = preview.getIntrinsicHeight();
li.recycle();
diff --git a/src/com/android/launcher3/widget/WidgetAddFlowHandler.java b/src/com/android/launcher3/widget/WidgetAddFlowHandler.java
index 9313266..9319a9c 100644
--- a/src/com/android/launcher3/widget/WidgetAddFlowHandler.java
+++ b/src/com/android/launcher3/widget/WidgetAddFlowHandler.java
@@ -55,7 +55,7 @@
public void startBindFlow(Launcher launcher, int appWidgetId, ItemInfo info, int requestCode) {
launcher.setWaitingForResult(PendingRequestArgs.forWidgetInfo(appWidgetId, this, info));
- launcher.getAppWidgetHost()
+ launcher.getAppWidgetHolder()
.startBindFlow(launcher, appWidgetId, mProviderInfo, requestCode);
}
@@ -77,7 +77,7 @@
return false;
}
launcher.setWaitingForResult(PendingRequestArgs.forWidgetInfo(appWidgetId, this, info));
- launcher.getAppWidgetHost().startConfigActivity(launcher, appWidgetId, requestCode);
+ launcher.getAppWidgetHolder().startConfigActivity(launcher, appWidgetId, requestCode);
return true;
}
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index 2796721..ce47d70 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -284,6 +284,40 @@
ensurePreviewWithCallback(callback, cachedPreview);
}
+ private static class ScaledAppWidgetHostView extends LauncherAppWidgetHostView {
+ private boolean mKeepOrigForDragging = true;
+
+ ScaledAppWidgetHostView(Context context) {
+ super(context);
+ }
+
+ /**
+ * Set if the view will keep its original scale when dragged
+ * @param isKeepOrig True if keep original scale when dragged, false otherwise
+ */
+ public void setKeepOrigForDragging(boolean isKeepOrig) {
+ mKeepOrigForDragging = isKeepOrig;
+ }
+
+ /**
+ * @return True if the view is set to preserve original scale when dragged, false otherwise
+ */
+ public boolean isKeepOrigForDragging() {
+ return mKeepOrigForDragging;
+ }
+
+ @Override
+ public void startDrag() {
+ super.startDrag();
+ if (!isKeepOrigForDragging()) {
+ // restore to original scale when being dragged, if set to do so
+ setScaleToFit(1.0f);
+ }
+ // When the drag start, translations need to be set to zero to center the view
+ setTranslationForCentering(0f, 0f);
+ }
+ }
+
private void applyPreviewOnAppWidgetHostView(WidgetItem item) {
if (mRemoteViewsPreview != null) {
mAppWidgetHostViewPreview = createAppWidgetHostView(getContext());
@@ -299,7 +333,7 @@
// a preview during drag & drop. And thus, we should use LauncherAppWidgetHostView, which
// supports applying local color extraction during drag & drop.
mAppWidgetHostViewPreview = isLauncherContext(context)
- ? new LauncherAppWidgetHostView(context)
+ ? new ScaledAppWidgetHostView(context)
: createAppWidgetHostView(context);
LauncherAppWidgetProviderInfo launcherAppWidgetProviderInfo =
LauncherAppWidgetProviderInfo.fromProviderInfo(context, item.widgetInfo.clone());
@@ -398,23 +432,41 @@
int containerWidth = (int) (mTargetPreviewWidth * mPreviewContainerScale);
int containerHeight = (int) (mTargetPreviewHeight * mPreviewContainerScale);
setContainerSize(containerWidth, containerHeight);
+ boolean shouldMeasureAndScale = false;
if (mAppWidgetHostViewPreview.getChildCount() == 1) {
View widgetContent = mAppWidgetHostViewPreview.getChildAt(0);
ViewGroup.LayoutParams layoutParams = widgetContent.getLayoutParams();
// We only scale preview if both the width & height of the outermost view group are
// not set to MATCH_PARENT.
- boolean shouldScale =
+ shouldMeasureAndScale =
layoutParams.width != MATCH_PARENT && layoutParams.height != MATCH_PARENT;
- if (shouldScale) {
+ if (shouldMeasureAndScale) {
setNoClip(mWidgetImageContainer);
setNoClip(mAppWidgetHostViewPreview);
mAppWidgetHostViewScale = measureAndComputeWidgetPreviewScale();
- mAppWidgetHostViewPreview.setScaleToFit(mAppWidgetHostViewScale);
}
}
+
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
- containerWidth, containerHeight, Gravity.FILL);
+ mTargetPreviewWidth, mTargetPreviewHeight, Gravity.FILL);
mAppWidgetHostViewPreview.setLayoutParams(params);
+
+ if (!shouldMeasureAndScale
+ && mAppWidgetHostViewPreview instanceof ScaledAppWidgetHostView) {
+ // If the view is not measured & scaled, at least one side will match the grid size,
+ // so it should be safe to restore the original scale once it is dragged.
+ ScaledAppWidgetHostView tempView =
+ (ScaledAppWidgetHostView) mAppWidgetHostViewPreview;
+ tempView.setKeepOrigForDragging(false);
+ tempView.setScaleToFit(mPreviewContainerScale);
+ } else if (!shouldMeasureAndScale) {
+ mAppWidgetHostViewPreview.setScaleToFit(mPreviewContainerScale);
+ } else {
+ mAppWidgetHostViewPreview.setScaleToFit(mAppWidgetHostViewScale);
+ }
+ mAppWidgetHostViewPreview.setTranslationForCentering(
+ -(params.width - (params.width * mPreviewContainerScale)) / 2.0f,
+ -(params.height - (params.height * mPreviewContainerScale)) / 2.0f);
mWidgetImageContainer.addView(mAppWidgetHostViewPreview, /* index= */ 0);
mWidgetImage.setVisibility(View.GONE);
applyPreview(null);
diff --git a/src/com/android/launcher3/widget/WidgetHostViewLoader.java b/src/com/android/launcher3/widget/WidgetHostViewLoader.java
index 46141e0..b18cd47 100644
--- a/src/com/android/launcher3/widget/WidgetHostViewLoader.java
+++ b/src/com/android/launcher3/widget/WidgetHostViewLoader.java
@@ -59,7 +59,7 @@
// Cleanup widget id
if (mWidgetLoadingId != -1) {
- mLauncher.getAppWidgetHost().deleteAppWidgetId(mWidgetLoadingId);
+ mLauncher.getAppWidgetHolder().deleteAppWidgetId(mWidgetLoadingId);
mWidgetLoadingId = -1;
}
@@ -69,7 +69,7 @@
Log.d(TAG, "...removing widget from drag layer");
}
mLauncher.getDragLayer().removeView(mInfo.boundWidget);
- mLauncher.getAppWidgetHost().deleteAppWidgetId(mInfo.boundWidget.getAppWidgetId());
+ mLauncher.getAppWidgetHolder().deleteAppWidgetId(mInfo.boundWidget.getAppWidgetId());
mInfo.boundWidget = null;
}
}
@@ -94,7 +94,7 @@
mBindWidgetRunnable = new Runnable() {
@Override
public void run() {
- mWidgetLoadingId = mLauncher.getAppWidgetHost().allocateAppWidgetId();
+ mWidgetLoadingId = mLauncher.getAppWidgetHolder().allocateAppWidgetId();
if (LOGD) {
Log.d(TAG, "Binding widget, id: " + mWidgetLoadingId);
}
@@ -116,7 +116,7 @@
if (mWidgetLoadingId == -1) {
return;
}
- AppWidgetHostView hostView = mLauncher.getAppWidgetHost().createView(
+ AppWidgetHostView hostView = mLauncher.getAppWidgetHolder().createView(
(Context) mLauncher, mWidgetLoadingId, pInfo);
mInfo.boundWidget = hostView;
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index 0d9198f..72ec629 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -65,7 +65,7 @@
import com.android.launcher3.views.StickyHeaderLayout;
import com.android.launcher3.views.WidgetsEduView;
import com.android.launcher3.widget.BaseWidgetSheet;
-import com.android.launcher3.widget.LauncherAppWidgetHost.ProviderChangedListener;
+import com.android.launcher3.widget.LauncherWidgetHolder.ProviderChangedListener;
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
import com.android.launcher3.widget.picker.search.SearchModeListener;
import com.android.launcher3.widget.picker.search.WidgetsSearchBar;
@@ -85,7 +85,6 @@
implements ProviderChangedListener, OnActivePageChangedListener,
WidgetsRecyclerView.HeaderViewDimensionsProvider, SearchModeListener {
- private static final long DEFAULT_OPEN_DURATION = 267;
private static final long FADE_IN_DURATION = 150;
private static final long EDUCATION_TIP_DELAY_MS = 200;
private static final long EDUCATION_DIALOG_DELAY_MS = 500;
@@ -322,7 +321,7 @@
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- mActivityContext.getAppWidgetHost().addProviderChangeListener(this);
+ mActivityContext.getAppWidgetHolder().addProviderChangeListener(this);
notifyWidgetProvidersChanged();
onRecommendedWidgetsBound();
}
@@ -330,7 +329,7 @@
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
- mActivityContext.getAppWidgetHost().removeProviderChangeListener(this);
+ mActivityContext.getAppWidgetHolder().removeProviderChangeListener(this);
mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView
.removeOnAttachStateChangeListener(mBindScrollbarInSearchMode);
if (mHasWorkProfile) {
@@ -582,7 +581,7 @@
mOpenCloseAnimator.setValues(
PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
mOpenCloseAnimator
- .setDuration(DEFAULT_OPEN_DURATION)
+ .setDuration(mActivityContext.getDeviceProfile().bottomSheetOpenDuration)
.setInterpolator(AnimationUtils.loadInterpolator(
getContext(), android.R.interpolator.linear_out_slow_in));
mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() {
@@ -603,7 +602,7 @@
@Override
protected void handleClose(boolean animate) {
- handleClose(animate, DEFAULT_OPEN_DURATION);
+ handleClose(animate, mActivityContext.getDeviceProfile().bottomSheetCloseDuration);
}
@Override
diff --git a/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
index 35fa7a4..5969e3e 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
@@ -19,7 +19,6 @@
import android.content.Context;
import android.graphics.Point;
import android.util.AttributeSet;
-import android.util.SparseIntArray;
import android.view.MotionEvent;
import androidx.recyclerview.widget.LinearLayoutManager;
@@ -28,6 +27,7 @@
import com.android.launcher3.FastScrollRecyclerView;
import com.android.launcher3.R;
+import com.android.launcher3.util.ScrollableLayoutManager;
/**
* The widgets recycler view.
@@ -42,14 +42,6 @@
private boolean mTouchDownOnScroller;
private HeaderViewDimensionsProvider mHeaderViewDimensionsProvider;
- /**
- * There is always 1 or 0 item of VIEW_TYPE_WIDGETS_LIST. Other types have fixes sizes, so the
- * the size can be used for all other items of same type. Caching the last know size for
- * VIEW_TYPE_WIDGETS_LIST allows us to use it to estimate full size even when
- * VIEW_TYPE_WIDGETS_LIST is not visible on the screen.
- */
- private final SparseIntArray mCachedSizes = new SparseIntArray();
-
public WidgetsRecyclerView(Context context) {
this(context, null);
}
@@ -68,9 +60,7 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- // create a layout manager with Launcher's context so that scroll position
- // can be preserved during screen rotation.
- setLayoutManager(new LinearLayoutManager(getContext()));
+ setLayoutManager(new ScrollableLayoutManager(getContext()));
}
@Override
@@ -114,7 +104,7 @@
}
// Skip early if, there no child laid out in the container.
- int scrollY = getCurrentScrollY();
+ int scrollY = computeVerticalScrollOffset();
if (scrollY < 0) {
mScrollbar.setThumbOffsetY(-1);
return;
@@ -164,39 +154,6 @@
}
/**
- * Returns the sum of the height, in pixels, of this list adapter's items from index 0 until
- * {@code untilIndex}.
- *
- * <p>If the untilIndex is larger than the total number of items in this adapter, returns the
- * sum of all items' height.
- */
- @Override
- protected int getItemsHeight(int untilIndex) {
- // Initialize cache
- int childCount = getChildCount();
- int startPosition;
- if (childCount > 0
- && ((startPosition = getChildAdapterPosition(getChildAt(0))) != NO_POSITION)) {
- int loopCount = Math.min(getChildCount(), getAdapter().getItemCount() - startPosition);
- for (int i = 0; i < loopCount; i++) {
- mCachedSizes.put(
- mAdapter.getItemViewType(startPosition + i),
- getChildAt(i).getMeasuredHeight());
- }
- }
-
- if (untilIndex > mAdapter.getItems().size()) {
- untilIndex = mAdapter.getItems().size();
- }
- int totalItemsHeight = 0;
- for (int i = 0; i < untilIndex; i++) {
- int type = mAdapter.getItemViewType(i);
- totalItemsHeight += mCachedSizes.get(type);
- }
- return totalItemsHeight;
- }
-
- /**
* Provides dimensions of the header view that is shown at the top of a
* {@link WidgetsRecyclerView}.
*/
diff --git a/src_plugins/com/android/systemui/plugins/shared/LauncherExterns.java b/src_plugins/com/android/systemui/plugins/shared/LauncherExterns.java
index 13e4999..173b454 100644
--- a/src_plugins/com/android/systemui/plugins/shared/LauncherExterns.java
+++ b/src_plugins/com/android/systemui/plugins/shared/LauncherExterns.java
@@ -40,10 +40,4 @@
* Sets the overlay on the target activity
*/
void setLauncherOverlay(LauncherOverlay overlay);
-
- /**
- * Executes the command, next time the overlay is hidden
- */
- void runOnOverlayHidden(Runnable runnable);
-
}
diff --git a/src_plugins/com/android/systemui/plugins/shared/LauncherOverlayManager.java b/src_plugins/com/android/systemui/plugins/shared/LauncherOverlayManager.java
index ac02ba4..582ab23 100644
--- a/src_plugins/com/android/systemui/plugins/shared/LauncherOverlayManager.java
+++ b/src_plugins/com/android/systemui/plugins/shared/LauncherOverlayManager.java
@@ -93,6 +93,6 @@
interface LauncherOverlayCallbacks {
- void onScrollChanged(float progress);
+ void onOverlayScrollChanged(float progress);
}
}
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java b/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java
index c8b5e2f..02f4ece 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java
@@ -17,10 +17,18 @@
package com.android.launcher3.uioverrides;
import android.app.Person;
+import android.appwidget.AppWidgetHost;
import android.content.pm.ShortcutInfo;
-import com.android.launcher3.Utilities;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.widget.LauncherWidgetHolder;
+
+/**
+ * A wrapper for the hidden API calls
+ */
public class ApiWrapper {
public static final boolean TASKBAR_DRAWN_IN_PROCESS = false;
@@ -28,4 +36,14 @@
public static Person[] getPersons(ShortcutInfo si) {
return Utilities.EMPTY_PERSON_ARRAY;
}
+
+ /**
+ * Set the interaction handler for the host
+ * @param host AppWidgetHost that needs the interaction handler
+ * @param handler InteractionHandler for the views in the host
+ */
+ public static void setHostInteractionHandler(@NonNull AppWidgetHost host,
+ @Nullable LauncherWidgetHolder.LauncherWidgetInteractionHandler handler) {
+ // No-op
+ }
}
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 bf35dd8..a581f91 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -32,7 +32,6 @@
public class AllAppsState extends LauncherState {
private static final float PARALLAX_COEFFICIENT = .125f;
- private static final float WORKSPACE_SCALE_FACTOR = 0.97f;
private static final int STATE_FLAGS = FLAG_WORKSPACE_INACCESSIBLE;
@@ -43,9 +42,9 @@
@Override
public <DEVICE_PROFILE_CONTEXT extends Context & DeviceProfileListenable>
int getTransitionDuration(DEVICE_PROFILE_CONTEXT context, boolean isToState) {
- return !context.getDeviceProfile().isTablet && isToState
- ? 600
- : isToState ? 500 : 300;
+ return isToState
+ ? context.getDeviceProfile().allAppsOpenDuration
+ : context.getDeviceProfile().allAppsCloseDuration;
}
@Override
@@ -60,7 +59,8 @@
@Override
public ScaleAndTranslation getWorkspaceScaleAndTranslation(Launcher launcher) {
- return new ScaleAndTranslation(WORKSPACE_SCALE_FACTOR, NO_OFFSET, NO_OFFSET);
+ return new ScaleAndTranslation(launcher.getDeviceProfile().workspaceContentScale, NO_OFFSET,
+ NO_OFFSET);
}
@Override
@@ -71,7 +71,7 @@
ScaleAndTranslation overviewScaleAndTranslation = LauncherState.OVERVIEW
.getWorkspaceScaleAndTranslation(launcher);
return new ScaleAndTranslation(
- WORKSPACE_SCALE_FACTOR,
+ launcher.getDeviceProfile().workspaceContentScale,
overviewScaleAndTranslation.translationX,
overviewScaleAndTranslation.translationY);
}
diff --git a/tests/Android.bp b/tests/Android.bp
index 1584308..39bd307 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -60,6 +60,7 @@
"src/com/android/launcher3/testcomponent/CustomShortcutConfigActivity.java",
"src/com/android/launcher3/testcomponent/TestCommandReceiver.java",
"src/com/android/launcher3/testcomponent/TestLauncherActivity.java",
+ "src/com/android/launcher3/testcomponent/ImeTestActivity.java",
],
}
diff --git a/tests/AndroidManifest-common.xml b/tests/AndroidManifest-common.xml
index 9cc3aed..bedf277 100644
--- a/tests/AndroidManifest-common.xml
+++ b/tests/AndroidManifest-common.xml
@@ -24,6 +24,7 @@
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
<uses-permission android:name="android.permission.READ_LOGS"/>
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<application android:debuggable="true" android:extractNativeLibs="true">
<uses-library android:name="android.test.runner"/>
@@ -62,6 +63,17 @@
</receiver>
<receiver
+ android:name="com.android.launcher3.testcomponent.AppWidgetWithDialog"
+ android:exported="true"
+ android:label="With Dialog">
+ <intent-filter>
+ <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
+ </intent-filter>
+ <meta-data android:name="android.appwidget.provider"
+ android:resource="@xml/appwidget_no_config"/>
+ </receiver>
+
+ <receiver
android:name="com.android.launcher3.testcomponent.AppWidgetDynamicColors"
android:exported="true"
android:label="Dynamic Colors">
@@ -276,6 +288,26 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
+ <activity
+ android:name="com.android.launcher3.testcomponent.DialogTestActivity"
+ android:label="Dialog Activity"
+ android:theme="@android:style/Theme.Dialog"
+ android:exported="true"
+ android:taskAffinity="com.android.launcher3.testcomponent.Affinity2">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ <activity android:name="com.android.launcher3.testcomponent.ImeTestActivity"
+ android:label="ImeTestActivity"
+ android:icon="@drawable/test_theme_icon"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
<!-- [b/197780098] Disable eager initialization of Jetpack libraries. -->
<provider
diff --git a/tests/res/layout/test_layout_appwidget_blue.xml b/tests/res/layout/test_layout_appwidget_blue.xml
index 8111978..f33e575 100644
--- a/tests/res/layout/test_layout_appwidget_blue.xml
+++ b/tests/res/layout/test_layout_appwidget_blue.xml
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@android:id/content"
android:orientation="vertical"
android:background="#FF0000FF"
android:layout_width="match_parent"
diff --git a/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt b/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt
index 77ade80..fe30fdc 100644
--- a/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt
+++ b/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt
@@ -16,11 +16,12 @@
package com.android.launcher3
import android.content.Context
+import android.graphics.Point
import android.graphics.PointF
import android.graphics.Rect
import android.util.SparseArray
import androidx.test.core.app.ApplicationProvider
-import com.android.launcher3.DeviceProfile.DEFAULT_PROVIDER;
+import com.android.launcher3.DeviceProfile.DEFAULT_PROVIDER
import com.android.launcher3.util.DisplayController.Info
import com.android.launcher3.util.WindowBounds
import org.junit.Before
@@ -96,8 +97,6 @@
numColumns = 4
numSearchContainerColumns = 4
- numFolderRows = 3
- numFolderColumns = 3
iconSize = floatArrayOf(60f, 54f, 60f, 60f)
iconTextSize = FloatArray(4) { 14f }
deviceType = InvariantDeviceProfile.TYPE_PHONE
@@ -115,7 +114,11 @@
PointF(16f, 16f),
PointF(16f, 16f)
).toTypedArray()
- folderBorderSpace = 16f
+
+ numFolderRows = 3
+ numFolderColumns = 3
+ folderStyle = R.style.FolderDefaultStyle
+
inlineNavButtonsEndSpacing = R.dimen.taskbar_button_margin_4_5
horizontalMargin = FloatArray(4) { 22f }
@@ -199,6 +202,11 @@
PointF(16f, 64f),
PointF(16f, 64f)
).toTypedArray()
+
+ numFolderRows = 3
+ numFolderColumns = 3
+ folderStyle = R.style.FolderDefaultStyle
+
inlineNavButtonsEndSpacing = R.dimen.taskbar_button_margin_6_5
horizontalMargin = floatArrayOf(54f, 120f, 54f, 54f)
@@ -229,7 +237,7 @@
numAllAppsColumns = 6
isScalable = true
- devicePaddingId = 2132148242 // "@xml/paddings_6x5"
+ devicePaddingId = R.xml.paddings_6x5
inlineQsb = booleanArrayOf(
false,
@@ -273,8 +281,6 @@
numColumns = 4
numSearchContainerColumns = 4
- numFolderRows = 3
- numFolderColumns = 4
iconSize = floatArrayOf(60f, 52f, 52f, 60f)
iconTextSize = floatArrayOf(14f, 14f, 12f, 14f)
deviceType = InvariantDeviceProfile.TYPE_MULTI_DISPLAY
@@ -292,7 +298,11 @@
PointF(16f, 20f),
PointF(20f, 20f)
).toTypedArray()
- folderBorderSpace = 16f
+
+ numFolderRows = 3
+ numFolderColumns = 3
+ folderStyle = R.style.FolderDefaultStyle
+
inlineNavButtonsEndSpacing = R.dimen.taskbar_button_margin_4_4
horizontalMargin = floatArrayOf(21.5f, 21.5f, 22.5f, 30.5f)
diff --git a/tests/src/com/android/launcher3/celllayout/CellLayoutBoard.java b/tests/src/com/android/launcher3/celllayout/CellLayoutBoard.java
index 3ca05bc..a32ce3c 100644
--- a/tests/src/com/android/launcher3/celllayout/CellLayoutBoard.java
+++ b/tests/src/com/android/launcher3/celllayout/CellLayoutBoard.java
@@ -30,39 +30,117 @@
public class CellLayoutBoard {
+ public static class CellType {
+ // The cells marked by this will be filled by 1x1 widgets and will be ignored when
+ // validating
+ public static final char IGNORE = 'x';
+ // The cells marked by this will be filled by app icons
+ public static final char ICON = 'i';
+ // Empty space
+ public static final char EMPTY = '-';
+ // Widget that will be saved as "main widget" for easier retrieval
+ public static final char MAIN_WIDGET = 'm';
+ // Everything else will be consider a widget
+ }
+
+ public static class WidgetRect {
+ public char mType;
+ public Rect mBounds;
+
+ WidgetRect(char type, Rect bounds) {
+ this.mType = type;
+ this.mBounds = bounds;
+ }
+
+ int getSpanX() {
+ return mBounds.right - mBounds.left + 1;
+ }
+
+ int getSpanY() {
+ return mBounds.top - mBounds.bottom + 1;
+ }
+
+ int getCellX() {
+ return mBounds.left;
+ }
+
+ int getCellY() {
+ return mBounds.bottom;
+ }
+
+ boolean shouldIgnore() {
+ return this.mType == CellType.IGNORE;
+ }
+
+ @Override
+ public String toString() {
+ return "WidgetRect type = " + mType + " bounds = " + mBounds.toString();
+ }
+ }
+
+ public static class IconPoint {
+ public Point coord;
+ public char mType;
+
+ public IconPoint(Point coord, char type) {
+ this.coord = coord;
+ mType = type;
+ }
+
+ public char getType() {
+ return mType;
+ }
+
+ public void setType(char type) {
+ mType = type;
+ }
+
+ public Point getCoord() {
+ return coord;
+ }
+
+ public void setCoord(Point coord) {
+ this.coord = coord;
+ }
+ }
+
static final int INFINITE = 99999;
- char[][] mBoard = new char[30][30];
+ char[][] mWidget = new char[30][30];
- List<TestBoardWidget> mWidgetsRects = new ArrayList<>();
- Map<Character, TestBoardWidget> mWidgetsMap = new HashMap<>();
+ List<WidgetRect> mWidgetsRects = new ArrayList<>();
+ Map<Character, WidgetRect> mWidgetsMap = new HashMap<>();
- List<TestBoardAppIcon> mIconPoints = new ArrayList<>();
- Map<Character, TestBoardAppIcon> mIconsMap = new HashMap<>();
+ List<IconPoint> mIconPoints = new ArrayList<>();
+ Map<Character, IconPoint> mIconsMap = new HashMap<>();
Point mMain = new Point();
CellLayoutBoard() {
- for (int x = 0; x < mBoard.length; x++) {
- for (int y = 0; y < mBoard[0].length; y++) {
- mBoard[x][y] = '-';
+ for (int x = 0; x < mWidget.length; x++) {
+ for (int y = 0; y < mWidget[0].length; y++) {
+ mWidget[x][y] = CellType.EMPTY;
}
}
}
- public List<TestBoardWidget> getWidgets() {
+ public List<WidgetRect> getWidgets() {
return mWidgetsRects;
}
+ public List<IconPoint> getIcons() {
+ return mIconPoints;
+ }
+
public Point getMain() {
return mMain;
}
- public TestBoardWidget getWidgetRect(char c) {
+ public WidgetRect getWidgetRect(char c) {
return mWidgetsMap.get(c);
}
- public static TestBoardWidget getWidgetRect(int x, int y, Set<Point> used, char[][] board) {
+ public static WidgetRect getWidgetRect(int x, int y, Set<Point> used, char[][] board) {
char type = board[x][y];
Queue<Point> search = new ArrayDeque<Point>();
Point current = new Point(x, y);
@@ -91,20 +169,20 @@
}
}
}
- return new TestBoardWidget(type, widgetRect);
+ return new WidgetRect(type, widgetRect);
}
public static boolean isWidget(char type) {
- return type != 'i' && type != '-';
+ return type != CellType.ICON && type != CellType.EMPTY;
}
public static boolean isIcon(char type) {
- return type == 'i';
+ return type == CellType.ICON;
}
- private static List<TestBoardWidget> getRects(char[][] board) {
+ private static List<WidgetRect> getRects(char[][] board) {
Set<Point> used = new HashSet<>();
- List<TestBoardWidget> widgetsRects = new ArrayList<>();
+ List<WidgetRect> widgetsRects = new ArrayList<>();
for (int x = 0; x < board.length; x++) {
for (int y = 0; y < board[0].length; y++) {
if (!used.contains(new Point(x, y)) && isWidget(board[x][y])) {
@@ -115,12 +193,12 @@
return widgetsRects;
}
- private static List<TestBoardAppIcon> getIconPoints(char[][] board) {
- List<TestBoardAppIcon> iconPoints = new ArrayList<>();
+ private static List<IconPoint> getIconPoints(char[][] board) {
+ List<IconPoint> iconPoints = new ArrayList<>();
for (int x = 0; x < board.length; x++) {
for (int y = 0; y < board[0].length; y++) {
if (isIcon(board[x][y])) {
- iconPoints.add(new TestBoardAppIcon(new Point(x, y), board[x][y]));
+ iconPoints.add(new IconPoint(new Point(x, y), board[x][y]));
}
}
}
@@ -135,18 +213,18 @@
String line = lines[y];
for (int x = 0; x < line.length(); x++) {
char c = line.charAt(x);
- if (c == 'm') {
+ if (c == CellType.MAIN_WIDGET) {
board.mMain = new Point(x, y);
}
- if (c != '-') {
- board.mBoard[x][y] = line.charAt(x);
+ if (c != CellType.EMPTY) {
+ board.mWidget[x][y] = line.charAt(x);
}
}
}
- board.mWidgetsRects = getRects(board.mBoard);
+ board.mWidgetsRects = getRects(board.mWidget);
board.mWidgetsRects.forEach(
widgetRect -> board.mWidgetsMap.put(widgetRect.mType, widgetRect));
- board.mIconPoints = getIconPoints(board.mBoard);
+ board.mIconPoints = getIconPoints(board.mWidget);
return board;
}
}
diff --git a/tests/src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java b/tests/src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java
new file mode 100644
index 0000000..8ce932d
--- /dev/null
+++ b/tests/src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.celllayout;
+
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.ui.AbstractLauncherUiTest;
+import com.android.launcher3.util.ContentWriter;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+
+public class FavoriteItemsTransaction {
+ private ArrayList<ItemInfo> mItemsToSubmit;
+ private Context mContext;
+ private ContentResolver mResolver;
+ public AbstractLauncherUiTest mTest;
+
+ public FavoriteItemsTransaction(Context context, AbstractLauncherUiTest test) {
+ mItemsToSubmit = new ArrayList<>();
+ mContext = context;
+ mResolver = mContext.getContentResolver();
+ mTest = test;
+ }
+
+ public FavoriteItemsTransaction addItem(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();
+ }
+}
diff --git a/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java b/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java
index ed0b71d..9ba3f7b 100644
--- a/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java
+++ b/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java
@@ -15,16 +15,13 @@
*/
package com.android.launcher3.celllayout;
-import static com.android.launcher3.util.WidgetUtils.createWidgetInfo;
-
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import android.graphics.Point;
-import android.graphics.Rect;
import android.util.Log;
import android.view.View;
-import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -34,12 +31,14 @@
import com.android.launcher3.celllayout.testcases.PushReorderCase;
import com.android.launcher3.celllayout.testcases.ReorderTestCase;
import com.android.launcher3.celllayout.testcases.SimpleReorderCase;
-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.TaplTestsLauncher3;
-import com.android.launcher3.ui.TestViewHelpers;
+import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
import com.android.launcher3.util.rule.ShellCommandRule;
-import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.views.DoubleShadowBubbleTextView;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
import org.junit.Assume;
import org.junit.Before;
@@ -48,6 +47,7 @@
import org.junit.runner.RunWith;
import java.util.Map;
+import java.util.concurrent.ExecutionException;
@SmallTest
@@ -59,6 +59,8 @@
private static final String TAG = ReorderWidgets.class.getSimpleName();
+ TestWorkspaceBuilder mWorkspaceBuilder;
+
private View getViewAt(int cellX, int cellY) {
return getFromLauncher(l -> l.getWorkspace().getScreenWithId(
l.getWorkspace().getScreenIdForPageIndex(0)).getChildAt(cellX, cellY));
@@ -75,6 +77,7 @@
@Before
public void setup() throws Throwable {
+ mWorkspaceBuilder = new TestWorkspaceBuilder(this, mTargetContext);
TaplTestsLauncher3.initialize(this);
clearHomescreen();
}
@@ -85,78 +88,47 @@
private boolean validateBoard(CellLayoutBoard board) {
boolean match = true;
Point cellDimensions = getCellDimensions();
- for (TestBoardWidget widgetRect: board.getWidgets()) {
+ for (CellLayoutBoard.WidgetRect widgetRect : board.getWidgets()) {
if (widgetRect.shouldIgnore()) {
continue;
}
View widget = getViewAt(widgetRect.getCellX(), widgetRect.getCellY());
+ assertTrue("The view selected at " + board + " is not a widget",
+ widget instanceof LauncherAppWidgetHostView);
match &= widgetRect.getSpanX()
== Math.round(widget.getWidth() / (float) cellDimensions.x);
match &= widgetRect.getSpanY()
== Math.round(widget.getHeight() / (float) cellDimensions.y);
if (!match) return match;
}
+ for (CellLayoutBoard.IconPoint iconPoint : board.getIcons()) {
+ View icon = getViewAt(iconPoint.getCoord().x, iconPoint.getCoord().y);
+ assertTrue("The view selected at " + iconPoint.coord + " is not an Icon",
+ icon instanceof DoubleShadowBubbleTextView);
+ }
return match;
}
- /**
- * Fills the given rect in WidgetRect with 1x1 widgets. This is useful to equalize cases.
- */
- private void fillWithWidgets(TestBoardWidget widgetRect) {
- int initX = widgetRect.getCellX();
- int initY = widgetRect.getCellY();
- for (int x = 0; x < widgetRect.getSpanX(); x++) {
- for (int y = 0; y < widgetRect.getSpanY(); y++) {
- int auxX = initX + x;
- int auxY = initY + y;
- try {
- // this widgets are filling, we don't care if we can't place them
- addWidgetInCell(
- new TestBoardWidget('x',
- new Rect(auxX, auxY, auxX, auxY))
- );
- } catch (Exception e) {
- Log.d(TAG, "Unable to place filling widget at " + auxX + "," + auxY);
- }
- }
- }
- }
-
- private void addWidgetInCell(TestBoardWidget widgetRect) {
- LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false);
- LauncherAppWidgetInfo item = createWidgetInfo(info,
- ApplicationProvider.getApplicationContext(), true);
- item.cellX = widgetRect.getCellX();
- item.cellY = widgetRect.getCellY();
-
- item.spanX = widgetRect.getSpanX();
- item.spanY = widgetRect.getSpanY();
- addItemToScreen(item);
- }
-
- private void addCorrespondingWidgetRect(TestBoardWidget widgetRect) {
- if (widgetRect.mType == 'x') {
- fillWithWidgets(widgetRect);
- } else {
- addWidgetInCell(widgetRect);
- }
- }
-
- private void runTestCase(ReorderTestCase testCase) {
+ private void runTestCase(ReorderTestCase testCase)
+ throws ExecutionException, InterruptedException {
Point mainWidgetCellPos = testCase.mStart.getMain();
- testCase.mStart.getWidgets().forEach(this::addCorrespondingWidgetRect);
-
- mLauncher.getWorkspace()
- .getWidgetAtCell(mainWidgetCellPos.x, mainWidgetCellPos.y)
- .dragWidgetToWorkspace(testCase.moveMainTo.x, testCase.moveMainTo.y)
- .dismiss(); // dismiss resize frame
+ FavoriteItemsTransaction transaction =
+ new FavoriteItemsTransaction(mTargetContext, this);
+ mWorkspaceBuilder.buildFromBoard(testCase.mStart, transaction).commit();
+ waitForLauncherCondition("Workspace didn't finish loading", l -> !l.isWorkspaceLoading());
+ Widget widget = mLauncher.getWorkspace().getWidgetAtCell(mainWidgetCellPos.x,
+ mainWidgetCellPos.y);
+ assertNotNull(widget);
+ WidgetResizeFrame resizeFrame = widget.dragWidgetToWorkspace(testCase.moveMainTo.x,
+ testCase.moveMainTo.y);
+ resizeFrame.dismiss();
boolean isValid = false;
for (CellLayoutBoard board : testCase.mEnd) {
isValid |= validateBoard(board);
}
- assertTrue("None of the valid boards match with the current state", isValid);
+ assertTrue("Non of the valid boards match with the current state", isValid);
}
/**
@@ -164,7 +136,8 @@
*
* @param testCaseMap map containing all the tests per grid size (Point)
*/
- private void runTestCaseMap(Map<Point, ReorderTestCase> testCaseMap, String testName) {
+ private void runTestCaseMap(Map<Point, ReorderTestCase> testCaseMap, String testName)
+ throws ExecutionException, InterruptedException {
Point iconGridDimensions = mLauncher.getWorkspace().getIconGridDimensions();
Log.d(TAG, "Running test " + testName + " for grid " + iconGridDimensions);
Assume.assumeTrue(
@@ -173,24 +146,28 @@
runTestCase(testCaseMap.get(iconGridDimensions));
}
+ @ScreenRecord // b/242323136
@Test
- public void simpleReorder() {
+ public void simpleReorder() throws ExecutionException, InterruptedException {
runTestCaseMap(SimpleReorderCase.TEST_BY_GRID_SIZE,
SimpleReorderCase.class.getSimpleName());
}
+ @ScreenRecord // b/242323136
@Test
- public void pushTest() {
+ public void pushTest() throws ExecutionException, InterruptedException {
runTestCaseMap(PushReorderCase.TEST_BY_GRID_SIZE, PushReorderCase.class.getSimpleName());
}
+ @ScreenRecord // b/242323136
@Test
- public void fullReorder() {
+ public void fullReorder() throws ExecutionException, InterruptedException {
runTestCaseMap(FullReorderCase.TEST_BY_GRID_SIZE, FullReorderCase.class.getSimpleName());
}
+ @ScreenRecord // b/242323136
@Test
- public void moveOutReorder() {
+ public void moveOutReorder() throws ExecutionException, InterruptedException {
runTestCaseMap(MoveOutReorderCase.TEST_BY_GRID_SIZE,
MoveOutReorderCase.class.getSimpleName());
}
diff --git a/tests/src/com/android/launcher3/celllayout/TestWorkspaceBuilder.java b/tests/src/com/android/launcher3/celllayout/TestWorkspaceBuilder.java
new file mode 100644
index 0000000..16448af
--- /dev/null
+++ b/tests/src/com/android/launcher3/celllayout/TestWorkspaceBuilder.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.celllayout;
+
+import static com.android.launcher3.WorkspaceLayoutManager.FIRST_SCREEN_ID;
+import static com.android.launcher3.util.WidgetUtils.createWidgetInfo;
+
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.Process;
+import android.os.UserHandle;
+import android.util.Log;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.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;
+
+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;
+ mMyUser = Process.myUserHandle();
+ mContext = context;
+ mResolver = mContext.getContentResolver();
+ }
+
+ /**
+ * Fills the given rect in WidgetRect with 1x1 widgets. This is useful to equalize cases.
+ */
+ private FavoriteItemsTransaction fillWithWidgets(CellLayoutBoard.WidgetRect widgetRect,
+ FavoriteItemsTransaction transaction) {
+ int initX = widgetRect.getCellX();
+ int initY = widgetRect.getCellY();
+ for (int x = initX; x < initX + widgetRect.getSpanX(); x++) {
+ 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(
+ new CellLayoutBoard.WidgetRect(CellLayoutBoard.CellType.IGNORE,
+ new Rect(x, y, x, y))
+ );
+ transaction.addItem(item);
+ } catch (Exception e) {
+ Log.d(TAG, "Unable to place filling widget at " + x + "," + y);
+ }
+ }
+ }
+ 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));
+ }
+
+ private void addCorrespondingWidgetRect(CellLayoutBoard.WidgetRect widgetRect,
+ FavoriteItemsTransaction transaction) {
+ if (widgetRect.mType == 'x') {
+ fillWithWidgets(widgetRect, transaction);
+ } else {
+ transaction.addItem(createWidgetInCell(widgetRect));
+ }
+ }
+
+ /**
+ * Builds the given board into the transaction
+ */
+ public FavoriteItemsTransaction buildFromBoard(CellLayoutBoard board,
+ FavoriteItemsTransaction transaction) {
+ board.getWidgets().forEach(
+ (widgetRect) -> addCorrespondingWidgetRect(widgetRect, transaction));
+ board.getIcons().forEach((iconPoint) ->
+ transaction.addItem(createIconInCell(iconPoint))
+ );
+ return transaction;
+ }
+
+ /**
+ * Fills the hotseat row with apps instead of suggestions, for this to work the workspace should
+ * be clean otherwise this doesn't overrides the existing icons.
+ */
+ public FavoriteItemsTransaction fillHotseatIcons(FavoriteItemsTransaction transaction) {
+ int hotseatCount = InvariantDeviceProfile.INSTANCE.get(mContext).numShownHotseatIcons;
+ for (int i = 0; i < hotseatCount; i++) {
+ transaction.addItem(getHotseatValues(i));
+ }
+ return transaction;
+ }
+
+ private ItemInfo createWidgetInCell(CellLayoutBoard.WidgetRect widgetRect) {
+ 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();
+ item.screenId = FIRST_SCREEN_ID;
+ return item;
+ }
+
+ private ItemInfo createIconInCell(CellLayoutBoard.IconPoint iconPoint) {
+ WorkspaceItemInfo item = new WorkspaceItemInfo(getApp());
+ item.id = getID();
+ item.screenId = FIRST_SCREEN_ID;
+ item.cellX = iconPoint.getCoord().x;
+ item.cellY = iconPoint.getCoord().y;
+ item.minSpanY = item.minSpanX = item.spanX = item.spanY = 1;
+ item.container = LauncherSettings.Favorites.CONTAINER_DESKTOP;
+ return item;
+ }
+
+ 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;
+ item.rank = x;
+ item.screenId = x;
+ item.container = LauncherSettings.Favorites.CONTAINER_HOTSEAT;
+ return item;
+ }
+}
diff --git a/tests/src/com/android/launcher3/celllayout/testcases/FullReorderCase.java b/tests/src/com/android/launcher3/celllayout/testcases/FullReorderCase.java
index 1326389..a98882c 100644
--- a/tests/src/com/android/launcher3/celllayout/testcases/FullReorderCase.java
+++ b/tests/src/com/android/launcher3/celllayout/testcases/FullReorderCase.java
@@ -19,6 +19,10 @@
import java.util.Map;
+/**
+ * The grids represent the workspace to be build by TestWorkspaceBuilder, to see what each character
+ * in the board mean refer to {@code CellType}
+ */
public class FullReorderCase {
/** 5x5 Test
@@ -27,13 +31,13 @@
+ "xxxxx\n"
+ "222mm\n"
+ "222mm\n"
- + "ad111\n"
- + "bc111";
+ + "ii111\n"
+ + "ii111";
private static final Point MOVE_TO_5x5 = new Point(0, 4);
private static final String END_BOARD_STR_5x5 = ""
+ "xxxxx\n"
- + "222ad\n"
- + "222bc\n"
+ + "222ii\n"
+ + "222ii\n"
+ "mm111\n"
+ "mm111";
private static final ReorderTestCase TEST_CASE_5x5 = new ReorderTestCase(START_BOARD_STR_5x5,
@@ -46,13 +50,13 @@
+ "xxxxxx\n"
+ "2222mm\n"
+ "2222mm\n"
- + "ad1111\n"
- + "bc1111";
+ + "ii1111\n"
+ + "ii1111";
private static final Point MOVE_TO_6x5 = new Point(0, 4);
private static final String END_BOARD_STR_6x5 = ""
+ "xxxxxx\n"
- + "2222ad\n"
- + "2222bc\n"
+ + "2222ii\n"
+ + "2222ii\n"
+ "mm1111\n"
+ "mm1111";
private static final ReorderTestCase TEST_CASE_6x5 = new ReorderTestCase(START_BOARD_STR_6x5,
@@ -64,13 +68,13 @@
private static final String START_BOARD_STR_4x4 = ""
+ "xxxx\n"
+ "22mm\n"
- + "admm\n"
- + "bc11";
+ + "iimm\n"
+ + "ii11";
private static final Point MOVE_TO_4x4 = new Point(0, 3);
private static final String END_BOARD_STR_4x4 = ""
+ "xxxx\n"
- + "22ad\n"
- + "mmbc\n"
+ + "22ii\n"
+ + "mmii\n"
+ "mm11";
private static final ReorderTestCase TEST_CASE_4x4 = new ReorderTestCase(START_BOARD_STR_4x4,
diff --git a/tests/src/com/android/launcher3/celllayout/testcases/MoveOutReorderCase.java b/tests/src/com/android/launcher3/celllayout/testcases/MoveOutReorderCase.java
index 1701390..047d342 100644
--- a/tests/src/com/android/launcher3/celllayout/testcases/MoveOutReorderCase.java
+++ b/tests/src/com/android/launcher3/celllayout/testcases/MoveOutReorderCase.java
@@ -19,6 +19,10 @@
import java.util.Map;
+/**
+ * The grids represent the workspace to be build by TestWorkspaceBuilder, to see what each character
+ * in the board mean refer to {@code CellType}
+ */
public class MoveOutReorderCase {
/** 5x5 Test
@@ -59,25 +63,7 @@
MOVE_TO_6x5,
END_BOARD_STR_6x5);
- /** 4x4 Test
- **/
- private static final String START_BOARD_STR_4x4 = ""
- + "xxxx\n"
- + "34-m\n"
- + "3511\n"
- + "3211";
- private static final Point MOVE_TO_4x4 = new Point(1, 2);
- private static final String END_BOARD_STR_4x4 = ""
- + "xxxx\n"
- + "345-\n"
- + "3m11\n"
- + "3211";
- private static final ReorderTestCase TEST_CASE_4x4 = new ReorderTestCase(START_BOARD_STR_4x4,
- MOVE_TO_4x4,
- END_BOARD_STR_4x4);
-
public static final Map<Point, ReorderTestCase> TEST_BY_GRID_SIZE =
Map.of(new Point(5, 5), TEST_CASE_5x5,
- new Point(6, 5), TEST_CASE_6x5,
- new Point(4, 4), TEST_CASE_4x4);
+ new Point(6, 5), TEST_CASE_6x5);
}
diff --git a/tests/src/com/android/launcher3/celllayout/testcases/PushReorderCase.java b/tests/src/com/android/launcher3/celllayout/testcases/PushReorderCase.java
index bb8d5e9..38c9aee 100644
--- a/tests/src/com/android/launcher3/celllayout/testcases/PushReorderCase.java
+++ b/tests/src/com/android/launcher3/celllayout/testcases/PushReorderCase.java
@@ -19,6 +19,10 @@
import java.util.Map;
+/**
+ * The grids represent the workspace to be build by TestWorkspaceBuilder, to see what each character
+ * in the board mean refer to {@code CellType}
+ */
public class PushReorderCase {
/** 5x5 Test
@@ -60,25 +64,7 @@
MOVE_TO_6x5,
END_BOARD_STR_6x5);
- /** 4x4 Test
- **/
- private static final String START_BOARD_STR_4x4 = ""
- + "xxxx\n"
- + "222m\n"
- + "-111\n"
- + "----";
- private static final Point MOVE_TO_4x4 = new Point(2, 1);
- private static final String END_BOARD_STR_4x4 = ""
- + "xxxx\n"
- + "--m-\n"
- + "222-\n"
- + "-111";
- private static final ReorderTestCase TEST_CASE_4x4 = new ReorderTestCase(START_BOARD_STR_4x4,
- MOVE_TO_4x4,
- END_BOARD_STR_4x4);
-
public static final Map<Point, ReorderTestCase> TEST_BY_GRID_SIZE =
Map.of(new Point(5, 5), TEST_CASE_5x5,
- new Point(6, 5), TEST_CASE_6x5,
- new Point(4, 4), TEST_CASE_4x4);
+ new Point(6, 5), TEST_CASE_6x5);
}
diff --git a/tests/src/com/android/launcher3/celllayout/testcases/SimpleReorderCase.java b/tests/src/com/android/launcher3/celllayout/testcases/SimpleReorderCase.java
index 30269a0..546c48b 100644
--- a/tests/src/com/android/launcher3/celllayout/testcases/SimpleReorderCase.java
+++ b/tests/src/com/android/launcher3/celllayout/testcases/SimpleReorderCase.java
@@ -19,6 +19,10 @@
import java.util.Map;
+/**
+ * The grids represent the workspace to be build by TestWorkspaceBuilder, to see what each character
+ * in the board mean refer to {@code CellType}
+ */
public class SimpleReorderCase {
/** 5x5 Test
diff --git a/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java b/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
index 960d27d..7d36740 100644
--- a/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
+++ b/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
@@ -64,23 +64,26 @@
IconCache iconCache = LauncherAppState.getInstance(context).getIconCache();
CachingLogic<ItemInfo> placeholderLogic = new CachingLogic<ItemInfo>() {
@Override
- public ComponentName getComponent(ItemInfo info) {
+ @NonNull
+ public ComponentName getComponent(@NonNull ItemInfo info) {
return info.getTargetComponent();
}
+ @NonNull
@Override
- public UserHandle getUser(ItemInfo info) {
+ public UserHandle getUser(@NonNull ItemInfo info) {
return info.user;
}
+ @NonNull
@Override
- public CharSequence getLabel(ItemInfo info) {
+ public CharSequence getLabel(@NonNull ItemInfo info) {
return NEW_LABEL_PREFIX + info.id;
}
@NonNull
@Override
- public BitmapInfo loadIcon(Context context, ItemInfo info) {
+ public BitmapInfo loadIcon(@NonNull Context context, @NonNull ItemInfo info) {
return BitmapInfo.of(Bitmap.createBitmap(1, 1, Config.ARGB_8888), Color.RED);
}
};
diff --git a/tests/src/com/android/launcher3/secondarydisplay/SDLauncherTest.java b/tests/src/com/android/launcher3/secondarydisplay/SDLauncherTest.java
deleted file mode 100644
index fd86cf1..0000000
--- a/tests/src/com/android/launcher3/secondarydisplay/SDLauncherTest.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.secondarydisplay;
-
-import static androidx.test.core.app.ActivityScenario.launch;
-
-import androidx.test.core.app.ActivityScenario;
-import androidx.test.espresso.intent.Intents;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.MediumTest;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests for {@link SecondaryDisplayLauncher}
- */
-@MediumTest
-@RunWith(AndroidJUnit4.class)
-public class SDLauncherTest {
-
- @Before
- public void setUp() {
- Intents.init();
- }
-
- @After
- public void tearDown() {
- Intents.release();
- }
-
- @Test
- public void testAllAppsListOpens() {
- ActivityScenario<SecondaryDisplayLauncher> launcher =
- launch(SecondaryDisplayLauncher.class);
- launcher.onActivity(l -> l.showAppDrawer(true));
- }
-}
diff --git a/tests/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncherTest.java b/tests/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncherTest.java
new file mode 100644
index 0000000..082e243
--- /dev/null
+++ b/tests/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncherTest.java
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.secondarydisplay;
+
+import static android.content.Context.MODE_PRIVATE;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.view.MotionEvent.ACTION_DOWN;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Point;
+import android.os.SystemClock;
+import android.view.MotionEvent;
+import android.widget.TextView;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.LargeTest;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.UiObject2;
+import androidx.test.uiautomator.Until;
+
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.tapl.LauncherInstrumentation;
+import com.android.launcher3.ui.AbstractLauncherUiTest;
+import com.android.launcher3.util.LauncherModelHelper;
+
+import org.junit.After;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link SecondaryDisplayLauncher}.
+ * TODO (b/242776943): Remove anti-patterns & migrate prediction row tests to Quickstep directory
+ */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public final class SecondaryDisplayLauncherTest extends AbstractLauncherUiTest {
+ private static final int WAIT_TIME_MS = 5000;
+ private static final int LONG_PRESS_DURATION_MS = 1000;
+ private static final int DRAG_TIME_MS = 160;
+
+ private static final String PINNED_APPS_KEY = "pinned_apps";
+
+ // Variables required to coordinate drag steps.
+ private Point mStartPoint;
+ private Point mEndPoint;
+ private long mDownTime;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ setDragNDropFlag(true);
+ }
+
+ @After
+ public void tearDown() {
+ mTargetContext.getSharedPreferences(PINNED_APPS_KEY, MODE_PRIVATE)
+ .edit().clear().commit();
+ }
+
+ @Test
+ @Ignore
+ public void initializeSecondaryDisplayLauncher_allAppsButtonVisible() {
+ assertThat(findObjectByResourceName("all_apps_button")).isNotNull();
+ }
+
+ @Test
+ @Ignore
+ public void allAppsButtonTap_opensAppDrawer() {
+ openAppDrawer();
+ assertThat(findObjectByResourceName("search_container_all_apps")).isNotNull();
+ }
+
+ @Test
+ @Ignore("Launcher3 without quickstep doesn't have a predictions row.")
+ public void appDrawerOpened_predictionRowAppDividerVisible() {
+ openAppDrawer();
+ assertThat(findObjectByResourceName("apps_divider_view")).isNotNull();
+ }
+
+ @Test
+ @Ignore
+ public void dragNDropDisabled_pinIconAddsToWorkspace() {
+ setDragNDropFlag(false);
+ openAppDrawer();
+ UiObject2 app = findDescendantByResourceName(
+ findObjectByResourceName("apps_list_view"), "icon");
+ app.click(LONG_PRESS_DURATION_MS);
+ UiObject2 popupContainer = findObjectByResourceName("popup_container");
+ assertThat(popupContainer).isNotNull();
+ UiObject2 pinIcon = findDescendantByTextOrDesc(popupContainer, "Add to home screen");
+ assertThat(pinIcon).isNotNull();
+ pinIcon.click();
+ String appName = app.getContentDescription();
+ assertThat(findAppInWorkspace(appName)).isNotNull();
+ }
+
+ @Test
+ @Ignore
+ public void pressBackFromAllApps_popupMenuOpen_returnsToWorkspace() {
+ openAppDrawer();
+ assertThat(findObjectByResourceName("search_container_all_apps")).isNotNull();
+
+ findDescendantByResourceName(findObjectByResourceName("apps_list_view"), "icon")
+ .click(LONG_PRESS_DURATION_MS);
+ assertThat(findObjectByResourceName("popup_container")).isNotNull();
+
+ // First back press should close only popup menu.
+ mDevice.pressBack();
+ assertThat(findObjectByResourceName("search_container_all_apps")).isNotNull();
+ assertThat(findObjectByResourceName("popup_container")).isNull();
+
+ // Second back press should close app drawer.
+ mDevice.pressBack();
+ assertThat(findObjectByResourceName("popup_container")).isNull();
+ assertThat(findObjectByResourceName("search_container_all_apps")).isNull();
+ }
+
+ @Test
+ @Ignore("Launcher3 without quickstep doesn't have a predictions row.")
+ public void dragNDropFromPredictionsRow_pinToGrid() {
+ openAppDrawer();
+ assertThat(findObjectByResourceName("prediction_row")).isNotNull();
+ String appName = startDragFromPredictionRow();
+ moveAppToCenterOfScreen();
+ dropApp();
+
+ // Ensure app was added.
+ assertThat(findAppInWorkspace(appName)).isNotNull();
+ }
+
+ @Test
+ @Ignore
+ public void dragNDropFromAppDrawer_pinToGrid() {
+ openAppDrawer();
+ String draggedAppName = startDragFromAllApps();
+ moveAppToCenterOfScreen();
+ dropApp();
+
+ // Ensure app was added.
+ assertThat(findAppInWorkspace(draggedAppName)).isNotNull();
+ }
+
+ @Test
+ @Ignore
+ public void tapRemoveButton_unpinApp() {
+ openAppDrawer();
+ String draggedAppName = startDragFromAllApps();
+ moveAppToCenterOfScreen();
+ dropApp();
+ removeAppByName(draggedAppName);
+ assertThat(findAppInWorkspace(draggedAppName)).isNull();
+ }
+
+ private void openAppDrawer() {
+ UiObject2 allAppsButton = findObjectByResourceName("all_apps_button");
+ assertThat(allAppsButton).isNotNull();
+ allAppsButton.click();
+ }
+
+ private String startDragFromAllApps() {
+ // Find app from app drawer.
+ UiObject2 allApps = findObjectByResourceName("apps_list_view");
+ assertThat(allApps).isNotNull();
+ UiObject2 icon = findDescendantByResourceName(allApps, "icon");
+ assertThat(icon).isNotNull();
+ String appName = icon.getContentDescription();
+
+ // Start drag action.
+ mDownTime = SystemClock.uptimeMillis();
+ mStartPoint = icon.getVisibleCenter();
+ mEndPoint = new Point(mStartPoint.x, mStartPoint.y);
+ mLauncher.sendPointer(mDownTime, mDownTime, ACTION_DOWN, mStartPoint,
+ LauncherInstrumentation.GestureScope.INSIDE);
+ assertThat(findObjectByResourceName("popup_container")).isNotNull();
+ return appName;
+ }
+
+ private String startDragFromPredictionRow() {
+ // Find app from predictions.
+ UiObject2 predictionRow = findObjectByResourceName("prediction_row");
+ assertThat(predictionRow).isNotNull();
+
+ UiObject2 icon = findDescendantByResourceName(predictionRow, "icon");
+ assertThat(icon).isNotNull();
+
+ String appName = icon.getContentDescription();
+ UiObject2 app = findDescendantByAppName(predictionRow, appName);
+ assertThat(app).isNotNull();
+
+ // Start drag action.
+ mDownTime = SystemClock.uptimeMillis();
+ mStartPoint = icon.getVisibleCenter();
+ mEndPoint = new Point(mStartPoint.x, mStartPoint.y);
+ mLauncher.sendPointer(mDownTime, mDownTime, ACTION_DOWN, mStartPoint,
+ LauncherInstrumentation.GestureScope.INSIDE);
+ assertThat(findObjectByResourceName("popup_container")).isNotNull();
+ return appName;
+ }
+
+ private void moveAppToCenterOfScreen() {
+ mEndPoint.set(mDevice.getDisplayWidth() / 2, mDevice.getDisplayHeight() / 2);
+ mLauncher.movePointer(mDownTime, SystemClock.uptimeMillis(), DRAG_TIME_MS, true,
+ mStartPoint, mEndPoint, LauncherInstrumentation.GestureScope.INSIDE);
+ }
+
+ private void dropApp() {
+ mLauncher.sendPointer(mDownTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP,
+ mEndPoint, LauncherInstrumentation.GestureScope.INSIDE);
+ }
+
+ private void removeAppByName(String appName) {
+ // Find app within home screen.
+ UiObject2 app = findDescendantByAppName(findObjectByResourceName("workspace_grid"),
+ appName);
+ if (app == null) return;
+
+ // Open app's popup container.
+ app.click(LONG_PRESS_DURATION_MS);
+ UiObject2 popupContainer = findObjectByResourceName("popup_container");
+ assertThat(popupContainer).isNotNull();
+
+ // Grab & click remove button.
+ UiObject2 removeButton = findDescendantByTextOrDesc(popupContainer, "Remove");
+ assertThat(removeButton).isNotNull();
+ removeButton.click();
+ }
+
+ private UiObject2 findAppInWorkspace(String appName) {
+ UiObject2 workspace = findObjectByResourceName("workspace_grid");
+ return findDescendantByAppName(workspace, appName);
+ }
+
+ private UiObject2 findObjectByResourceName(String resourceName) {
+ return mDevice.wait(Until.findObject(By.res(mTargetPackage, resourceName)), WAIT_TIME_MS);
+ }
+
+ private UiObject2 findDescendantByResourceName(UiObject2 outerObject,
+ String resourceName) {
+ assertThat(outerObject).isNotNull();
+ return outerObject.findObject(By.res(mTargetPackage, resourceName));
+ }
+
+ private UiObject2 findDescendantByAppName(UiObject2 outerObject, String appName) {
+ assertThat(outerObject).isNotNull();
+ return outerObject.findObject(By.clazz(TextView.class).text(appName)
+ .pkg(mDevice.getLauncherPackageName()));
+ }
+
+ private UiObject2 findDescendantByTextOrDesc(UiObject2 outerObject, String content) {
+ assertThat(outerObject).isNotNull();
+ UiObject2 innerObject = outerObject.findObject(By.desc(content));
+ if (innerObject == null) innerObject = outerObject.findObject(By.text(content));
+ return innerObject;
+ }
+
+ private void startSecondaryDisplayActivity() {
+ mTargetContext.startActivity((
+ new Intent(mTargetContext, SecondaryDisplayLauncher.class).addFlags(
+ FLAG_ACTIVITY_NEW_TASK)));
+ }
+
+ private void setDragNDropFlag(Boolean status) {
+ Context context = new LauncherModelHelper().sandboxContext;
+ context.getSharedPreferences(FeatureFlags.FLAGS_PREF_NAME, Context.MODE_PRIVATE).edit()
+ .putBoolean(FeatureFlags.SECONDARY_DRAG_N_DROP_TO_PIN.key, status)
+ .commit();
+ FeatureFlags.initialize(context);
+ startSecondaryDisplayActivity();
+ }
+}
diff --git a/tests/src/com/android/launcher3/testcomponent/AppWidgetWithDialog.java b/tests/src/com/android/launcher3/testcomponent/AppWidgetWithDialog.java
new file mode 100644
index 0000000..6d617fa
--- /dev/null
+++ b/tests/src/com/android/launcher3/testcomponent/AppWidgetWithDialog.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.testcomponent;
+
+import android.app.PendingIntent;
+import android.appwidget.AppWidgetManager;
+import android.content.Context;
+import android.content.Intent;
+import android.widget.RemoteViews;
+
+/**
+ * A simple app widget with shows a dialog on clicking.
+ */
+public class AppWidgetWithDialog extends AppWidgetNoConfig {
+
+ @Override
+ public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
+ int layoutId = context.getResources().getIdentifier(
+ "test_layout_appwidget_blue", "layout", context.getPackageName());
+ RemoteViews views = new RemoteViews(context.getPackageName(), layoutId);
+
+ PendingIntent pi = PendingIntent.getActivity(context, 0,
+ new Intent(context, DialogTestActivity.class), PendingIntent.FLAG_IMMUTABLE);
+ views.setOnClickPendingIntent(android.R.id.content, pi);
+ AppWidgetManager.getInstance(context).updateAppWidget(appWidgetIds, views);
+ }
+}
diff --git a/tests/src/com/android/launcher3/testcomponent/BaseTestingActivity.java b/tests/src/com/android/launcher3/testcomponent/BaseTestingActivity.java
index 9c6d102..d3ce67c 100644
--- a/tests/src/com/android/launcher3/testcomponent/BaseTestingActivity.java
+++ b/tests/src/com/android/launcher3/testcomponent/BaseTestingActivity.java
@@ -24,7 +24,9 @@
import android.os.Bundle;
import android.util.TypedValue;
import android.view.View;
+import android.view.WindowInsets;
import android.widget.Button;
+import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
@@ -81,6 +83,20 @@
mView.addView(button, lp);
}
+ protected void addEditor(String initText, String hint, boolean requestIme) {
+ EditText editText = new EditText(this);
+ editText.setHint(hint);
+ editText.setText(initText);
+
+ LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
+ lp.bottomMargin = mMargin;
+ mView.addView(editText, lp);
+ if (requestIme) {
+ editText.requestFocus();
+ mView.getWindowInsetsController().show(WindowInsets.Type.ime());
+ }
+ }
+
@Override
protected void onResume() {
super.onResume();
diff --git a/tests/src/com/android/launcher3/testcomponent/DialogTestActivity.java b/tests/src/com/android/launcher3/testcomponent/DialogTestActivity.java
new file mode 100644
index 0000000..9e5a274
--- /dev/null
+++ b/tests/src/com/android/launcher3/testcomponent/DialogTestActivity.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.testcomponent;
+
+
+/**
+ * Extension of BaseTestingActivity with a Dialog theme
+ */
+public class DialogTestActivity extends BaseTestingActivity {}
diff --git a/tests/src/com/android/launcher3/testcomponent/ImeTestActivity.java b/tests/src/com/android/launcher3/testcomponent/ImeTestActivity.java
new file mode 100644
index 0000000..43952d5
--- /dev/null
+++ b/tests/src/com/android/launcher3/testcomponent/ImeTestActivity.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.testcomponent;
+
+import android.os.Bundle;
+
+public class ImeTestActivity extends OtherBaseTestingActivity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ // Requests to focus an editor and show IME for test.
+ addEditor("Focused editor for test", "Focused editor for test", true);
+ }
+}
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 304153f..70d122b 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -151,6 +151,8 @@
device.executeShellCommand(
"am dumpheap " + device.getLauncherPackageName() + " " + fileName);
}
+ Log.d(TAG, "Saved leak dump, the leak is still present: "
+ + !launcher.noLeakedActivities());
sDumpWasGenerated = true;
result = "saved memory dump as an artifact";
} catch (Throwable e) {
@@ -319,7 +321,7 @@
/**
* Adds {@param item} on the homescreen on the 0th screen
*/
- protected void addItemToScreen(ItemInfo item) {
+ public void addItemToScreen(ItemInfo item) {
WidgetUtils.addItemToScreen(item, mTargetContext);
resetLoaderState();
@@ -477,6 +479,16 @@
false /* newTask */);
}
+ public static void startImeTestActivity() {
+ final String packageName = getAppPackageName();
+ final Intent intent = getInstrumentation().getContext().getPackageManager().
+ getLaunchIntentForPackage(packageName);
+ intent.setComponent(new ComponentName(packageName,
+ "com.android.launcher3.testcomponent.ImeTestActivity"));
+ startIntent(intent, By.pkg(packageName).text("ImeTestActivity"),
+ false /* newTask */);
+ }
+
private static void startIntent(Intent intent, BySelector selector, boolean newTask) {
intent.addCategory(Intent.CATEGORY_LAUNCHER);
if (newTask) {
@@ -525,7 +537,7 @@
}
protected int getAllAppsScroll(Launcher launcher) {
- return launcher.getAppsView().getActiveRecyclerView().getCurrentScrollY();
+ return launcher.getAppsView().getActiveRecyclerView().computeVerticalScrollOffset();
}
private void checkLauncherIntegrity(
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index 03bf4af..50e0990 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -29,6 +29,7 @@
import android.content.Intent;
import android.graphics.Point;
+import android.platform.test.annotations.IwTest;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
@@ -203,6 +204,7 @@
false /* tapRight */);
}
+ @IwTest(focusArea="launcher")
@Test
@ScreenRecord // b/202433017
public void testWorkspace() throws Exception {
@@ -211,9 +213,9 @@
// Test that ensureWorkspaceIsScrollable adds a page by dragging an icon there.
executeOnLauncher(launcher -> assertFalse("Initial workspace state is scrollable",
isWorkspaceScrollable(launcher)));
- workspace.verifyWorkspaceAppIconIsGone(
- "Chrome app was found on empty workspace", "Chrome");
-
+ assertEquals("Initial workspace doesn't have the correct page", workspace.pagesPerScreen(),
+ workspace.getPageCount());
+ workspace.verifyWorkspaceAppIconIsGone("Chrome app was found on empty workspace", "Chrome");
workspace.ensureWorkspaceIsScrollable();
executeOnLauncher(
@@ -300,7 +302,7 @@
}
private int getWidgetsScroll(Launcher launcher) {
- return getWidgetsView(launcher).getCurrentScrollY();
+ return getWidgetsView(launcher).computeVerticalScrollOffset();
}
private boolean isOptionsPopupVisible(Launcher launcher) {
@@ -330,8 +332,10 @@
}
}
+ @IwTest(focusArea="launcher")
@Test
@PortraitLandscape
+ @ScreenRecord // b/256898879
public void testDragAppIcon() throws Throwable {
// 1. Open all apps and wait for load complete.
// 2. Drag icon to homescreen.
@@ -433,6 +437,23 @@
@Test
@PortraitLandscape
+ public void testDragAndCancelAppIcon() {
+ final HomeAppIcon homeAppIcon = createShortcutInCenterIfNotExist(GMAIL_APP_NAME);
+ Point positionBeforeDrag =
+ mLauncher.getWorkspace().getWorkspaceIconsPositions().get(GMAIL_APP_NAME);
+ assertNotNull("App not found in Workspace before dragging.", positionBeforeDrag);
+
+ mLauncher.getWorkspace().dragAndCancelAppIcon(homeAppIcon);
+
+ Point positionAfterDrag =
+ mLauncher.getWorkspace().getWorkspaceIconsPositions().get(GMAIL_APP_NAME);
+ assertNotNull("App not found in Workspace after dragging.", positionAfterDrag);
+ assertEquals("App not returned to same position in Workspace after drag & cancel",
+ positionBeforeDrag, positionAfterDrag);
+ }
+
+ @Test
+ @PortraitLandscape
public void testDeleteFromWorkspace() throws Exception {
// test delete both built-in apps and user-installed app from workspace
for (String appName : new String[]{"Gmail", "Play Store", APP_NAME}) {
@@ -458,7 +479,7 @@
@Test
@PortraitLandscape
public void testUninstallFromWorkspace() throws Exception {
- TestUtil.installDummyApp();
+ installDummyAppAndWaitForUIUpdate();
try {
verifyAppUninstalledFromAllApps(
createShortcutInCenterIfNotExist(DUMMY_APP_NAME).uninstall(), DUMMY_APP_NAME);
@@ -469,8 +490,9 @@
@Test
@PortraitLandscape
+ @ScreenRecord // (b/256659409)
public void testUninstallFromAllApps() throws Exception {
- TestUtil.installDummyApp();
+ installDummyAppAndWaitForUIUpdate();
try {
Workspace workspace = mLauncher.getWorkspace();
final HomeAllApps allApps = workspace.switchToAllApps();
@@ -515,7 +537,7 @@
Point[] gridPositions = getCornersAndCenterPositions();
createShortcutIfNotExist(STORE_APP_NAME, gridPositions[0]);
createShortcutIfNotExist(MAPS_APP_NAME, gridPositions[1]);
- TestUtil.installDummyApp();
+ installDummyAppAndWaitForUIUpdate();
try {
createShortcutIfNotExist(DUMMY_APP_NAME, gridPositions[2]);
Map<String, Point> initialPositions =
@@ -566,6 +588,17 @@
mLauncher.getWorkspace().getHotseatAppIcon(APP_NAME));
}
+ private void installDummyAppAndWaitForUIUpdate() throws IOException {
+ TestUtil.installDummyApp();
+ // Wait for model thread completion as it may be processing
+ // the install event from the SystemService
+ mLauncher.waitForModelQueueCleared();
+ // Wait for Launcher UI thread completion, as it may be processing updating the UI in
+ // response to the model update. Not that `waitForLauncherInitialized` is just a proxy
+ // method, we can use any method which touches Launcher UI thread,
+ mLauncher.waitForLauncherInitialized();
+ }
+
/**
* @return List of workspace grid coordinates. Those are not pixels. See {@link
* Workspace#getIconGridDimensions()}
@@ -584,4 +617,16 @@
public static String getAppPackageName() {
return getInstrumentation().getContext().getPackageName();
}
+
+ @Test
+ public void testGetAppIconName() {
+ HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps();
+ allApps.freeze();
+ try {
+ HomeAppIcon icon = allApps.getAppIcon(APP_NAME);
+ assertEquals("Wrong app icon name.", icon.getIconName(), APP_NAME);
+ } finally {
+ allApps.unfreeze();
+ }
+ }
}
diff --git a/tests/src/com/android/launcher3/ui/WorkProfileTest.java b/tests/src/com/android/launcher3/ui/WorkProfileTest.java
index a7a17b1..302bd2f 100644
--- a/tests/src/com/android/launcher3/ui/WorkProfileTest.java
+++ b/tests/src/com/android/launcher3/ui/WorkProfileTest.java
@@ -21,6 +21,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
import android.util.Log;
import android.view.View;
@@ -47,6 +48,8 @@
private static final int WORK_PAGE = ActivityAllAppsContainerView.AdapterHolder.WORK;
private int mProfileUserId;
+ private boolean mWorkProfileSetupSuccessful;
+ private final String TAG = "WorkProfileTest";
@Before
@Override
@@ -55,12 +58,17 @@
String output =
mDevice.executeShellCommand(
"pm create-user --profileOf 0 --managed TestProfile");
- Log.d("b/203817455", "pm create-user; output: " + output);
- assertTrue("Failed to create work profile", output.startsWith("Success"));
+ // b/203817455
+ updateWorkProfileSetupSuccessful("pm create-user", output);
String[] tokens = output.split("\\s+");
mProfileUserId = Integer.parseInt(tokens[tokens.length - 1]);
- mDevice.executeShellCommand("am start-user " + mProfileUserId);
+ output = mDevice.executeShellCommand("am start-user " + mProfileUserId);
+ updateWorkProfileSetupSuccessful("am start-user", output);
+
+ if (!mWorkProfileSetupSuccessful) {
+ return; // no need to setup launcher since all tests will skip.
+ }
mDevice.pressHome();
waitForLauncherCondition("Launcher didn't start", Objects::nonNull);
@@ -99,6 +107,8 @@
@Test
public void workTabExists() {
+ assumeTrue(mWorkProfileSetupSuccessful);
+ waitForWorkTabSetup();
waitForLauncherCondition("Personal tab is missing",
launcher -> launcher.getAppsView().isPersonalTabVisible(),
LauncherInstrumentation.WAIT_TIME_MS);
@@ -109,8 +119,8 @@
@Test
public void toggleWorks() {
+ assumeTrue(mWorkProfileSetupSuccessful);
waitForWorkTabSetup();
-
executeOnLauncher(launcher -> {
AllAppsPagedView pagedView = (AllAppsPagedView) launcher.getAppsView().getContentView();
pagedView.setCurrentPage(WORK_PAGE);
@@ -126,7 +136,11 @@
LauncherInstrumentation.WAIT_TIME_MS);
//start work profile toggle OFF test
- executeOnLauncher(l -> l.getAppsView().getWorkManager().getWorkModeSwitch().performClick());
+ executeOnLauncher(l -> {
+ // Ensure updates are not deferred so notification happens when apps pause.
+ l.getAppsView().getAppsStore().disableDeferUpdates(DEFER_UPDATES_TEST);
+ l.getAppsView().getWorkManager().getWorkModeSwitch().performClick();
+ });
waitForLauncherCondition("Work profile toggle OFF failed", launcher -> {
manager.reset(); // pulls current state from system
@@ -152,6 +166,7 @@
@Test
public void testEdu() {
+ assumeTrue(mWorkProfileSetupSuccessful);
waitForWorkTabSetup();
executeOnLauncher(l -> {
l.getSharedPrefs().edit().putInt(WorkProfileManager.KEY_WORK_EDU_STEP, 0).commit();
@@ -174,4 +189,14 @@
}
}, LauncherInstrumentation.WAIT_TIME_MS);
}
+
+ private void updateWorkProfileSetupSuccessful(String cli, String output) {
+ Log.d(TAG, "updateWorkProfileSetupSuccessful, cli=" + cli + " " + "output=" + output);
+ if (output.startsWith("Success")) {
+ assertTrue(output, output.startsWith("Success"));
+ mWorkProfileSetupSuccessful = true;
+ } else {
+ mWorkProfileSetupSuccessful = false;
+ }
+ }
}
diff --git a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
index 194ee4f..1f5590e 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
@@ -20,6 +20,8 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import android.platform.test.annotations.IwTest;
+
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
@@ -45,12 +47,15 @@
@Rule
public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind();
+ @IwTest(focusArea="launcher")
@Test
@PortraitLandscape
public void testDragIcon() throws Throwable {
clearHomescreen();
mDevice.pressHome();
+ waitForLauncherCondition("Workspace didn't finish loading", l -> !l.isWorkspaceLoading());
+
final LauncherAppWidgetProviderInfo widgetInfo =
TestViewHelpers.findWidgetProvider(this, false /* hasConfigureScreen */);
diff --git a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
index fa39ce0..0f861eb 100644
--- a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
@@ -190,7 +190,7 @@
waitForLauncherCondition("App widget options did not update",
l -> appWidgetManager.getAppWidgetOptions(appWidgetId).getBoolean(
WidgetManagerHelper.WIDGET_OPTION_RESTORE_COMPLETED));
- executeOnLauncher(l -> l.getAppWidgetHost().startListening());
+ executeOnLauncher(l -> l.getAppWidgetHolder().startListening());
verifyWidgetPresent(info);
assertNull(mLauncher.getWorkspace().tryGetPendingWidget(100));
}
diff --git a/tests/src/com/android/launcher3/util/LauncherModelHelper.java b/tests/src/com/android/launcher3/util/LauncherModelHelper.java
index 3324959..e7e551f 100644
--- a/tests/src/com/android/launcher3/util/LauncherModelHelper.java
+++ b/tests/src/com/android/launcher3/util/LauncherModelHelper.java
@@ -47,6 +47,7 @@
import android.test.mock.MockContentResolver;
import android.util.ArrayMap;
+import androidx.annotation.NonNull;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.uiautomator.UiDevice;
@@ -194,8 +195,9 @@
Executor mockExecutor = mock(Executor.class);
model.enqueueModelUpdateTask(new ModelUpdateTask() {
@Override
- public void init(LauncherAppState app, LauncherModel model, BgDataModel dataModel,
- AllAppsList allAppsList, Executor uiExecutor) {
+ 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);
}
diff --git a/tests/src/com/android/launcher3/util/MultiAdditivePropertyTest.kt b/tests/src/com/android/launcher3/util/MultiPropertyFactoryTest.kt
similarity index 64%
rename from tests/src/com/android/launcher3/util/MultiAdditivePropertyTest.kt
rename to tests/src/com/android/launcher3/util/MultiPropertyFactoryTest.kt
index 309d055..bf3a092 100644
--- a/tests/src/com/android/launcher3/util/MultiAdditivePropertyTest.kt
+++ b/tests/src/com/android/launcher3/util/MultiPropertyFactoryTest.kt
@@ -16,38 +16,44 @@
package com.android.launcher3.util
-import android.view.View
+import android.util.FloatProperty
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
-/** Unit tests for [MultiAdditivePropertyFactory] */
+/** Unit tests for [MultiPropertyFactory] */
@SmallTest
@RunWith(AndroidJUnit4::class)
-class MultiAdditivePropertyTest {
+class MultiPropertyFactoryTest {
private val received = mutableListOf<Float>()
- private val factory =
- object : MultiAdditivePropertyFactory<View?>("Test", View.TRANSLATION_X) {
- override fun apply(obj: View?, value: Float) {
- received.add(value)
- }
+ private val receiveProperty: FloatProperty<Any> = object : FloatProperty<Any>("receive") {
+ override fun setValue(obj: Any?, value: Float) {
+ received.add(value)
}
+ override fun get(o: Any): Float {
+ return 0f
+ }
+ }
- private val p1 = factory.get(1)
- private val p2 = factory.get(2)
- private val p3 = factory.get(3)
+ private val factory = MultiPropertyFactory(null, receiveProperty, 3) {
+ x: Float, y: Float -> x + y
+ }
+
+ private val p1 = factory.get(0)
+ private val p2 = factory.get(1)
+ private val p3 = factory.get(2)
@Test
fun set_sameIndexes_allApplied() {
val v1 = 50f
val v2 = 100f
- p1.set(null, v1)
- p1.set(null, v1)
- p1.set(null, v2)
+ p1.value = v1
+ p1.value = v1
+ p1.value = v2
assertThat(received).containsExactly(v1, v1, v2)
}
@@ -57,9 +63,9 @@
val v1 = 50f
val v2 = 100f
val v3 = 150f
- p1.set(null, v1)
- p2.set(null, v2)
- p3.set(null, v3)
+ p1.value = v1
+ p2.value = v2
+ p3.value = v3
assertThat(received).containsExactly(v1, v1 + v2, v1 + v2 + v3)
}
diff --git a/tests/src/com/android/launcher3/util/TestUtil.java b/tests/src/com/android/launcher3/util/TestUtil.java
index 67f3902..d7c6c4f 100644
--- a/tests/src/com/android/launcher3/util/TestUtil.java
+++ b/tests/src/com/android/launcher3/util/TestUtil.java
@@ -17,8 +17,13 @@
import static androidx.test.InstrumentationRegistry.getContext;
import static androidx.test.InstrumentationRegistry.getInstrumentation;
+import static androidx.test.InstrumentationRegistry.getTargetContext;
+import android.content.pm.LauncherApps;
import android.content.res.Resources;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserHandle;
import androidx.test.uiautomator.UiDevice;
@@ -27,6 +32,7 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.util.concurrent.CountDownLatch;
public class TestUtil {
public static final String DUMMY_PACKAGE = "com.example.android.aardwolf";
@@ -40,24 +46,77 @@
final String apkFilename = getInstrumentation().getTargetContext().
getFilesDir().getPath() + "/dummy_app.apk";
- final FileOutputStream out = new FileOutputStream(apkFilename);
- byte[] buff = new byte[1024];
- int read;
+ try (PackageInstallCheck pic = new PackageInstallCheck()) {
+ final FileOutputStream out = new FileOutputStream(apkFilename);
+ byte[] buff = new byte[1024];
+ int read;
- while ((read = in.read(buff)) > 0) {
- out.write(buff, 0, read);
+ while ((read = in.read(buff)) > 0) {
+ out.write(buff, 0, read);
+ }
+ in.close();
+ out.close();
+
+ final String result = UiDevice.getInstance(getInstrumentation())
+ .executeShellCommand("pm install " + apkFilename);
+ Assert.assertTrue(
+ "Failed to install wellbeing test apk; make sure the device is rooted",
+ "Success".equals(result.replaceAll("\\s+", "")));
+ pic.mAddWait.await();
+ } catch (InterruptedException e) {
+ throw new IOException(e);
}
- in.close();
- out.close();
-
- final String result = UiDevice.getInstance(getInstrumentation())
- .executeShellCommand("pm install " + apkFilename);
- Assert.assertTrue("Failed to install wellbeing test apk; make sure the device is rooted",
- "Success".equals(result.replaceAll("\\s+", "")));
}
public static void uninstallDummyApp() throws IOException {
UiDevice.getInstance(getInstrumentation()).executeShellCommand(
"pm uninstall " + DUMMY_PACKAGE);
}
+
+ private static class PackageInstallCheck extends LauncherApps.Callback
+ implements AutoCloseable {
+
+ final CountDownLatch mAddWait = new CountDownLatch(1);
+ final LauncherApps mLauncherApps;
+
+ PackageInstallCheck() {
+ mLauncherApps = getTargetContext().getSystemService(LauncherApps.class);
+ mLauncherApps.registerCallback(this, new Handler(Looper.getMainLooper()));
+ }
+
+ private void verifyPackage(String packageName) {
+ if (DUMMY_PACKAGE.equals(packageName)) {
+ mAddWait.countDown();
+ }
+ }
+
+ @Override
+ public void onPackageAdded(String packageName, UserHandle user) {
+ verifyPackage(packageName);
+ }
+
+ @Override
+ public void onPackageChanged(String packageName, UserHandle user) {
+ verifyPackage(packageName);
+ }
+
+ @Override
+ public void onPackageRemoved(String packageName, UserHandle user) { }
+
+ @Override
+ public void onPackagesAvailable(String[] packageNames, UserHandle user, boolean replacing) {
+ for (String packageName : packageNames) {
+ verifyPackage(packageName);
+ }
+ }
+
+ @Override
+ public void onPackagesUnavailable(String[] packageNames, UserHandle user,
+ boolean replacing) { }
+
+ @Override
+ public void close() {
+ mLauncherApps.unregisterCallback(this);
+ }
+ }
}
diff --git a/tests/src/com/android/launcher3/util/WidgetUtils.java b/tests/src/com/android/launcher3/util/WidgetUtils.java
index 6fc8491..e514142 100644
--- a/tests/src/com/android/launcher3/util/WidgetUtils.java
+++ b/tests/src/com/android/launcher3/util/WidgetUtils.java
@@ -20,7 +20,6 @@
import static com.android.launcher3.WorkspaceLayoutManager.FIRST_SCREEN_ID;
-import android.appwidget.AppWidgetHost;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
@@ -32,8 +31,8 @@
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
-import com.android.launcher3.widget.LauncherAppWidgetHost;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.widget.LauncherWidgetHolder;
import com.android.launcher3.widget.PendingAddWidgetInfo;
import com.android.launcher3.widget.WidgetManagerHelper;
@@ -71,14 +70,19 @@
pendingInfo.minSpanY = item.minSpanY;
Bundle options = pendingInfo.getDefaultSizeOptions(targetContext);
- AppWidgetHost host = new LauncherAppWidgetHost(targetContext);
- int widgetId = host.allocateAppWidgetId();
- if (!new WidgetManagerHelper(targetContext)
- .bindAppWidgetIdIfAllowed(widgetId, info, options)) {
- host.deleteAppWidgetId(widgetId);
- throw new IllegalArgumentException("Unable to bind widget id");
+ LauncherWidgetHolder holder = new LauncherWidgetHolder(targetContext);
+ try {
+ int widgetId = holder.allocateAppWidgetId();
+ if (!new WidgetManagerHelper(targetContext)
+ .bindAppWidgetIdIfAllowed(widgetId, info, options)) {
+ holder.deleteAppWidgetId(widgetId);
+ throw new IllegalArgumentException("Unable to bind widget id");
+ }
+ item.appWidgetId = widgetId;
+ } finally {
+ // Necessary to destroy the holder to free up possible activity context
+ holder.destroy();
}
- item.appWidgetId = widgetId;
}
return item;
}
diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java
index 4791846..b0cf20f 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java
@@ -16,6 +16,9 @@
package com.android.launcher3.tapl;
+import static com.android.launcher3.tapl.LauncherInstrumentation.DEFAULT_POLL_INTERVAL;
+import static com.android.launcher3.tapl.LauncherInstrumentation.WAIT_TIME_MS;
+
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Bundle;
@@ -37,6 +40,9 @@
* Operations on AllApps opened from Home. Also a parent for All Apps opened from Overview.
*/
public abstract class AllApps extends LauncherInstrumentation.VisibleContainer {
+ // Defer updates flag used to defer all apps updates by a test's request.
+ private static final int DEFER_UPDATES_TEST = 1 << 1;
+
private static final int MAX_SCROLL_ATTEMPTS = 40;
private final int mHeight;
@@ -46,8 +52,7 @@
super(launcher);
final UiObject2 allAppsContainer = verifyActiveContainer();
mHeight = mLauncher.getVisibleBounds(allAppsContainer).height();
- final UiObject2 appListRecycler = mLauncher.waitForObjectInContainer(allAppsContainer,
- "apps_list_view");
+ final UiObject2 appListRecycler = getAppListRecycler(allAppsContainer);
// Wait for the recycler to populate.
mLauncher.waitForObjectInContainer(appListRecycler, By.clazz(TextView.class));
verifyNotFrozen("All apps freeze flags upon opening all apps");
@@ -78,6 +83,11 @@
LauncherInstrumentation.log("hasClickableIcon: icon center is under search box");
return false;
}
+ if (iconCenterInRecyclerTopPadding(appListRecycler, icon)) {
+ LauncherInstrumentation.log(
+ "hasClickableIcon: icon center is under the app list recycler's top padding.");
+ return false;
+ }
if (iconBounds.bottom > displayBottom) {
LauncherInstrumentation.log("hasClickableIcon: icon bottom below bottom offset");
return false;
@@ -92,6 +102,13 @@
iconCenter.x, iconCenter.y);
}
+ private boolean iconCenterInRecyclerTopPadding(UiObject2 appListRecycler, UiObject2 icon) {
+ final Point iconCenter = icon.getVisibleCenter();
+
+ return iconCenter.y <= mLauncher.getVisibleBounds(appListRecycler).top
+ + getAppsListRecyclerTopPadding();
+ }
+
/**
* Finds an icon. If the icon doesn't exist, return null.
* Scrolls the app list when needed to make sure the icon is visible.
@@ -105,9 +122,7 @@
LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"getting app icon " + appName + " on all apps")) {
final UiObject2 allAppsContainer = verifyActiveContainer();
- final UiObject2 appListRecycler = mLauncher.waitForObjectInContainer(allAppsContainer,
- "apps_list_view");
- final UiObject2 searchBox = hasSearchBox() ? getSearchBox(allAppsContainer) : null;
+ final UiObject2 appListRecycler = getAppListRecycler(allAppsContainer);
int deviceHeight = mLauncher.getRealDisplaySize().y;
int bottomGestureStartOnScreen = mLauncher.getBottomGestureStartOnScreen();
@@ -128,10 +143,9 @@
mLauncher.getVisibleBounds(icon).top
< bottomGestureStartOnScreen)
.collect(Collectors.toList()),
- hasSearchBox()
- ? mLauncher.getVisibleBounds(searchBox).bottom
- - mLauncher.getVisibleBounds(allAppsContainer).top
- : 0);
+ mLauncher.getVisibleBounds(appListRecycler).top
+ + getAppsListRecyclerTopPadding()
+ - mLauncher.getVisibleBounds(allAppsContainer).top);
verifyActiveContainer();
final int newScroll = getAllAppsScroll();
mLauncher.assertTrue(
@@ -180,16 +194,22 @@
protected abstract boolean hasSearchBox();
+ protected abstract int getAppsListRecyclerTopPadding();
+
private void scrollBackToBeginning() {
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"want to scroll back in all apps")) {
LauncherInstrumentation.log("Scrolling to the beginning");
final UiObject2 allAppsContainer = verifyActiveContainer();
- final UiObject2 searchBox = hasSearchBox() ? getSearchBox(allAppsContainer) : null;
+ final UiObject2 appListRecycler = getAppListRecycler(allAppsContainer);
int attempts = 0;
final Rect margins = new Rect(
- 0, hasSearchBox() ? mLauncher.getVisibleBounds(searchBox).bottom + 1 : 0, 0, 5);
+ /* left= */ 0,
+ mLauncher.getVisibleBounds(appListRecycler).top
+ + getAppsListRecyclerTopPadding() + 1,
+ /* right= */ 0,
+ /* bottom= */ 5);
for (int scroll = getAllAppsScroll();
scroll != 0;
@@ -220,6 +240,10 @@
.getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
}
+ private UiObject2 getAppListRecycler(UiObject2 allAppsContainer) {
+ return mLauncher.waitForObjectInContainer(allAppsContainer, "apps_list_view");
+ }
+
private UiObject2 getSearchBox(UiObject2 allAppsContainer) {
return mLauncher.waitForObjectInContainer(allAppsContainer, "search_container_all_apps");
}
@@ -274,12 +298,16 @@
*/
public void unfreeze() {
mLauncher.getTestInfo(TestProtocol.REQUEST_UNFREEZE_APP_LIST);
- verifyNotFrozen("All apps freeze flags upon unfreezing");
}
private void verifyNotFrozen(String message) {
+ mLauncher.assertEquals(message, 0, getFreezeFlags() & DEFER_UPDATES_TEST);
+ mLauncher.assertTrue(message, mLauncher.waitAndGet(() -> getFreezeFlags() == 0,
+ WAIT_TIME_MS, DEFAULT_POLL_INTERVAL));
+ }
+
+ private int getFreezeFlags() {
final Bundle testInfo = mLauncher.getTestInfo(TestProtocol.REQUEST_APP_LIST_FREEZE_FLAGS);
- if (testInfo == null) return;
- mLauncher.assertEquals(message, 0, testInfo.getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD));
+ return testInfo == null ? 0 : testInfo.getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
}
}
\ No newline at end of file
diff --git a/tests/tapl/com/android/launcher3/tapl/AllAppsFromTaskbar.java b/tests/tapl/com/android/launcher3/tapl/AllAppsFromTaskbar.java
index 5164025..f804e28 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllAppsFromTaskbar.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllAppsFromTaskbar.java
@@ -18,6 +18,8 @@
import androidx.annotation.NonNull;
import androidx.test.uiautomator.UiObject2;
+import com.android.launcher3.testing.shared.TestProtocol;
+
/**
* Operations on AllApps opened from the Taskbar.
*/
@@ -48,4 +50,10 @@
protected boolean hasSearchBox() {
return false;
}
+
+ @Override
+ protected int getAppsListRecyclerTopPadding() {
+ return mLauncher.getTestInfo(TestProtocol.REQUEST_TASKBAR_ALL_APPS_TOP_PADDING)
+ .getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+ }
}
diff --git a/tests/tapl/com/android/launcher3/tapl/AppIcon.java b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
index d221259..2687b28 100644
--- a/tests/tapl/com/android/launcher3/tapl/AppIcon.java
+++ b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
@@ -18,6 +18,7 @@
import android.widget.TextView;
+import androidx.annotation.NonNull;
import androidx.test.uiautomator.By;
import androidx.test.uiautomator.BySelector;
import androidx.test.uiautomator.UiObject2;
@@ -86,4 +87,10 @@
protected String launchableType() {
return "app icon";
}
+
+ /** Return the app name of a icon */
+ @NonNull
+ public String getIconName() {
+ return getObject().getText();
+ }
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Background.java b/tests/tapl/com/android/launcher3/tapl/Background.java
index eb8d055..5a96d95 100644
--- a/tests/tapl/com/android/launcher3/tapl/Background.java
+++ b/tests/tapl/com/android/launcher3/tapl/Background.java
@@ -58,7 +58,7 @@
"want to switch from background to overview")) {
verifyActiveContainer();
goToOverviewUnchecked();
- return mLauncher.isFallbackOverview()
+ return mLauncher.is3PLauncher()
? new BaseOverview(mLauncher) : new Overview(mLauncher);
}
}
@@ -206,21 +206,30 @@
MotionEvent.ACTION_UP, end, gestureScope);
}
+ /**
+ * Quick switching to the app with swiping to right.
+ */
@NonNull
public LaunchedAppState quickSwitchToPreviousApp() {
- boolean toRight = true;
- quickSwitch(toRight);
+ quickSwitch(true /* toRight */);
return new LaunchedAppState(mLauncher);
}
+ /**
+ * Quick switching to the app with swiping to left.
+ */
@NonNull
public LaunchedAppState quickSwitchToPreviousAppSwipeLeft() {
- boolean toRight = false;
- quickSwitch(toRight);
+ quickSwitch(false /* toRight */);
return new LaunchedAppState(mLauncher);
}
- @NonNull
+ /**
+ * Making swipe gesture to quick-switch app tasks.
+ *
+ * @param toRight {@code true} means swiping right, {@code false} means swiping left.
+ * @throws {@link AssertionError} when failing to verify the visible UI in the container.
+ */
private void quickSwitch(boolean toRight) {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
diff --git a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
index f47f710..afeb8d7 100644
--- a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
+++ b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
@@ -244,43 +244,39 @@
* Returns if clear all button is visible.
*/
public boolean isClearAllVisible() {
- return mLauncher.hasLauncherObject(mLauncher.getOverviewObjectSelector("clear_all"));
+ return verifyActiveContainer().hasObject(
+ mLauncher.getOverviewObjectSelector("clear_all"));
}
protected boolean isActionsViewVisible() {
+ if (!hasTasks() || isClearAllVisible()) {
+ return false;
+ }
OverviewTask task = mLauncher.isTablet() ? getFocusedTaskForTablet() : getCurrentTask();
if (task == null) {
return false;
}
+ // In tablets, if focused task is not in center, overview actions aren't visible.
+ if (mLauncher.isTablet()
+ && Math.abs(task.getExactCenterX() - mLauncher.getExactScreenCenterX()) >= 1) {
+ return false;
+ }
+ // Overview actions aren't visible for split screen tasks.
return !task.isTaskSplit();
}
private void verifyActionsViewVisibility() {
- if (!hasTasks() || !isActionsViewVisible()) {
- return;
- }
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"want to assert overview actions view visibility")) {
- if (mLauncher.isTablet() && !isOverviewSnappedToFocusedTaskForTablet()) {
- mLauncher.waitUntilOverviewObjectGone("action_buttons");
- } else {
+ if (isActionsViewVisible()) {
mLauncher.waitForOverviewObject("action_buttons");
+ } else {
+ mLauncher.waitUntilOverviewObjectGone("action_buttons");
}
}
}
/**
- * Returns if focused task is currently snapped task in tablet grid overview.
- */
- private boolean isOverviewSnappedToFocusedTaskForTablet() {
- OverviewTask focusedTask = getFocusedTaskForTablet();
- if (focusedTask == null) {
- return false;
- }
- return Math.abs(focusedTask.getExactCenterX() - mLauncher.getExactScreenCenterX()) < 1;
- }
-
- /**
* Returns Overview focused task if it exists.
*
* @throws IllegalStateException if not run on a tablet device.
diff --git a/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java b/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java
index 7123de4..9a4c6d4 100644
--- a/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java
@@ -18,6 +18,8 @@
import androidx.annotation.NonNull;
import androidx.test.uiautomator.UiObject2;
+import com.android.launcher3.testing.shared.TestProtocol;
+
public class HomeAllApps extends AllApps {
private static final String BOTTOM_SHEET_RES_ID = "bottom_sheet_background";
@@ -47,6 +49,12 @@
return true;
}
+ @Override
+ protected int getAppsListRecyclerTopPadding() {
+ return mLauncher.getTestInfo(TestProtocol.REQUEST_ALL_APPS_TOP_PADDING)
+ .getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+ }
+
/**
* Taps outside bottom sheet to dismiss and return to workspace. Available on tablets only.
* @param tapRight Tap on the right of bottom sheet if true, or left otherwise.
diff --git a/tests/tapl/com/android/launcher3/tapl/HomeQsb.java b/tests/tapl/com/android/launcher3/tapl/HomeQsb.java
index 5f92199..c365708 100644
--- a/tests/tapl/com/android/launcher3/tapl/HomeQsb.java
+++ b/tests/tapl/com/android/launcher3/tapl/HomeQsb.java
@@ -15,12 +15,21 @@
*/
package com.android.launcher3.tapl;
+import androidx.annotation.NonNull;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
+import androidx.test.uiautomator.UiObject2;
+import androidx.test.uiautomator.Until;
+
/**
* Operations on home screen qsb.
*/
public class HomeQsb {
private final LauncherInstrumentation mLauncher;
+ private static final String ASSISTANT_APP_PACKAGE = "com.google.android.googlequicksearchbox";
+ private static final String ASSISTANT_ICON_RES_ID = "mic_icon";
+
HomeQsb(LauncherInstrumentation launcher) {
mLauncher = launcher;
@@ -28,6 +37,35 @@
}
/**
+ * Launch assistant app by tapping mic icon on qsb.
+ */
+ @NonNull
+ public LaunchedAppState launchAssistant() {
+ try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "want to click assistant mic icon button");
+ LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
+ UiObject2 assistantIcon = mLauncher.waitForLauncherObject(ASSISTANT_ICON_RES_ID);
+
+ LauncherInstrumentation.log("HomeQsb.launchAssistant before click "
+ + assistantIcon.getVisibleCenter() + " in "
+ + mLauncher.getVisibleBounds(assistantIcon));
+
+ mLauncher.clickLauncherObject(assistantIcon);
+
+ try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer("clicked")) {
+ // assert Assistant App Launched
+ BySelector selector = By.pkg(ASSISTANT_APP_PACKAGE);
+ mLauncher.assertTrue(
+ "assistant app didn't start: (" + selector + ")",
+ mLauncher.getDevice().wait(Until.hasObject(selector),
+ LauncherInstrumentation.WAIT_TIME_MS)
+ );
+ return new LaunchedAppState(mLauncher);
+ }
+ }
+ }
+
+ /**
* Show search result page from tapping qsb.
*/
public SearchResultFromQsb showSearchResult() {
diff --git a/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java b/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java
index 04167839..4a3507e 100644
--- a/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java
+++ b/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java
@@ -16,7 +16,10 @@
package com.android.launcher3.tapl;
+import static com.android.launcher3.tapl.LauncherInstrumentation.TASKBAR_RES_ID;
+import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_DISABLE_BLOCK_TIMEOUT;
import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_DISABLE_MANUAL_TASKBAR_STASHING;
+import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_ENABLE_BLOCK_TIMEOUT;
import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_ENABLE_MANUAL_TASKBAR_STASHING;
import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_STASHED_TASKBAR_HEIGHT;
@@ -54,24 +57,45 @@
public Taskbar getTaskbar() {
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"want to get the taskbar")) {
- mLauncher.waitForLauncherObject("taskbar_view");
+ mLauncher.waitForSystemLauncherObject(TASKBAR_RES_ID);
return new Taskbar(mLauncher);
}
}
/**
+ * Waits for the taskbar to be hidden, or fails.
+ */
+ public void assertTaskbarHidden() {
+ try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "waiting for taskbar to be hidden")) {
+ mLauncher.waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
+ }
+ }
+
+ /**
+ * Waits for the taskbar to be visible, or fails.
+ */
+ public void assertTaskbarVisible() {
+ try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "waiting for taskbar to be visible")) {
+ mLauncher.waitForSystemLauncherObject(TASKBAR_RES_ID);
+ }
+ }
+
+ /**
* Returns the Taskbar in a visible state.
*
* The taskbar must already be hidden when calling this method.
*/
public Taskbar showTaskbar() {
mLauncher.getTestInfo(REQUEST_ENABLE_MANUAL_TASKBAR_STASHING);
+ mLauncher.getTestInfo(REQUEST_ENABLE_BLOCK_TIMEOUT);
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
"want to show the taskbar")) {
- mLauncher.waitUntilLauncherObjectGone("taskbar_view");
+ mLauncher.waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
final long downTime = SystemClock.uptimeMillis();
final int unstashTargetY = mLauncher.getRealDisplaySize().y
@@ -85,7 +109,7 @@
LauncherInstrumentation.log("showTaskbar: sent down");
try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer("pressed down")) {
- mLauncher.waitForLauncherObject("taskbar_view");
+ mLauncher.waitForSystemLauncherObject(TASKBAR_RES_ID);
mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_UP, unstashTarget,
LauncherInstrumentation.GestureScope.OUTSIDE_WITH_PILFER);
@@ -93,6 +117,7 @@
}
} finally {
mLauncher.getTestInfo(REQUEST_DISABLE_MANUAL_TASKBAR_STASHING);
+ mLauncher.getTestInfo(REQUEST_DISABLE_BLOCK_TIMEOUT);
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index fa7e8e9..c3ea14a 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -48,7 +48,6 @@
import android.util.Log;
import android.view.InputDevice;
import android.view.MotionEvent;
-import android.view.Surface;
import android.view.ViewConfiguration;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
@@ -68,7 +67,6 @@
import com.android.launcher3.testing.shared.ResourceUtils;
import com.android.launcher3.testing.shared.TestInformationRequest;
import com.android.launcher3.testing.shared.TestProtocol;
-import com.android.systemui.shared.system.ContextUtils;
import com.android.systemui.shared.system.QuickStepContract;
import org.junit.Assert;
@@ -85,6 +83,7 @@
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeoutException;
+import java.util.function.BooleanSupplier;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
@@ -111,8 +110,11 @@
static final Pattern EVENT_TOUCH_UP_TIS = getTouchEventPatternTIS("ACTION_UP");
static final Pattern EVENT_TOUCH_CANCEL_TIS = getTouchEventPatternTIS("ACTION_CANCEL");
- static final Pattern EVENT_KEY_BACK_DOWN = getKeyEventPattern("ACTION_DOWN", "KEYCODE_BACK");
- static final Pattern EVENT_KEY_BACK_UP = getKeyEventPattern("ACTION_UP", "KEYCODE_BACK");
+ private static final Pattern EVENT_KEY_BACK_DOWN =
+ getKeyEventPattern("ACTION_DOWN", "KEYCODE_BACK");
+ private static final Pattern EVENT_KEY_BACK_UP =
+ getKeyEventPattern("ACTION_UP", "KEYCODE_BACK");
+ private static final Pattern EVENT_ON_BACK_INVOKED = Pattern.compile("onBackInvoked");
private final String mLauncherPackage;
private Boolean mIsLauncher3;
@@ -136,6 +138,15 @@
OUTSIDE_WITH_KEYCODE,
}
+ /**
+ * Represents a point in the code at which a callback can run.
+ */
+ public enum CALLBACK_RUN_POINT {
+ CALLBACK_HOLD_BEFORE_DROP
+ }
+
+ private Consumer<CALLBACK_RUN_POINT> mCallbackAtRunPoint = null;
+
// Base class for launcher containers.
abstract static class VisibleContainer {
protected final LauncherInstrumentation mLauncher;
@@ -168,9 +179,10 @@
private static final String OVERVIEW_RES_ID = "overview_panel";
private static final String WIDGETS_RES_ID = "primary_widgets_list_view";
private static final String CONTEXT_MENU_RES_ID = "popup_container";
- private static final String TASKBAR_RES_ID = "taskbar_view";
+ static final String TASKBAR_RES_ID = "taskbar_view";
private static final String SPLIT_PLACEHOLDER_RES_ID = "split_placeholder";
public static final int WAIT_TIME_MS = 30000;
+ static final long DEFAULT_POLL_INTERVAL = 1000;
private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
private static final String ANDROID_PACKAGE = "android";
@@ -178,11 +190,13 @@
private final UiDevice mDevice;
private final Instrumentation mInstrumentation;
- private int mExpectedRotation = Surface.ROTATION_0;
+ private Integer mExpectedRotation = null;
private final Uri mTestProviderUri;
private final Deque<String> mDiagnosticContext = new LinkedList<>();
private Function<Long, String> mSystemHealthSupplier;
+ private boolean mIgnoreTaskbarVisibility = false;
+
private Consumer<ContainerType> mOnSettledStateAction;
private LogEventChecker mEventChecker;
@@ -237,7 +251,7 @@
// Launcher package. As during inproc tests the tested launcher may not be selected as the
// current launcher, choosing target package for inproc. For out-of-proc, use the installed
// launcher package.
- mLauncherPackage = testPackage.equals(targetPackage)
+ mLauncherPackage = testPackage.equals(targetPackage) || isGradleInstrumentation()
? getLauncherPackageName()
: targetPackage;
@@ -263,7 +277,7 @@
SystemClock.sleep(5000);
} else {
try {
- final int userId = ContextUtils.getUserId(getContext());
+ final int userId = getContext().getUserId();
final String launcherPidCommand = "pidof " + pi.packageName;
final String initialPid = mDevice.executeShellCommand(launcherPidCommand)
.replaceAll("\\s", "");
@@ -284,6 +298,20 @@
}
}
+ /**
+ * Gradle only supports out of process instrumentation. The test package is automatically
+ * generated by appending `.test` to the target package.
+ */
+ private boolean isGradleInstrumentation() {
+ final String testPackage = getContext().getPackageName();
+ final String targetPackage = mInstrumentation.getTargetContext().getPackageName();
+ final String testSuffix = ".test";
+
+ return testPackage.endsWith(testSuffix) && testPackage.length() > testSuffix.length()
+ && testPackage.substring(0, testPackage.length() - testSuffix.length())
+ .equals(targetPackage);
+ }
+
public void enableCheckEventsForSuccessfulGestures() {
mCheckEventsForSuccessfulGestures = true;
}
@@ -365,6 +393,10 @@
getTestInfo(TestProtocol.REQUEST_ENABLE_ROTATION, Boolean.toString(on));
}
+ public void setEnableSuggestion(boolean enableSuggestion) {
+ getTestInfo(TestProtocol.REQUEST_ENABLE_SUGGESTION, Boolean.toString(enableSuggestion));
+ }
+
public boolean hadNontestEvents() {
return getTestInfo(TestProtocol.REQUEST_GET_HAD_NONTEST_EVENTS)
.getBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD);
@@ -529,7 +561,7 @@
private String getVisibleStateMessage() {
if (hasLauncherObject(CONTEXT_MENU_RES_ID)) return "Context Menu";
if (hasLauncherObject(WIDGETS_RES_ID)) return "Widgets";
- if (hasLauncherObject(OVERVIEW_RES_ID)) return "Overview";
+ if (hasSystemLauncherObject(OVERVIEW_RES_ID)) return "Overview";
if (hasLauncherObject(WORKSPACE_RES_ID)) return "Workspace";
if (hasLauncherObject(APPS_RES_ID)) return "AllApps";
return "LaunchedApp (" + getVisiblePackages() + ")";
@@ -664,7 +696,24 @@
}
}
- public void setExpectedRotation(int expectedRotation) {
+ /**
+ * Whether to ignore verifying the task bar visibility during instrumenting.
+ *
+ * @param ignoreTaskbarVisibility {@code true} will ignore the instrumentation implicitly
+ * verifying the task bar visibility with
+ * {@link VisibleContainer#verifyActiveContainer}.
+ * {@code false} otherwise.
+ */
+ public void setIgnoreTaskbarVisibility(boolean ignoreTaskbarVisibility) {
+ mIgnoreTaskbarVisibility = ignoreTaskbarVisibility;
+ }
+
+ /**
+ * Sets expected rotation.
+ * TAPL periodically checks that Launcher didn't suddenly change the rotation to unexpected one.
+ * Null parameter disables checks. The initial state is "no checks".
+ */
+ public void setExpectedRotation(Integer expectedRotation) {
mExpectedRotation = expectedRotation;
}
@@ -701,8 +750,10 @@
private UiObject2 verifyContainerType(ContainerType containerType) {
waitForLauncherInitialized();
- assertEquals("Unexpected display rotation",
- mExpectedRotation, mDevice.getDisplayRotation());
+ if (mExpectedRotation != null) {
+ assertEquals("Unexpected display rotation",
+ mExpectedRotation, mDevice.getDisplayRotation());
+ }
final String error = getNavigationModeMismatchError(true);
assertTrue(error, error == null);
@@ -722,70 +773,89 @@
switch (containerType) {
case WORKSPACE: {
waitUntilLauncherObjectGone(APPS_RES_ID);
- waitUntilLauncherObjectGone(OVERVIEW_RES_ID);
waitUntilLauncherObjectGone(WIDGETS_RES_ID);
- waitUntilLauncherObjectGone(TASKBAR_RES_ID);
- waitUntilLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID);
+ waitUntilSystemLauncherObjectGone(OVERVIEW_RES_ID);
+ waitUntilSystemLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID);
+
+ if (is3PLauncher() && isTablet()) {
+ waitForSystemLauncherObject(TASKBAR_RES_ID);
+ } else {
+ waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
+ }
return waitForLauncherObject(WORKSPACE_RES_ID);
}
case WIDGETS: {
waitUntilLauncherObjectGone(WORKSPACE_RES_ID);
waitUntilLauncherObjectGone(APPS_RES_ID);
- waitUntilLauncherObjectGone(OVERVIEW_RES_ID);
- waitUntilLauncherObjectGone(TASKBAR_RES_ID);
- waitUntilLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID);
+ waitUntilSystemLauncherObjectGone(OVERVIEW_RES_ID);
+ waitUntilSystemLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID);
+
+ if (is3PLauncher() && isTablet()) {
+ waitForSystemLauncherObject(TASKBAR_RES_ID);
+ } else {
+ waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
+ }
return waitForLauncherObject(WIDGETS_RES_ID);
}
- case TASKBAR_ALL_APPS:
- case HOME_ALL_APPS: {
+ case TASKBAR_ALL_APPS: {
waitUntilLauncherObjectGone(WORKSPACE_RES_ID);
- waitUntilLauncherObjectGone(OVERVIEW_RES_ID);
waitUntilLauncherObjectGone(WIDGETS_RES_ID);
- waitUntilLauncherObjectGone(TASKBAR_RES_ID);
- waitUntilLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID);
+ waitUntilSystemLauncherObjectGone(OVERVIEW_RES_ID);
+ waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
+ waitUntilSystemLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID);
return waitForLauncherObject(APPS_RES_ID);
}
- case OVERVIEW: {
+ case HOME_ALL_APPS: {
+ waitUntilLauncherObjectGone(WORKSPACE_RES_ID);
+ waitUntilLauncherObjectGone(WIDGETS_RES_ID);
+ waitUntilSystemLauncherObjectGone(OVERVIEW_RES_ID);
+ waitUntilSystemLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID);
+
+ if (is3PLauncher() && isTablet()) {
+ waitForSystemLauncherObject(TASKBAR_RES_ID);
+ } else {
+ waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
+ }
+
+ return waitForLauncherObject(APPS_RES_ID);
+ }
+ case OVERVIEW:
+ case FALLBACK_OVERVIEW: {
waitUntilLauncherObjectGone(APPS_RES_ID);
waitUntilLauncherObjectGone(WORKSPACE_RES_ID);
waitUntilLauncherObjectGone(WIDGETS_RES_ID);
- waitUntilLauncherObjectGone(TASKBAR_RES_ID);
- waitUntilLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID);
+ waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
+ waitUntilSystemLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID);
- return waitForLauncherObject(OVERVIEW_RES_ID);
+ return waitForSystemLauncherObject(OVERVIEW_RES_ID);
}
case SPLIT_SCREEN_SELECT: {
waitUntilLauncherObjectGone(APPS_RES_ID);
waitUntilLauncherObjectGone(WORKSPACE_RES_ID);
waitUntilLauncherObjectGone(WIDGETS_RES_ID);
- waitUntilLauncherObjectGone(TASKBAR_RES_ID);
+ waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
- waitForLauncherObject(SPLIT_PLACEHOLDER_RES_ID);
- return waitForLauncherObject(OVERVIEW_RES_ID);
- }
- case FALLBACK_OVERVIEW: {
- waitUntilLauncherObjectGone(APPS_RES_ID);
- waitUntilLauncherObjectGone(WORKSPACE_RES_ID);
- waitUntilLauncherObjectGone(WIDGETS_RES_ID);
- waitUntilLauncherObjectGone(TASKBAR_RES_ID);
- waitUntilLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID);
-
- return waitForFallbackLauncherObject(OVERVIEW_RES_ID);
+ waitForSystemLauncherObject(SPLIT_PLACEHOLDER_RES_ID);
+ return waitForSystemLauncherObject(OVERVIEW_RES_ID);
}
case LAUNCHED_APP: {
waitUntilLauncherObjectGone(WORKSPACE_RES_ID);
waitUntilLauncherObjectGone(APPS_RES_ID);
- waitUntilLauncherObjectGone(OVERVIEW_RES_ID);
waitUntilLauncherObjectGone(WIDGETS_RES_ID);
- waitUntilLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID);
+ waitUntilSystemLauncherObjectGone(OVERVIEW_RES_ID);
+ waitUntilSystemLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID);
- if (isTablet() && !isFallbackOverview()) {
- waitForLauncherObject(TASKBAR_RES_ID);
+ if (mIgnoreTaskbarVisibility) {
+ return null;
+ }
+
+ if (isTablet()) {
+ waitForSystemLauncherObject(TASKBAR_RES_ID);
} else {
- waitUntilLauncherObjectGone(TASKBAR_RES_ID);
+ waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
}
return null;
}
@@ -796,6 +866,10 @@
}
}
+ public void waitForModelQueueCleared() {
+ getTestInfo(TestProtocol.REQUEST_MODEL_QUEUE_CLEARED);
+ }
+
public void waitForLauncherInitialized() {
for (int i = 0; i < 100; ++i) {
if (getTestInfo(
@@ -891,7 +965,14 @@
}
/**
- * Presses nav bar home button.
+ * Goes to home by swiping up in zero-button mode or pressing Home button.
+ * Calling it after another TAPL call is safe because all TAPL methods wait for the animations
+ * to finish.
+ * When calling it after a non-TAPL method, make sure that all animations have already
+ * completed, otherwise it may detect the current state (for example "Application" or "Home")
+ * incorrectly.
+ * The method expects either app or Launcher to be active when it's called. Other states, such
+ * as visible notification shade are not supported.
*
* @return the Workspace object.
*/
@@ -909,14 +990,9 @@
checkForAnomaly(false, true);
final Point displaySize = getRealDisplaySize();
- // The swipe up to home gesture starts from inside the launcher when the user is
- // already home. Otherwise, the gesture can start inside the launcher process if the
- // taskbar is visible.
- boolean gestureStartFromLauncher = isTablet()
- ? !isLauncher3()
- || hasLauncherObject(WORKSPACE_RES_ID)
- || hasLauncherObject(TASKBAR_RES_ID)
- : isLauncherVisible();
+
+ boolean gestureStartFromLauncher =
+ isTablet() ? !isLauncher3() : isLauncherVisible();
// CLose floating views before going back to home.
swipeUpToCloseFloatingView(gestureStartFromLauncher);
@@ -949,7 +1025,7 @@
NORMAL_STATE_ORDINAL,
!hasLauncherObject(WORKSPACE_RES_ID)
&& (hasLauncherObject(APPS_RES_ID)
- || hasLauncherObject(OVERVIEW_RES_ID)),
+ || hasSystemLauncherObject(OVERVIEW_RES_ID)),
action);
}
try (LauncherInstrumentation.Closable c1 = addContextLayer(
@@ -984,8 +1060,12 @@
}
}
if (launcherVisible) {
- expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_KEY_BACK_DOWN);
- expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_KEY_BACK_UP);
+ if (getContext().getApplicationInfo().isOnBackInvokedCallbackEnabled()) {
+ expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ON_BACK_INVOKED);
+ } else {
+ expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_KEY_BACK_DOWN);
+ expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_KEY_BACK_UP);
+ }
}
}
}
@@ -1001,7 +1081,8 @@
boolean isLauncherContainerVisible() {
final String[] containerResources = {WORKSPACE_RES_ID, OVERVIEW_RES_ID, APPS_RES_ID};
- return Arrays.stream(containerResources).anyMatch(r -> hasLauncherObject(r));
+ return Arrays.stream(containerResources).anyMatch(
+ r -> r.equals(OVERVIEW_RES_ID) ? hasSystemLauncherObject(r) : hasLauncherObject(r));
}
/**
@@ -1084,6 +1165,14 @@
waitUntilGoneBySelector(getOverviewObjectSelector(resId));
}
+ void waitUntilSystemLauncherObjectGone(String resId) {
+ if (is3PLauncher()) {
+ waitUntilOverviewObjectGone(resId);
+ } else {
+ waitUntilLauncherObjectGone(resId);
+ }
+ }
+
void waitUntilLauncherObjectGone(BySelector selector) {
waitUntilGoneBySelector(makeLauncherSelector(selector));
}
@@ -1214,6 +1303,11 @@
return mDevice.hasObject(getLauncherObjectSelector(resId));
}
+ private boolean hasSystemLauncherObject(String resId) {
+ return mDevice.hasObject(is3PLauncher() ? getOverviewObjectSelector(resId)
+ : getLauncherObjectSelector(resId));
+ }
+
boolean hasLauncherObject(BySelector selector) {
return mDevice.hasObject(makeLauncherSelector(selector));
}
@@ -1233,6 +1327,12 @@
}
@NonNull
+ UiObject2 waitForSystemLauncherObject(String resName) {
+ return is3PLauncher() ? waitForOverviewObject(resName)
+ : waitForLauncherObject(resName);
+ }
+
+ @NonNull
UiObject2 waitForLauncherObject(BySelector selector) {
return waitForObjectBySelector(makeLauncherSelector(selector));
}
@@ -1243,11 +1343,6 @@
}
@NonNull
- UiObject2 waitForFallbackLauncherObject(String resName) {
- return waitForObjectBySelector(getOverviewObjectSelector(resName));
- }
-
- @NonNull
UiObject2 waitForAndroidObject(String resId) {
final UiObject2 object = TestHelpers.wait(
Until.findObject(By.res(ANDROID_PACKAGE, resId)), WAIT_TIME_MS);
@@ -1284,7 +1379,7 @@
return mDevice.getLauncherPackageName();
}
- boolean isFallbackOverview() {
+ boolean is3PLauncher() {
return !getOverviewPackageName().equals(getLauncherPackageName());
}
@@ -1755,6 +1850,29 @@
getTestInfo(TestProtocol.REQUEST_UNSTASH_TASKBAR_IF_STASHED);
}
+ /** Blocks the taskbar from automatically stashing based on time. */
+ public void enableBlockTimeout(boolean enable) {
+ getTestInfo(enable
+ ? TestProtocol.REQUEST_ENABLE_BLOCK_TIMEOUT
+ : TestProtocol.REQUEST_DISABLE_BLOCK_TIMEOUT);
+ }
+
+ /** Enables transient taskbar for testing purposes only. */
+ public void enableTransientTaskbar(boolean enable) {
+ getTestInfo(enable
+ ? TestProtocol.REQUEST_ENABLE_TRANSIENT_TASKBAR
+ : TestProtocol.REQUEST_DISABLE_TRANSIENT_TASKBAR);
+ }
+
+ /**
+ * Recreates the taskbar (outside of tests this is done for certain configuration changes).
+ * The expected behavior is that the taskbar retains its current state after being recreated.
+ * For example, if taskbar is currently stashed, it should still be stashed after recreating.
+ */
+ public void recreateTaskbar() {
+ getTestInfo(TestProtocol.REQUEST_RECREATE_TASKBAR);
+ }
+
public List<String> getHotseatIconNames() {
return getTestInfo(TestProtocol.REQUEST_HOTSEAT_ICON_NAMES)
.getStringArrayList(TestProtocol.TEST_INFO_RESPONSE_FIELD);
@@ -1879,8 +1997,9 @@
/**
* Taps outside container to dismiss.
+ *
* @param container container to be dismissed
- * @param tapRight tap on the right of the container if true, or left otherwise
+ * @param tapRight tap on the right of the container if true, or left otherwise
*/
void touchOutsideContainer(UiObject2 container, boolean tapRight) {
try (LauncherInstrumentation.Closable c = addContextLayer(
@@ -1898,4 +2017,37 @@
LauncherInstrumentation.GestureScope.INSIDE);
}
}
+
+ /**
+ * Sets the consumer to run callbacks at all run-points.
+ */
+ public void setRunPointCallback(Consumer<CALLBACK_RUN_POINT> callback) {
+ mCallbackAtRunPoint = callback;
+ }
+
+ /**
+ * Runs the callback at the specified point if it exists.
+ */
+ void runCallbackIfActive(CALLBACK_RUN_POINT runPoint) {
+ if (mCallbackAtRunPoint != null) {
+ mCallbackAtRunPoint.accept(runPoint);
+ }
+ }
+
+ /**
+ * Waits until a particular condition is true. Based on WaitMixin.
+ */
+ boolean waitAndGet(BooleanSupplier condition, long timeout, long interval) {
+ long startTime = SystemClock.uptimeMillis();
+
+ boolean result = condition.getAsBoolean();
+ for (long elapsedTime = 0; !result; elapsedTime = SystemClock.uptimeMillis() - startTime) {
+ if (elapsedTime >= timeout) {
+ break;
+ }
+ SystemClock.sleep(interval);
+ result = condition.getAsBoolean();
+ }
+ return result;
+ }
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Taskbar.java b/tests/tapl/com/android/launcher3/tapl/Taskbar.java
index 5d9be36..6ca7f4b 100644
--- a/tests/tapl/com/android/launcher3/tapl/Taskbar.java
+++ b/tests/tapl/com/android/launcher3/tapl/Taskbar.java
@@ -15,6 +15,7 @@
*/
package com.android.launcher3.tapl;
+import static com.android.launcher3.tapl.LauncherInstrumentation.TASKBAR_RES_ID;
import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_DISABLE_MANUAL_TASKBAR_STASHING;
import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_ENABLE_MANUAL_TASKBAR_STASHING;
@@ -51,7 +52,7 @@
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"want to get a taskbar icon")) {
return new TaskbarAppIcon(mLauncher, mLauncher.waitForObjectInContainer(
- mLauncher.waitForLauncherObject("taskbar_view"),
+ mLauncher.waitForSystemLauncherObject(TASKBAR_RES_ID),
AppIcon.getAppIconSelector(appName, mLauncher)));
}
}
@@ -67,7 +68,7 @@
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"want to hide the taskbar");
LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
- mLauncher.waitForLauncherObject("taskbar_view");
+ mLauncher.waitForSystemLauncherObject(TASKBAR_RES_ID);
final long downTime = SystemClock.uptimeMillis();
Point stashTarget = new Point(
@@ -78,7 +79,7 @@
LauncherInstrumentation.log("hideTaskbar: sent down");
try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer("pressed down")) {
- mLauncher.waitUntilLauncherObjectGone("taskbar_view");
+ mLauncher.waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_UP, stashTarget,
LauncherInstrumentation.GestureScope.INSIDE);
}
@@ -96,7 +97,8 @@
LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
mLauncher.clickLauncherObject(mLauncher.waitForObjectInContainer(
- mLauncher.waitForLauncherObject("taskbar_view"), getAllAppsButtonSelector()));
+ mLauncher.waitForSystemLauncherObject(TASKBAR_RES_ID),
+ getAllAppsButtonSelector()));
return new AllAppsFromTaskbar(mLauncher);
}
@@ -107,7 +109,7 @@
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"want to get all taskbar icons")) {
return mLauncher.waitForObjectsInContainer(
- mLauncher.waitForLauncherObject("taskbar_view"),
+ mLauncher.waitForSystemLauncherObject(TASKBAR_RES_ID),
AppIcon.getAnyAppIconSelector())
.stream()
.map(UiObject2::getText)
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index efbdb23..425a90a 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -18,6 +18,7 @@
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;
@@ -192,6 +193,12 @@
}
}
+ /** Returns the number of pages. */
+ public int getPageCount() {
+ final UiObject2 workspace = verifyActiveContainer();
+ return workspace.getChildCount();
+ }
+
/**
* Returns the number of pages that are visible on the screen simultaneously.
*/
@@ -296,6 +303,31 @@
}
/**
+ * Drag the appIcon from the workspace and cancel by dragging icon to corner of screen where no
+ * drop point exists.
+ *
+ * @param homeAppIcon to be dragged.
+ */
+ @NonNull
+ public Workspace dragAndCancelAppIcon(HomeAppIcon homeAppIcon) {
+ try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
+ LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "dragging app icon across workspace")) {
+ dragIconToWorkspace(
+ mLauncher,
+ homeAppIcon,
+ () -> new Point(0, 0),
+ () -> mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT),
+ null);
+
+ try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
+ "dragged the app across workspace")) {
+ return new Workspace(mLauncher);
+ }
+ }
+ }
+
+ /**
* Delete the appIcon from the workspace.
*
* @param homeAppIcon to be deleted.
@@ -469,7 +501,9 @@
// Since the destination can be on another page, we need to drag to the edge first
// until we reach the target page
while (targetDest.x > displayX || targetDest.x < 0) {
- int edgeX = targetDest.x > 0 ? displayX : 0;
+ // Don't drag all the way to the edge to prevent touch events from getting out of
+ //screen bounds.
+ int edgeX = targetDest.x > 0 ? displayX - 1 : 1;
Point screenEdge = new Point(edgeX, targetDest.y);
Point finalDragStart = dragStart;
executeAndWaitForPageScroll(launcher,
@@ -485,6 +519,7 @@
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);
}
}