Merge "Revert^8 "Enable Shell Transitions""
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..12daa59 100644
--- a/AndroidManifest-common.xml
+++ b/AndroidManifest-common.xml
@@ -42,8 +42,10 @@
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<!-- for rotating surface by arbitrary degree -->
<uses-permission android:name="android.permission.ROTATE_SURFACE_FLINGER" />
+ <uses-permission android:name="android.permission.WAKEUP_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/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 9123959..a77791f 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,2 +1,4 @@
[Hook Scripts]
checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --config_xml tools/checkstyle.xml --sha ${PREUPLOAD_COMMIT}
+
+ktfmt_hook = ${REPO_ROOT}/external/ktfmt/ktfmt.py --check ${PREUPLOAD_FILES}
\ No newline at end of file
diff --git a/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java b/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java
index a91ff44..a645e58 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;
@@ -209,12 +210,19 @@
}
case TestProtocol.REQUEST_USE_TEST_WORKSPACE_LAYOUT: {
- useTestWorkspaceLayout(true);
+ useTestWorkspaceLayout(
+ LauncherSettings.Settings.ARG_DEFAULT_WORKSPACE_LAYOUT_TEST);
+ return response;
+ }
+
+ case TestProtocol.REQUEST_USE_TEST2_WORKSPACE_LAYOUT: {
+ useTestWorkspaceLayout(
+ LauncherSettings.Settings.ARG_DEFAULT_WORKSPACE_LAYOUT_TEST2);
return response;
}
case TestProtocol.REQUEST_USE_DEFAULT_WORKSPACE_LAYOUT: {
- useTestWorkspaceLayout(false);
+ useTestWorkspaceLayout(null);
return response;
}
@@ -248,17 +256,25 @@
return response;
}
+ case TestProtocol.REQUEST_MODEL_QUEUE_CLEARED:
+ return getFromExecutorSync(MODEL_EXECUTOR, Bundle::new);
+
default:
return super.call(method, arg, extras);
}
}
- private void useTestWorkspaceLayout(boolean useTestWorkspaceLayout) {
+ private void useTestWorkspaceLayout(String layout) {
final long identity = Binder.clearCallingIdentity();
try {
- LauncherSettings.Settings.call(mContext.getContentResolver(), useTestWorkspaceLayout
- ? LauncherSettings.Settings.METHOD_SET_USE_TEST_WORKSPACE_LAYOUT_FLAG
- : LauncherSettings.Settings.METHOD_CLEAR_USE_TEST_WORKSPACE_LAYOUT_FLAG);
+ if (layout != null) {
+ LauncherSettings.Settings.call(mContext.getContentResolver(),
+ LauncherSettings.Settings.METHOD_SET_USE_TEST_WORKSPACE_LAYOUT_FLAG,
+ layout);
+ } else {
+ LauncherSettings.Settings.call(mContext.getContentResolver(),
+ LauncherSettings.Settings.METHOD_CLEAR_USE_TEST_WORKSPACE_LAYOUT_FLAG);
+ }
} finally {
Binder.restoreCallingIdentity(identity);
}
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-ky/strings.xml b/go/quickstep/res/values-ky/strings.xml
index e4a2474..55e70c8 100644
--- a/go/quickstep/res/values-ky/strings.xml
+++ b/go/quickstep/res/values-ky/strings.xml
@@ -9,7 +9,7 @@
<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>
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/go/quickstep/res/values-or/strings.xml b/go/quickstep/res/values-or/strings.xml
index 36de437..6a3c5dc 100644
--- a/go/quickstep/res/values-or/strings.xml
+++ b/go/quickstep/res/values-or/strings.xml
@@ -1,7 +1,7 @@
<?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">"ଆପ୍ ସେୟାର୍ କରନ୍ତୁ"</string>
+ <string name="app_share_drop_target_label" msgid="5804774105974539508">"ଆପ ସେୟାର କରନ୍ତୁ"</string>
<string name="action_listen" msgid="2370304050784689486">"ଶୁଣନ୍ତୁ"</string>
<string name="action_translate" msgid="8028378961867277746">"ଅନୁବାଦ କରନ୍ତୁ"</string>
<string name="action_search" msgid="6269564710943755464">"Lens"</string>
diff --git a/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java b/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java
index c997e52..253147d 100644
--- a/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java
+++ b/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java
@@ -50,8 +50,8 @@
import androidx.annotation.VisibleForTesting;
import com.android.launcher3.BaseActivity;
+import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
import com.android.launcher3.views.ArrowTipView;
import com.android.quickstep.util.AssistContentRequester;
import com.android.quickstep.util.RecentsOrientedState;
@@ -124,7 +124,7 @@
AssistContentRequester assistContentRequester) {
super(taskThumbnailView);
mFactoryContentRequester = assistContentRequester;
- mSharedPreferences = Utilities.getPrefs(mApplicationContext);
+ mSharedPreferences = LauncherPrefs.getPrefs(mApplicationContext);
}
/**
@@ -151,7 +151,7 @@
boolean isAllowedByPolicy = mThumbnailView.isRealSnapshot() && !isManagedProfileTask;
getActionsView().setCallbacks(new OverlayUICallbacksGoImpl(isAllowedByPolicy, task));
mTaskPackageName = task.key.getPackageName();
- mSharedPreferences = Utilities.getPrefs(mApplicationContext);
+ mSharedPreferences = LauncherPrefs.getPrefs(mApplicationContext);
checkSettings();
if (!mAssistStructurePermitted || !mAssistScreenshotPermitted
diff --git a/go/src/com/android/launcher3/model/LoaderResults.java b/go/src/com/android/launcher3/model/LauncherBinder.java
similarity index 82%
rename from go/src/com/android/launcher3/model/LoaderResults.java
rename to go/src/com/android/launcher3/model/LauncherBinder.java
index 5f71061..437d8ca 100644
--- a/go/src/com/android/launcher3/model/LoaderResults.java
+++ b/go/src/com/android/launcher3/model/LauncherBinder.java
@@ -22,11 +22,11 @@
import com.android.launcher3.model.BgDataModel.Callbacks;
/**
- * Helper class to handle results of {@link com.android.launcher3.model.LoaderTask}.
+ * Binds the results of {@link com.android.launcher3.model.LoaderTask} to the Callbacks objects.
*/
-public class LoaderResults extends BaseLoaderResults {
+public class LauncherBinder extends BaseLauncherBinder {
- public LoaderResults(LauncherAppState app, BgDataModel dataModel,
+ public LauncherBinder(LauncherAppState app, BgDataModel dataModel,
AllAppsList allAppsList, Callbacks[] callbacks) {
super(app, dataModel, allAppsList, callbacks, MAIN_EXECUTOR);
}
diff --git a/proguard.flags b/proguard.flags
index 1f5c065..31edd8d 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -2,10 +2,6 @@
*;
}
--keep class com.android.launcher3.graphics.ShadowDrawable {
- public <init>(...);
-}
-
# The support library contains references to newer platform versions.
# Don't warn about those in case this app is linking against an older
# platform version. We know about them, and they are safe.
diff --git a/protos/launcher_atom.proto b/protos/launcher_atom.proto
index 10eedc8..677c992 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{
@@ -127,7 +130,7 @@
optional int32 cardinality = 2;
}
-// Next value 40
+// Next value 41
enum Attribute {
UNKNOWN = 0;
DEFAULT_LAYOUT = 1; // icon automatically placed in workspace, folder, hotseat
@@ -183,6 +186,7 @@
WEB_SEARCH_RESULT_PERSONAL = 36;
WEB_SEARCH_RESULT_CALCULATOR = 37;
WEB_SEARCH_RESULT_URL = 38;
+ WEB_SEARCH_RESULT_RICH_ANSWER = 40;
WIDGETS_BOTTOM_TRAY = 28;
WIDGETS_TRAY_PREDICTION = 29;
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/AndroidManifest.xml b/quickstep/AndroidManifest.xml
index 352cd3e..3647e05 100644
--- a/quickstep/AndroidManifest.xml
+++ b/quickstep/AndroidManifest.xml
@@ -37,6 +37,7 @@
<uses-permission android:name="android.permission.MANAGE_ACCESSIBILITY"/>
<uses-permission android:name="android.permission.MONITOR_INPUT"/>
<uses-permission android:name="android.permission.ALLOW_SLIPPERY_TOUCHES"/>
+ <uses-permission android:name="android.permission.ACCESS_SHORTCUTS"/>
<uses-permission android:name="android.permission.SYSTEM_APPLICATION_OVERLAY" />
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/drawable/bg_taskbar_edu_tooltip.xml b/quickstep/res/drawable/bg_taskbar_edu_tooltip.xml
new file mode 100644
index 0000000..a20f7da
--- /dev/null
+++ b/quickstep/res/drawable/bg_taskbar_edu_tooltip.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:shape="rectangle">
+
+ <corners android:radius="@dimen/dialogCornerRadius" />
+ <solid android:color="?androidprv:attr/colorSurface" />
+</shape>
\ No newline at end of file
diff --git a/quickstep/res/drawable/taskbar_edu_splitscreen.png b/quickstep/res/drawable/taskbar_edu_splitscreen.png
deleted file mode 100644
index f9d2a63..0000000
--- a/quickstep/res/drawable/taskbar_edu_splitscreen.png
+++ /dev/null
Binary files differ
diff --git a/quickstep/res/drawable/taskbar_edu_stashing.png b/quickstep/res/drawable/taskbar_edu_stashing.png
deleted file mode 100644
index f9d2a63..0000000
--- a/quickstep/res/drawable/taskbar_edu_stashing.png
+++ /dev/null
Binary files differ
diff --git a/quickstep/res/drawable/taskbar_edu_switch_apps.png b/quickstep/res/drawable/taskbar_edu_switch_apps.png
deleted file mode 100644
index f9d2a63..0000000
--- a/quickstep/res/drawable/taskbar_edu_switch_apps.png
+++ /dev/null
Binary files differ
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/all_apps_edu_view.xml b/quickstep/res/layout/all_apps_edu_view.xml
index e7ef6e6..0dd4df1 100644
--- a/quickstep/res/layout/all_apps_edu_view.xml
+++ b/quickstep/res/layout/all_apps_edu_view.xml
@@ -3,4 +3,5 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="@dimen/swipe_edu_width"
- android:layout_height="@dimen/swipe_edu_max_height"/>
+ android:layout_height="@dimen/swipe_edu_max_height"
+ android:accessibilityPaneTitle="@string/taskbar_edu_a11y_title"/>
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.xml b/quickstep/res/layout/task.xml
index 7e5b85c..bd11c1e 100644
--- a/quickstep/res/layout/task.xml
+++ b/quickstep/res/layout/task.xml
@@ -28,6 +28,19 @@
android:layout_width="match_parent"
android:layout_height="match_parent"/>
+ <!-- Filtering affects only alpha instead of the visibility since visibility can be altered
+ separately through RecentsView#resetFromSplitSelectionState() -->
+ <ImageView
+ android:id="@+id/show_windows"
+ android:layout_height="@dimen/recents_filter_icon_size"
+ android:layout_width="@dimen/recents_filter_icon_size"
+ android:layout_gravity="end"
+ android:alpha="0"
+ android:tint="@color/recents_filter_icon"
+ android:contentDescription="@string/recents_filter_icon_desc"
+ android:importantForAccessibility="no"
+ android:src="@drawable/ic_select_windows" />
+
<com.android.quickstep.views.IconView
android:id="@+id/icon"
android:layout_width="@dimen/task_thumbnail_icon_size"
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/task_grouped.xml b/quickstep/res/layout/task_grouped.xml
index cd5bcbd..ec03c69 100644
--- a/quickstep/res/layout/task_grouped.xml
+++ b/quickstep/res/layout/task_grouped.xml
@@ -38,6 +38,32 @@
android:layout_width="match_parent"
android:layout_height="match_parent"/>
+ <!-- Filtering affects only alpha instead of the visibility since visibility can be altered
+ separately through RecentsView#resetFromSplitSelectionState() -->
+ <ImageView
+ android:id="@+id/show_windows"
+ android:layout_height="@dimen/recents_filter_icon_size"
+ android:layout_width="@dimen/recents_filter_icon_size"
+ android:layout_gravity="start"
+ android:alpha="0"
+ android:tint="@color/recents_filter_icon"
+ android:contentDescription="@string/recents_filter_icon_desc"
+ android:importantForAccessibility="no"
+ android:src="@drawable/ic_select_windows" />
+
+ <!-- Filtering affects only alpha instead of the visibility since visibility can be altered
+ separately through RecentsView#resetFromSplitSelectionState() -->
+ <ImageView
+ android:id="@+id/show_windows_right"
+ android:layout_height="@dimen/recents_filter_icon_size"
+ android:layout_width="@dimen/recents_filter_icon_size"
+ android:layout_gravity="end"
+ android:alpha="0"
+ android:tint="@color/recents_filter_icon"
+ android:contentDescription="@string/recents_filter_icon_desc"
+ android:importantForAccessibility="no"
+ android:src="@drawable/ic_select_windows" />
+
<com.android.quickstep.views.IconView
android:id="@+id/icon"
android:layout_width="@dimen/task_thumbnail_icon_size"
diff --git a/quickstep/res/layout/taskbar_all_apps.xml b/quickstep/res/layout/taskbar_all_apps.xml
index 34d4b23..976cd9e 100644
--- a/quickstep/res/layout/taskbar_all_apps.xml
+++ b/quickstep/res/layout/taskbar_all_apps.xml
@@ -17,7 +17,8 @@
<com.android.launcher3.taskbar.allapps.TaskbarAllAppsSlideInView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="match_parent"
+ android:accessibilityPaneTitle="@string/all_apps_label">
<com.android.launcher3.taskbar.allapps.TaskbarAllAppsContainerView
android:id="@+id/apps_view"
@@ -26,41 +27,5 @@
android:clipChildren="true"
android:clipToPadding="false"
android:focusable="false"
- android:saveEnabled="false"
- android:theme="?attr/allAppsTheme">
-
- <include
- layout="@layout/all_apps_bottom_sheet_background"
- android:visibility="gone" />
-
- <include
- layout="@layout/search_results_rv_layout"
- android:visibility="gone" />
-
- <include
- layout="@layout/all_apps_rv_layout"
- android:visibility="gone" />
-
- <com.android.launcher3.allapps.FloatingHeaderView
- android:id="@+id/all_apps_header"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_below="@id/search_container_all_apps"
- android:clipToPadding="false"
- android:paddingTop="@dimen/all_apps_header_top_padding"
- android:orientation="vertical">
-
- <include layout="@layout/floating_header_content" />
-
- <include layout="@layout/all_apps_personal_work_tabs" />
- </com.android.launcher3.allapps.FloatingHeaderView>
-
- <com.android.launcher3.taskbar.allapps.TaskbarAllAppsFallbackSearchContainer
- android:id="@+id/search_container_all_apps"
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:visibility="gone" />
-
- <include layout="@layout/all_apps_fast_scroller" />
- </com.android.launcher3.taskbar.allapps.TaskbarAllAppsContainerView>
+ android:saveEnabled="false" />
</com.android.launcher3.taskbar.allapps.TaskbarAllAppsSlideInView>
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..6b665e5
--- /dev/null
+++ b/quickstep/res/layout/taskbar_all_apps_button.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+
+<!-- Note: The actual size will match the taskbar icon sizes in TaskbarView#onLayout(). -->
+<com.android.launcher3.views.IconButtonView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="@dimen/taskbar_icon_min_touch_size"
+ android:layout_height="@dimen/taskbar_icon_min_touch_size"
+ android:contentDescription="@string/all_apps_button_label"
+ android:backgroundTint="@android:color/transparent"
+ android:icon="@drawable/ic_all_apps_button"
+ />
diff --git a/quickstep/res/layout/taskbar_edu.xml b/quickstep/res/layout/taskbar_edu.xml
index 3796ff9..d7daea3 100644
--- a/quickstep/res/layout/taskbar_edu.xml
+++ b/quickstep/res/layout/taskbar_edu.xml
@@ -40,74 +40,13 @@
android:layout_width="match_parent"
android:layout_height="378dp"
app:layout_constraintTop_toTopOf="parent"
- launcher:pageIndicator="@+id/content_page_indicator">
-
- <LinearLayout
- android:id="@+id/page_switch_apps"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:gravity="center_horizontal">
-
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- style="@style/TextAppearance.TaskbarEdu.Title"
- android:text="@string/taskbar_edu_switch_apps"/>
-
- <ImageView
- android:layout_width="322dp"
- android:layout_height="282dp"
- android:layout_marginTop="16dp"
- android:src="@drawable/taskbar_edu_switch_apps"/>
- </LinearLayout>
-
- <LinearLayout
- android:id="@+id/page_splitscreen"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:gravity="center_horizontal">
-
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- style="@style/TextAppearance.TaskbarEdu.Title"
- android:text="@string/taskbar_edu_splitscreen"/>
-
- <ImageView
- android:layout_width="322dp"
- android:layout_height="282dp"
- android:layout_marginTop="16dp"
- android:src="@drawable/taskbar_edu_splitscreen"/>
- </LinearLayout>
-
- <LinearLayout
- android:id="@+id/page_stashing"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:gravity="center_horizontal">
-
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- style="@style/TextAppearance.TaskbarEdu.Title"
- android:text="@string/taskbar_edu_stashing"/>
-
- <ImageView
- android:layout_width="322dp"
- android:layout_height="282dp"
- android:layout_marginTop="16dp"
- android:src="@drawable/taskbar_edu_stashing"/>
- </LinearLayout>
- </com.android.launcher3.taskbar.TaskbarEduPagedView>
+ launcher:pageIndicator="@+id/content_page_indicator" />
<Button
android:id="@+id/edu_start_button"
android:layout_width="wrap_content"
android:layout_height="36dp"
- android:layout_marginBottom="92dp"
+ android:layout_marginBottom="18dp"
android:layout_marginTop="32dp"
app:layout_constraintTop_toBottomOf="@id/content"
app:layout_constraintBottom_toBottomOf="parent"
diff --git a/quickstep/res/layout/taskbar_edu_features.xml b/quickstep/res/layout/taskbar_edu_features.xml
new file mode 100644
index 0000000..22b99e8
--- /dev/null
+++ b/quickstep/res/layout/taskbar_edu_features.xml
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+
+ <TextView
+ android:id="@+id/title"
+ style="@style/TextAppearance.TaskbarEduTooltip.Title"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:text="@string/taskbar_edu_features"
+ app:layout_constraintEnd_toEndOf="@id/suggestions_animation"
+ app:layout_constraintStart_toStartOf="@id/splitscreen_animation"
+ app:layout_constraintTop_toTopOf="parent" />
+
+ <com.airbnb.lottie.LottieAnimationView
+ android:id="@+id/splitscreen_animation"
+ android:layout_width="@dimen/taskbar_edu_features_lottie_width"
+ android:layout_height="@dimen/taskbar_edu_features_lottie_height"
+ android:layout_marginTop="@dimen/taskbar_edu_tooltip_vertical_margin"
+ android:scaleType="centerCrop"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/title"
+ app:lottie_autoPlay="true"
+ app:lottie_loop="true"
+ app:lottie_rawRes="@raw/taskbar_edu_splitscreen" />
+
+ <TextView
+ android:id="@+id/splitscreen_text"
+ style="@style/TextAppearance.TaskbarEduTooltip.Subtext"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:text="@string/taskbar_edu_splitscreen"
+ app:layout_constraintEnd_toEndOf="@id/splitscreen_animation"
+ app:layout_constraintStart_toStartOf="@id/splitscreen_animation"
+ app:layout_constraintTop_toBottomOf="@id/splitscreen_animation" />
+
+ <com.airbnb.lottie.LottieAnimationView
+ android:id="@+id/suggestions_animation"
+ android:layout_width="@dimen/taskbar_edu_features_lottie_width"
+ android:layout_height="@dimen/taskbar_edu_features_lottie_height"
+ android:layout_marginStart="@dimen/taskbar_edu_features_horizontal_spacing"
+ android:layout_marginTop="@dimen/taskbar_edu_tooltip_vertical_margin"
+ android:scaleType="centerCrop"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toEndOf="@id/splitscreen_animation"
+ app:layout_constraintTop_toBottomOf="@id/title"
+ app:lottie_autoPlay="true"
+ app:lottie_loop="true"
+ app:lottie_rawRes="@raw/taskbar_edu_suggestions" />
+
+ <TextView
+ android:id="@+id/suggestions_text"
+ style="@style/TextAppearance.TaskbarEduTooltip.Subtext"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:text="@string/taskbar_edu_suggestions"
+ app:layout_constraintEnd_toEndOf="@id/suggestions_animation"
+ app:layout_constraintStart_toStartOf="@id/suggestions_animation"
+ app:layout_constraintTop_toBottomOf="@id/suggestions_animation" />
+
+ <Button
+ android:id="@+id/done_button"
+ style="@style/TaskbarEdu.Button.Next"
+ android:layout_width="wrap_content"
+ android:layout_height="36dp"
+ android:layout_marginTop="32dp"
+ android:text="@string/taskbar_edu_done"
+ android:textColor="?androidprv:attr/textColorOnAccent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/suggestions_text" />
+</merge>
\ No newline at end of file
diff --git a/quickstep/res/layout/taskbar_edu_pages_persistent.xml b/quickstep/res/layout/taskbar_edu_pages_persistent.xml
new file mode 100644
index 0000000..77ce31a
--- /dev/null
+++ b/quickstep/res/layout/taskbar_edu_pages_persistent.xml
@@ -0,0 +1,58 @@
+<?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">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical">
+
+ <TextView
+ style="@style/TextAppearance.TaskbarEdu.Title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/taskbar_edu_splitscreen" />
+
+ <com.airbnb.lottie.LottieAnimationView
+ android:id="@+id/animation"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ app:lottie_rawRes="@raw/taskbar_edu_splitscreen_persistent" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical">
+
+ <TextView
+ style="@style/TextAppearance.TaskbarEdu.Title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/taskbar_edu_suggestions" />
+
+ <com.airbnb.lottie.LottieAnimationView
+ android:id="@id/animation"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ app:lottie_rawRes="@raw/taskbar_edu_suggestions_persistent" />
+ </LinearLayout>
+</merge>
diff --git a/quickstep/res/layout/taskbar_edu_pages_transient.xml b/quickstep/res/layout/taskbar_edu_pages_transient.xml
new file mode 100644
index 0000000..b68e723
--- /dev/null
+++ b/quickstep/res/layout/taskbar_edu_pages_transient.xml
@@ -0,0 +1,78 @@
+<?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">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical">
+
+ <TextView
+ style="@style/TextAppearance.TaskbarEdu.Title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/taskbar_edu_stashing" />
+
+ <com.airbnb.lottie.LottieAnimationView
+ android:id="@+id/animation"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ app:lottie_rawRes="@raw/taskbar_edu_stashing_transient" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical">
+
+ <TextView
+ style="@style/TextAppearance.TaskbarEdu.Title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/taskbar_edu_splitscreen" />
+
+ <com.airbnb.lottie.LottieAnimationView
+ android:id="@id/animation"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ app:lottie_rawRes="@raw/taskbar_edu_splitscreen_transient" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical">
+
+ <TextView
+ style="@style/TextAppearance.TaskbarEdu.Title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/taskbar_edu_suggestions" />
+
+ <com.airbnb.lottie.LottieAnimationView
+ android:id="@id/animation"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ app:lottie_rawRes="@raw/taskbar_edu_suggestions_transient" />
+ </LinearLayout>
+</merge>
\ No newline at end of file
diff --git a/quickstep/res/layout/taskbar_edu_swipe.xml b/quickstep/res/layout/taskbar_edu_swipe.xml
new file mode 100644
index 0000000..d8b56b5
--- /dev/null
+++ b/quickstep/res/layout/taskbar_edu_swipe.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+
+ <TextView
+ android:id="@+id/title"
+ style="@style/TextAppearance.TaskbarEduTooltip.Title"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:text="@string/taskbar_edu_stashing"
+ app:layout_constraintEnd_toEndOf="@id/swipe_animation"
+ app:layout_constraintStart_toStartOf="@id/swipe_animation"
+ app:layout_constraintTop_toTopOf="parent" />
+
+ <com.airbnb.lottie.LottieAnimationView
+ android:id="@+id/swipe_animation"
+ android:layout_width="@dimen/taskbar_edu_swipe_lottie_width"
+ android:layout_height="@dimen/taskbar_edu_swipe_lottie_height"
+ android:layout_marginTop="@dimen/taskbar_edu_tooltip_vertical_margin"
+ android:scaleType="centerCrop"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/title"
+ app:lottie_autoPlay="true"
+ app:lottie_loop="true"
+ app:lottie_rawRes="@raw/taskbar_edu_stashing" />
+
+</merge>
\ No newline at end of file
diff --git a/quickstep/res/layout/taskbar_edu_tooltip.xml b/quickstep/res/layout/taskbar_edu_tooltip.xml
new file mode 100644
index 0000000..29f4956
--- /dev/null
+++ b/quickstep/res/layout/taskbar_edu_tooltip.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<com.android.launcher3.taskbar.TaskbarEduTooltip xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom|center"
+ android:layout_marginBottom="16dp"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:fitsSystemWindows="true"
+ android:gravity="center"
+ android:orientation="vertical">
+
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:id="@+id/content"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="@drawable/bg_taskbar_edu_tooltip"
+ android:elevation="@dimen/taskbar_edu_tooltip_elevation"
+ android:paddingHorizontal="@dimen/taskbar_edu_tooltip_horizontal_margin"
+ android:paddingVertical="@dimen/taskbar_edu_tooltip_vertical_margin" />
+
+ <View
+ android:id="@+id/arrow"
+ android:layout_width="@dimen/popup_arrow_width"
+ android:layout_height="@dimen/popup_arrow_height"
+ android:elevation="@dimen/taskbar_edu_tooltip_elevation" />
+</com.android.launcher3.taskbar.TaskbarEduTooltip>
\ No newline at end of file
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-night/taskbar_edu_splitscreen_persistent.json b/quickstep/res/raw-night/taskbar_edu_splitscreen_persistent.json
new file mode 100644
index 0000000..2028083
--- /dev/null
+++ b/quickstep/res/raw-night/taskbar_edu_splitscreen_persistent.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":154,"w":412,"h":300,"nm":"Taskbar_Persistent_DT_Step_1","ddd":0,"assets":[{"id":"comp_0","nm":"Pre-comp_Taskbar_Persistent_Layers","fr":60,"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 2","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,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":11306,"st":-94,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"screen_matte","parent":1,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[81,0,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,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[320,204.01],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":13,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11306,"st":-94,"bm":0},{"ddd":0,"ind":3,"ty":0,"nm":"Pre-comp_TaskBar_Persistent","parent":1,"tt":1,"refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-1.5,87.892,0],"ix":2,"l":2},"a":{"a":0,"k":[206,227.625,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":11306,"st":-94,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"screen 3","parent":1,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[81,0,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,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[320,204.01],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":13,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":175,"op":11306,"st":-94,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".blue100","cl":"blue100","parent":1,"tt":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":155,"s":[0]},{"t":175,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[81,0.5,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,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[320,174.01],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":13,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.823529411765,0.890196078431,0.988235294118,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,-15.25],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":175,"op":11541,"st":141,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".blue100","cl":"blue100","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-73.438,0,0],"ix":2,"l":2},"a":{"a":0,"k":[-154.438,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":43,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":93,"s":[95,95,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":155,"s":[95,95,100]},{"t":175,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":43,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[13.125,-89.005],[13.125,59.005],[0.125,72.005],[-147,72.005],[-160,59.005],[-160,-89.005],[-147,-102.005],[0.125,-102.005]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.8,"y":0},"t":93,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[0,-89.005],[0,62.689],[-13,75.689],[-147,75.689],[-160,62.689],[-160,-89.005],[-147,-102.005],[-13,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.167,"y":0},"t":155,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[0,-89.005],[0,62.689],[-13,75.689],[-147,75.689],[-160,62.689],[-160,-89.005],[-147,-102.005],[-13,-102.005]],"c":true}]},{"t":175,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[13.125,-89.005],[13.125,59.005],[0.125,72.005],[-147,72.005],[-160,59.005],[-160,-89.005],[-147,-102.005],[0.125,-102.005]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.823529411765,0.890196078431,0.988235294118,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":175,"st":-94,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".blue100","cl":"blue100","parent":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.8],"y":[0]},"t":43,"s":[100]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.8],"y":[0]},"t":93,"s":[0]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":155,"s":[0]},{"t":175,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[73.438,0,0],"ix":2,"l":2},"a":{"a":0,"k":[-7.562,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":43,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":93,"s":[95,95,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":155,"s":[95,95,100]},{"t":175,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":43,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,59.005],[147,72.005],[-0.125,72.005],[-13.125,59.005],[-13.125,-89.005],[-0.125,-102.005],[147,-102.005]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.8,"y":0},"t":93,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,62.689],[147,75.689],[13,75.689],[0,62.689],[0,-89.005],[13,-102.005],[147,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.167,"y":0},"t":155,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,62.689],[147,75.689],[13,75.689],[0,62.689],[0,-89.005],[13,-102.005],[147,-102.005]],"c":true}]},{"t":175,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,59.005],[147,72.005],[-0.125,72.005],[-13.125,59.005],[-13.125,-89.005],[-0.125,-102.005],[147,-102.005]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.823529411765,0.890196078431,0.988235294118,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":175,"st":-94,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".yellow100","cl":"yellow100","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[73.438,0,0],"ix":2,"l":2},"a":{"a":0,"k":[-7.562,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":43,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":93,"s":[95,95,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":155,"s":[95,95,100]},{"t":175,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":43,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,59.005],[147,72.005],[-0.125,72.005],[-13.125,59.005],[-13.125,-89.005],[-0.125,-102.005],[147,-102.005]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.8,"y":0},"t":93,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,62.689],[147,75.689],[13,75.689],[0,62.689],[0,-89.005],[13,-102.005],[147,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.167,"y":0},"t":155,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,62.689],[147,75.689],[13,75.689],[0,62.689],[0,-89.005],[13,-102.005],[147,-102.005]],"c":true}]},{"t":175,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,59.005],[147,72.005],[-0.125,72.005],[-13.125,59.005],[-13.125,-89.005],[-0.125,-102.005],[147,-102.005]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.996078431373,0.937254901961,0.764705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":175,"st":-94,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".grey800","cl":"grey800","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[81,0,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,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[320,204.01],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":13,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11306,"st":-94,"bm":0}]},{"id":"comp_1","nm":"Pre-comp_TaskBar_Persistent","fr":60,"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 1","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[237.75,183,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[50,50,100],"ix":6,"l":2}},"ao":0,"ip":102,"op":255,"st":90,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"swipe up 2","parent":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":100,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":110,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":167,"s":[100]},{"t":177,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":100,"s":[-45.3,33.917,0],"to":[5.5,9,0],"ti":[-12,-36,0]},{"i":{"x":0.3,"y":0.3},"o":{"x":0.333,"y":0.333},"t":127,"s":[-12.3,87.917,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[-12.3,87.917,0],"to":[0,0,0],"ti":[-99.967,45.186,0]},{"t":187,"s":[89.5,-76.7,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.1,0.1,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":127,"s":[100,100,100]},{"i":{"x":[0.1,0.1,0.667],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.333],"y":[0,0,0]},"t":137,"s":[80,80,100]},{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.333],"y":[0,0,0]},"t":167,"s":[80,80,100]},{"t":179,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"ef":[{"ty":25,"nm":"Drop Shadow","np":8,"mn":"ADBE Drop Shadow","ix":1,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":63.75,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":8,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":30,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]},{"ty":25,"nm":"Drop Shadow 2","np":8,"mn":"ADBE Drop Shadow","ix":2,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":40,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]}],"shapes":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.5,0.5],"y":[1,1]},"o":{"x":[0.5,0.5],"y":[0,0]},"t":240,"s":[56,56]},{"i":{"x":[0.5,0.5],"y":[1,1]},"o":{"x":[0.5,0.5],"y":[0,0]},"t":262,"s":[56,98]},{"t":288,"s":[56,56]}],"ix":2},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[0,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.8,"y":0},"t":187,"s":[0,14],"to":[0,0],"ti":[0,0]},{"t":189,"s":[0,0]}],"ix":3},"r":{"a":0,"k":67,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,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":[1,1,1,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":102,"op":234,"st":77,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".grey300","cl":"grey300","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-83.044,0.454,0],"ix":2,"l":2},"a":{"a":0,"k":[121.456,227.454,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[125.547,231.545],"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":"Layer 18","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[125.547,227.455],"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":"Layer 17","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[125.547,223.364],"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":"Layer 16","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[121.453,231.545],"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":"Layer 15","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[121.453,227.455],"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":"Layer 14","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[121.453,223.364],"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":"Layer 13","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[117.364,231.545],"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":"Layer 12","np":1,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[117.364,227.455],"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":"Layer 11","np":1,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[117.364,223.364],"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":"Layer 10","np":1,"cix":2,"bm":0,"ix":9,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"green circle matte","parent":20,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84.409,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.807843148708,0.917647063732,0.839215695858,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.807843137255,0.917647058824,0.839215686275,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".green400","cl":"green400","parent":20,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":362,"s":[84.522,0.001,0],"to":[0,-3.333,0],"ti":[0,3.333,0]},{"t":392,"s":[84.522,-19.999,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[80,80,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.35686275363,0.72549021244,0.454901963472,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"green circle matte 2","parent":20,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84.409,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.807843148708,0.917647063732,0.839215695858,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.807843137255,0.917647058824,0.839215686275,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".green400","cl":"green400","parent":20,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":365,"s":[84.522,20.001,0],"to":[0,-3.333,0],"ti":[0,3.333,0]},{"t":395,"s":[84.522,0.001,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[80,80,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.35686275363,0.72549021244,0.454901963472,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".green100","cl":"green100","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84.409,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.807843148708,0.917647063732,0.839215695858,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.807843137255,0.917647058824,0.839215686275,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"blue circle matte 2","parent":20,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[55.772,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.541175991881,0.705881993911,0.972549019608,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".blue400","cl":"blue400","parent":20,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":356,"s":[55.772,0.001,0],"to":[0,-3.333,0],"ti":[0,3.333,0]},{"t":386,"s":[55.772,-19.999,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[80,80,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"blue circle matte","parent":20,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[55.772,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.541175991881,0.705881993911,0.972549019608,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".blue400","cl":"blue400","parent":20,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":359,"s":[55.772,20.001,0],"to":[0,-3.333,0],"ti":[0,3.333,0]},{"t":389,"s":[55.772,0.001,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[80,80,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".blue100","cl":"blue100","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[55.772,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.823529422283,0.890196084976,0.988235294819,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.823529411765,0.890196078431,0.988235294118,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":".yellow400","cl":"yellow400","parent":20,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":177,"s":[0]},{"t":187,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":947,"s":[27.135,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":957,"s":[27.135,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":967,"s":[27.135,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.988235294819,0.78823530674,0.203921571374,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":177,"op":11577,"st":177,"bm":0},{"ddd":0,"ind":15,"ty":4,"nm":".yelllow400","cl":"yelllow400","parent":20,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":167,"s":[100]},{"t":177,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[27.135,0.001,0],"to":[0.728,-9.542,0],"ti":[-45.478,19.292,0]},{"t":187,"s":[78,-76.749,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.988235294118,0.788235294118,0.203921568627,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":16,"ty":4,"nm":".grey400","cl":"grey400","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":944,"s":[27.135,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":954,"s":[27.135,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":964,"s":[27.135,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.741176470588,0.756862745098,0.776470588235,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":137,"op":252,"st":0,"bm":0},{"ddd":0,"ind":17,"ty":4,"nm":".green400","cl":"green400","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":941,"s":[-1.498,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":951,"s":[-1.498,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":961,"s":[-1.498,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.35686275363,0.72549021244,0.454901963472,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":18,"ty":4,"nm":".red400","cl":"red400","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":938,"s":[-30.134,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":948,"s":[-30.134,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":958,"s":[-30.134,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333337307,0.403921574354,0.360784322023,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":19,"ty":4,"nm":".blue400","cl":"blue400","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":935,"s":[-58.771,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":945,"s":[-58.771,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":955,"s":[-58.771,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.61568627451,0.964705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":20,"ty":4,"nm":".grey800","cl":"grey800","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[204.5,227.001,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0.001,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":[[-8.284,0],[0,0],[0,-8.284],[0,0],[8.284,0],[0,0],[0,8.284],[0,0]],"o":[[0,0],[8.284,0],[0,0],[0,8.284],[0,0],[-8.284,0],[0,0],[0,-8.284]],"v":[[-86.5,-15],[86.5,-15],[101.5,0],[101.5,0],[86.5,15],[-86.5,15],[-101.5,0],[-101.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"screen","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[287,150,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,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[320,204.01],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":13,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11306,"st":-94,"bm":0},{"ddd":0,"ind":2,"ty":0,"nm":"Pre-comp_Taskbar_Persistent_Layers","tt":1,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":205,"s":[206,150,0],"to":[-41.417,-45.917,0],"ti":[41.417,45.917,0]},{"t":285,"s":[-42.5,-125.5,0]}],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":205,"s":[100,100,100]},{"t":285,"s":[370,370,100]}],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".black","cl":"black","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[15.3,0],[0,0],[0,15.7],[0,0],[-15.3,0],[0,0],[0,-15.7],[0,0]],"o":[[0,0],[-15.3,0],[0,0],[0,-15.7],[0,0],[15.2,0],[0,0],[0,15.5]],"v":[[178.2,150],[-178.2,150],[-206,121.5],[-206,-121.5],[-178.2,-150],[178.3,-150],[206,-121.5],[206,121.7]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11306,"st":-94,"bm":0}],"markers":[{"tm":154,"cm":"","dr":0},{"tm":360,"cm":"","dr":0}]}
\ No newline at end of file
diff --git a/quickstep/res/raw-night/taskbar_edu_splitscreen_transient.json b/quickstep/res/raw-night/taskbar_edu_splitscreen_transient.json
new file mode 100644
index 0000000..0c0e45f
--- /dev/null
+++ b/quickstep/res/raw-night/taskbar_edu_splitscreen_transient.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":94,"op":249,"w":412,"h":300,"nm":"Taskbar_Transient_Step_2","ddd":0,"assets":[{"id":"comp_0","nm":"Pre-comp_TaskBar_Transient","fr":60,"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 1","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[237.75,183,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[50,50,100],"ix":6,"l":2}},"ao":0,"ip":102,"op":255,"st":90,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"swipe up 2","parent":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":100,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":110,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":167,"s":[100]},{"t":177,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":100,"s":[-45.3,33.917,0],"to":[5.5,9,0],"ti":[-12,-36,0]},{"i":{"x":0.3,"y":0.3},"o":{"x":0.333,"y":0.333},"t":127,"s":[-12.3,87.917,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[-12.3,87.917,0],"to":[0,0,0],"ti":[-99.967,45.186,0]},{"t":187,"s":[89.5,-76.7,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.1,0.1,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":127,"s":[100,100,100]},{"i":{"x":[0.1,0.1,0.667],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.333],"y":[0,0,0]},"t":137,"s":[80,80,100]},{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.333],"y":[0,0,0]},"t":167,"s":[80,80,100]},{"t":179,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"ef":[{"ty":25,"nm":"Drop Shadow","np":8,"mn":"ADBE Drop Shadow","ix":1,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":63.75,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":8,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":30,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]},{"ty":25,"nm":"Drop Shadow 2","np":8,"mn":"ADBE Drop Shadow","ix":2,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":40,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]}],"shapes":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.5,0.5],"y":[1,1]},"o":{"x":[0.5,0.5],"y":[0,0]},"t":240,"s":[56,56]},{"i":{"x":[0.5,0.5],"y":[1,1]},"o":{"x":[0.5,0.5],"y":[0,0]},"t":262,"s":[56,98]},{"t":288,"s":[56,56]}],"ix":2},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[0,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.8,"y":0},"t":187,"s":[0,14],"to":[0,0],"ti":[0,0]},{"t":189,"s":[0,0]}],"ix":3},"r":{"a":0,"k":67,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,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":[1,1,1,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":102,"op":234,"st":77,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".grey300","cl":"grey300","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-83.044,0.454,0],"ix":2,"l":2},"a":{"a":0,"k":[121.456,227.454,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[125.547,231.545],"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":"Layer 18","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[125.547,227.455],"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":"Layer 17","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[125.547,223.364],"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":"Layer 16","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[121.453,231.545],"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":"Layer 15","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[121.453,227.455],"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":"Layer 14","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[121.453,223.364],"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":"Layer 13","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[117.364,231.545],"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":"Layer 12","np":1,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[117.364,227.455],"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":"Layer 11","np":1,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[117.364,223.364],"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":"Layer 10","np":1,"cix":2,"bm":0,"ix":9,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"green circle matte","parent":20,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84.409,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.807843148708,0.917647063732,0.839215695858,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.807843137255,0.917647058824,0.839215686275,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".green400","cl":"green400","parent":20,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":550,"s":[84.522,0.001,0],"to":[0,-3.333,0],"ti":[0,3.333,0]},{"t":580,"s":[84.522,-19.999,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[80,80,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.35686275363,0.72549021244,0.454901963472,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"green circle matte 2","parent":20,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84.409,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.807843148708,0.917647063732,0.839215695858,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.807843137255,0.917647058824,0.839215686275,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".green400","cl":"green400","parent":20,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":553,"s":[84.522,20.001,0],"to":[0,-3.333,0],"ti":[0,3.333,0]},{"t":583,"s":[84.522,0.001,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[80,80,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.35686275363,0.72549021244,0.454901963472,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".green100","cl":"green100","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84.409,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.807843148708,0.917647063732,0.839215695858,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.807843137255,0.917647058824,0.839215686275,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"blue circle matte 2","parent":20,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[55.772,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.541175991881,0.705881993911,0.972549019608,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".blue400","cl":"blue400","parent":20,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":544,"s":[55.772,0.001,0],"to":[0,-3.333,0],"ti":[0,3.333,0]},{"t":574,"s":[55.772,-19.999,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[80,80,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"blue circle matte","parent":20,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[55.772,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.541175991881,0.705881993911,0.972549019608,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".blue400","cl":"blue400","parent":20,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":547,"s":[55.772,20.001,0],"to":[0,-3.333,0],"ti":[0,3.333,0]},{"t":577,"s":[55.772,0.001,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[80,80,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".blue100","cl":"blue100","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[55.772,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.823529422283,0.890196084976,0.988235294819,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.823529411765,0.890196078431,0.988235294118,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":".yellow400","cl":"yellow400","parent":20,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":177,"s":[0]},{"t":187,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":947,"s":[27.135,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":957,"s":[27.135,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":967,"s":[27.135,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.988235294819,0.78823530674,0.203921571374,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":177,"op":11577,"st":177,"bm":0},{"ddd":0,"ind":15,"ty":4,"nm":".yellow400","cl":"yellow400","parent":20,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":167,"s":[100]},{"t":177,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[27.135,0.001,0],"to":[0.728,-9.542,0],"ti":[-45.478,19.292,0]},{"t":187,"s":[78,-76.749,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.988235294819,0.78823530674,0.203921571374,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":16,"ty":4,"nm":".grey400","cl":"grey400","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":944,"s":[27.135,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":954,"s":[27.135,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":964,"s":[27.135,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.741176470588,0.756862745098,0.776470588235,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":137,"op":252,"st":0,"bm":0},{"ddd":0,"ind":17,"ty":4,"nm":".green400","cl":"green400","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":941,"s":[-1.498,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":951,"s":[-1.498,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":961,"s":[-1.498,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.35686275363,0.72549021244,0.454901963472,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":18,"ty":4,"nm":".red400","cl":"red400","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":938,"s":[-30.134,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":948,"s":[-30.134,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":958,"s":[-30.134,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333337307,0.403921574354,0.360784322023,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":19,"ty":4,"nm":".blue400","cl":"blue400","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":935,"s":[-58.771,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":945,"s":[-58.771,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":955,"s":[-58.771,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":20,"ty":4,"nm":".grey800","cl":"grey800","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[204.5,227.001,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0.001,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":[[-8.284,0],[0,0],[0,-8.284],[0,0],[8.284,0],[0,0],[0,8.284],[0,0]],"o":[[0,0],[8.284,0],[0,0],[0,8.284],[0,0],[-8.284,0],[0,0],[0,-8.284]],"v":[[-86.5,-15],[86.5,-15],[101.5,0],[101.5,0],[86.5,15],[-86.5,15],[-101.5,0],[-101.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.235294118524,0.250980407,0.262745112181,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0}]},{"id":"comp_1","nm":"Pre-comp_Toggle","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".blue900","cl":"blue900","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":336,"s":[-12.5,0,0],"to":[3.75,0,0],"ti":[-3.75,0,0]},{"i":{"x":0.2,"y":0.2},"o":{"x":0.7,"y":0.7},"t":356,"s":[10,0,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":416,"s":[10,0,0],"to":[-3.75,0,0],"ti":[3.75,0,0]},{"t":436,"s":[-12.5,0,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":336,"s":[{"i":[[-6.627,0],[0,0],[0,-6.627],[0,0],[6.627,0],[0,0],[0,6.627],[0,0]],"o":[[0,0],[6.627,0],[0,0],[0,6.627],[0,0],[-6.627,0],[0,0],[0,-6.627]],"v":[[0,-12],[0,-12],[12,0],[12,0],[0,12],[0,12],[-12,0],[-12,0]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.8,"y":0},"t":356,"s":[{"i":[[-7.732,0],[0,0],[0,-7.732],[0,0],[7.732,0],[0,0],[0,7.732],[0,0]],"o":[[0,0],[7.732,0],[0,0],[0,7.732],[0,0],[-7.732,0],[0,0],[0,-7.732]],"v":[[0,-14],[0,-14],[14,0],[14,0],[0,14],[0,14],[-14,0],[-14,0]],"c":true}]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0},"t":416,"s":[{"i":[[-7.732,0],[0,0],[0,-7.732],[0,0],[7.732,0],[0,0],[0,7.732],[0,0]],"o":[[0,0],[7.732,0],[0,0],[0,7.732],[0,0],[-7.732,0],[0,0],[0,-7.732]],"v":[[0,-14],[0,-14],[14,0],[14,0],[0,14],[0,14],[-14,0],[-14,0]],"c":true}]},{"t":436,"s":[{"i":[[-6.627,0],[0,0],[0,-6.627],[0,0],[6.627,0],[0,0],[0,6.627],[0,0]],"o":[[0,0],[6.627,0],[0,0],[0,6.627],[0,0],[-6.627,0],[0,0],[0,-6.627]],"v":[[0,-12],[0,-12],[12,0],[12,0],[0,12],[0,12],[-12,0],[-12,0]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.090196078431,0.305882352941,0.650980392157,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".blue200","cl":"blue200","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[254.5,133.75,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[50,50,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-8.837,0],[0,0],[0,-8.837],[0,0],[8.837,0],[0,0],[0,8.837],[0,0]],"o":[[0,0],[8.837,0],[0,0],[0,8.837],[0,0],[-8.837,0],[0,0],[0,-8.837]],"v":[[-10,-16],[10,-16],[26,0],[26,0],[10,16],[-10,16],[-26,0],[-26,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.682352941176,0.796078431373,0.980392156863,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".blue100","cl":"blue100","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,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,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.784,0],[0,1.783],[1.783,0],[0,-1.783]],"o":[[1.783,0],[0,-1.783],[-1.784,0],[0,1.783]],"v":[[-43.755,-12.577],[-40.526,-15.806],[-43.755,-19.035],[-46.984,-15.806]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.823529411765,0.890196078431,0.988235294118,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0.053]],"o":[[0,0.053],[0,0]],"v":[[-44.937,-23.822],[-44.937,-23.822]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[0,0.054]],"o":[[0,0.054],[0,0]],"v":[[-49.458,-21.078],[-49.458,-21.078]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[-0.431,0.269],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0.215],[0,0.215],[0,0],[0,0],[0,0],[0,0],[0,0],[0.431,0.161],[0,0],[0,0],[0,0],[0,0],[0,0],[0.431,-0.269],[0,0],[0,0],[0,0],[0,0],[0,0],[0,-0.216],[-0.054,-0.215],[0,0],[0,0],[0,0],[0,0],[0,0],[-0.431,-0.162],[0,0]],"o":[[0,0],[0,0],[0,0],[0.431,-0.162],[0,0],[0,0],[0,0],[0,0],[0,0],[0.054,-0.215],[0,-0.269],[0,0],[0,0],[0,0],[0,0],[0,0],[-0.377,-0.269],[0,0],[0,0],[0,0],[0,0],[0,0],[-0.431,0.161],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0.215],[0,0.215],[0,0],[0,0],[0,0],[0,0],[0,0],[0.377,0.323],[0,0],[0,0]],"v":[[-45.045,-8.215],[-42.515,-8.215],[-42.138,-10.529],[-41.654,-10.744],[-40.416,-11.444],[-39.986,-11.767],[-37.779,-10.906],[-36.487,-13.112],[-38.371,-14.565],[-38.317,-15.104],[-38.263,-15.803],[-38.317,-16.503],[-38.371,-17.041],[-36.487,-18.494],[-37.779,-20.701],[-39.986,-19.84],[-40.416,-20.163],[-41.654,-20.862],[-42.138,-21.078],[-42.461,-23.392],[-44.991,-23.392],[-45.314,-21.078],[-45.798,-20.862],[-47.036,-20.163],[-47.467,-19.84],[-49.673,-20.701],[-50.965,-18.494],[-49.081,-17.041],[-49.135,-16.503],[-49.189,-15.803],[-49.135,-15.104],[-49.081,-14.565],[-50.965,-13.112],[-49.673,-10.906],[-47.467,-11.767],[-47.036,-11.444],[-45.798,-10.744],[-45.368,-10.529]],"c":true},"ix":2},"nm":"Path 3","mn":"ADBE Vector Shape - Group","hd":false},{"ind":3,"ty":"sh","ix":4,"ks":{"a":0,"k":{"i":[[0,0],[0.054,0]],"o":[[0.054,0],[0,0]],"v":[[-44.991,-7.784],[-44.991,-7.784]],"c":true},"ix":2},"nm":"Path 4","mn":"ADBE Vector Shape - Group","hd":false},{"ind":4,"ty":"sh","ix":5,"ks":{"a":0,"k":{"i":[[0.7,0],[0,0],[0.108,0.7],[0,0],[0.215,0.162],[0,0],[0.323,0.592],[0,0],[-0.484,0.376],[0,0],[0,0.161],[0,0.162],[0,0],[-0.376,0.593],[0,0],[-0.592,-0.215],[0,0],[-0.215,0.162],[0,0],[-0.7,0],[0,0],[-0.107,-0.7],[0,0],[-0.269,-0.162],[0,0],[-0.323,-0.592],[0,0],[0.484,-0.377],[0,0],[0,-0.162],[0,-0.161],[0,0],[0.323,-0.592],[0,0],[0.646,0.215],[0,0],[0.216,-0.162],[0,0]],"o":[[0,0],[-0.7,0],[0,0],[-0.269,-0.108],[0,0],[-0.646,0.215],[0,0],[-0.323,-0.592],[0,0],[0,-0.161],[0,-0.162],[0,0],[-0.538,-0.431],[0,0],[0.323,-0.592],[0,0],[0.215,-0.162],[0,0],[0.053,-0.646],[0,0],[0.699,0],[0,0],[0.269,0.108],[0,0],[0.646,-0.215],[0,0],[0.323,0.592],[0,0],[0,0.161],[0,0.161],[0,0],[0.538,0.431],[0,0],[-0.323,0.592],[0,0],[-0.215,0.161],[0,0],[0,0.538]],"v":[[-42.085,-6.385],[-45.475,-6.385],[-46.821,-7.569],[-47.09,-9.291],[-47.789,-9.722],[-49.458,-9.076],[-51.126,-9.668],[-52.795,-12.574],[-52.472,-14.296],[-51.072,-15.373],[-51.072,-15.803],[-51.072,-16.234],[-52.472,-17.31],[-52.795,-19.033],[-51.126,-21.939],[-49.512,-22.531],[-47.843,-21.831],[-47.144,-22.262],[-46.874,-24.038],[-45.529,-25.168],[-42.138,-25.168],[-40.793,-23.984],[-40.524,-22.262],[-39.77,-21.831],[-38.102,-22.477],[-36.433,-21.885],[-34.765,-18.979],[-35.088,-17.256],[-36.487,-16.18],[-36.487,-15.749],[-36.487,-15.319],[-35.088,-14.243],[-34.765,-12.52],[-36.487,-9.56],[-38.156,-8.968],[-39.824,-9.614],[-40.524,-9.183],[-40.793,-7.407]],"c":true},"ix":2},"nm":"Path 5","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.823529411765,0.890196078431,0.988235294118,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 2","np":6,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[-43.792,-15.776],"ix":2},"a":{"a":0,"k":[-43.792,-15.776],"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":"Group 6","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-8.284,0],[0,0],[0,-8.284],[0,0],[8.284,0],[0,0],[0,8.284],[0,0]],"o":[[0,0],[8.284,0],[0,0],[0,8.284],[0,0],[-8.284,0],[0,0],[0,-8.284]],"v":[[-54,-33],[60,-33],[75,-18],[75,-13],[60,2],[-54,2],[-69,-13],[-69,-18]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.61568627451,0.964705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 5","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11400,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"press 2","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":381,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":391,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":416,"s":[100]},{"t":426,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[259.25,134,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":406,"s":[50,50,100]},{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":416,"s":[40,40,100]},{"t":426,"s":[50,50,100]}],"ix":6,"l":2}},"ao":0,"ef":[{"ty":25,"nm":"Drop Shadow","np":8,"mn":"ADBE Drop Shadow","ix":1,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":63.75,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":8,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":30,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]},{"ty":25,"nm":"Drop Shadow 2","np":8,"mn":"ADBE Drop Shadow","ix":2,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":40,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]}],"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[56,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":67,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,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":[1,1,1,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.167,"y":0.186},"t":381,"s":[-37,67],"to":[15,-7],"ti":[7,20]},{"t":406,"s":[0,0],"h":1}],"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":"Touch","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":381,"op":437,"st":176,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"press","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":301,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":311,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":336,"s":[100]},{"t":346,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[248,134,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":326,"s":[50,50,100]},{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":336,"s":[40,40,100]},{"t":346,"s":[50,50,100]}],"ix":6,"l":2}},"ao":0,"ef":[{"ty":25,"nm":"Drop Shadow","np":8,"mn":"ADBE Drop Shadow","ix":1,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":63.75,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":8,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":30,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]},{"ty":25,"nm":"Drop Shadow 2","np":8,"mn":"ADBE Drop Shadow","ix":2,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":40,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]}],"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[56,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":67,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,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":[1,1,1,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.167,"y":0.186},"t":301,"s":[-37,67],"to":[15,-7],"ti":[7,20]},{"t":326,"s":[0,0],"h":1}],"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":"Touch","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":301,"op":357,"st":96,"bm":0},{"ddd":0,"ind":3,"ty":3,"nm":"Null 1","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,194,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[50,50,100],"ix":6,"l":2}},"ao":0,"ip":15,"op":73,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"swipe up","parent":3,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":15,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":25,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":60,"s":[100]},{"t":70,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":15,"s":[-48.3,37.417,0],"to":[5.5,9,0],"ti":[-12,-36,0]},{"i":{"x":0.667,"y":0.667},"o":{"x":0.333,"y":0.333},"t":40,"s":[-15.3,91.417,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.45,"y":0},"t":50,"s":[-15.3,91.417,0],"to":[0,0,0],"ti":[0,0,0]},{"t":70,"s":[-15.3,-62.583,0],"h":1}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":40,"s":[100,100,100]},{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":50,"s":[80,80,100]},{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":60,"s":[80,80,100]},{"t":70,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"ef":[{"ty":25,"nm":"Drop Shadow","np":8,"mn":"ADBE Drop Shadow","ix":1,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":63.75,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":8,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":30,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]},{"ty":25,"nm":"Drop Shadow 2","np":8,"mn":"ADBE Drop Shadow","ix":2,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":40,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]}],"shapes":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.5,0.5],"y":[1,1]},"o":{"x":[0.5,0.5],"y":[0,0]},"t":52,"s":[56,56]},{"i":{"x":[0.5,0.5],"y":[1,1]},"o":{"x":[0.5,0.5],"y":[0,0]},"t":62,"s":[56,98]},{"t":72,"s":[56,56]}],"ix":2},"p":{"a":1,"k":[{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":52,"s":[0,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":62,"s":[0,14],"to":[0,0],"ti":[0,0]},{"t":72,"s":[0,0]}],"ix":3},"r":{"a":0,"k":67,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,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":[1,1,1,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":15,"op":73,"st":-10,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"screen_matte","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[287,150,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,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[320,204.01],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":13,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":0,"nm":"Pre-comp_TaskBar_Transient","tt":1,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.1,"y":1},"o":{"x":0.7,"y":0},"t":50,"s":[204.5,244.501,0],"to":[0,-2.5,0],"ti":[0,2.5,0]},{"i":{"x":0.1,"y":0.1},"o":{"x":0.167,"y":0.167},"t":94,"s":[204.5,229.501,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":356,"s":[204.5,229.501,0],"to":[0,1.333,0],"ti":[0,-1.333,0]},{"i":{"x":0.1,"y":0.1},"o":{"x":0.167,"y":0.167},"t":386,"s":[204.5,237.501,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":436,"s":[204.5,237.501,0],"to":[0,-1.333,0],"ti":[0,1.333,0]},{"i":{"x":0.3,"y":0.3},"o":{"x":0.3,"y":0.3},"t":466,"s":[204.5,229.501,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":474,"s":[204.5,229.501,0],"to":[-42.083,-12.917,0],"ti":[42.083,12.917,0]},{"t":554,"s":[-48,152.001,0]}],"ix":2,"l":2},"a":{"a":0,"k":[206,227.625,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.7,0.7,0.167],"y":[0,0,0]},"t":50,"s":[70,70,100]},{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":94,"s":[100,100,100]},{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":474,"s":[100,100,100]},{"t":554,"s":[370,370,100]}],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".grey800","cl":"grey800","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.9,"y":0},"t":46,"s":[205.5,241.5,0],"to":[0,-1.667,0],"ti":[0,1.667,0]},{"t":76,"s":[205.5,231.5,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.381,0],[0,0],[0,-1.381],[0,0],[1.381,0],[0,0],[0,1.381],[0,0]],"o":[[0,0],[1.381,0],[0,0],[0,1.381],[0,0],[-1.381,0],[0,0],[0,-1.381]],"v":[[-42,-2.5],[42,-2.5],[44.5,0],[44.5,0],[42,2.5],[-42,2.5],[-44.5,0],[-44.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":65,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":0,"nm":"Pre-comp_Toggle","refId":"comp_1","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":256,"s":[0]},{"i":{"x":[0.316],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":286,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":474,"s":[100]},{"t":554,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":235,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".blue100","cl":"blue100","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":249,"s":[0]},{"t":269,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[287,150,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,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.3,0.3],"y":[0,0]},"t":356,"s":[320,204.01]},{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0,0]},"t":386,"s":[320,173.01]},{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.3,0.3],"y":[0,0]},"t":436,"s":[320,173.01]},{"t":466,"s":[320,204.01]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":12,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.823529411765,0.890196078431,0.988235294118,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":356,"s":[-81,0],"to":[0,-2.542],"ti":[0,2.542]},{"i":{"x":0.1,"y":0.1},"o":{"x":0.167,"y":0.167},"t":386,"s":[-81,-15.25],"to":[0,0],"ti":[0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":436,"s":[-81,-15.25],"to":[0,2.542],"ti":[0,-2.542]},{"t":466,"s":[-81,0]}],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":286,"op":11635,"st":235,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".blue100","cl":"blue100","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[132.562,150,0],"ix":2,"l":2},"a":{"a":0,"k":[-154.438,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":137,"s":[100,100,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":187,"s":[95,95,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.167],"y":[0,0,0]},"t":249,"s":[95,95,100]},{"t":269,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[13.125,-89.005],[13.125,89.005],[0.125,102.005],[-147,102.005],[-160,89.005],[-160,-89.005],[-147,-102.005],[0.125,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.8,"y":0},"t":187,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[0,-89.005],[0,89.005],[-13,102.005],[-147,102.005],[-160,89.005],[-160,-89.005],[-147,-102.005],[-13,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":249,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[0,-89.005],[0,89.005],[-13,102.005],[-147,102.005],[-160,89.005],[-160,-89.005],[-147,-102.005],[-13,-102.005]],"c":true}]},{"t":269,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[13.125,-89.005],[13.125,89.005],[0.125,102.005],[-147,102.005],[-160,89.005],[-160,-89.005],[-147,-102.005],[0.125,-102.005]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.823529411765,0.890196078431,0.988235294118,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":286,"st":0,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":".blue100","cl":"blue100","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.8],"y":[0]},"t":137,"s":[100]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.8],"y":[0]},"t":187,"s":[0]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.3],"y":[0]},"t":249,"s":[0]},{"t":259,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[279.438,150,0],"ix":2,"l":2},"a":{"a":0,"k":[-7.562,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":137,"s":[100,100,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":187,"s":[95,95,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.167],"y":[0,0,0]},"t":249,"s":[95,95,100]},{"t":269,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[-0.125,102.005],[-13.125,89.005],[-13.125,-89.005],[-0.125,-102.005],[147,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.8,"y":0},"t":187,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[13,102.005],[0,89.005],[0,-89.005],[13,-102.005],[147,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":249,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[13,102.005],[0,89.005],[0,-89.005],[13,-102.005],[147,-102.005]],"c":true}]},{"t":269,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[-0.125,102.005],[-13.125,89.005],[-13.125,-89.005],[-0.125,-102.005],[147,-102.005]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.823529411765,0.890196078431,0.988235294118,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":286,"st":0,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".yellow100","cl":"yellow100","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[279.438,150,0],"ix":2,"l":2},"a":{"a":0,"k":[-7.562,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":137,"s":[100,100,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":187,"s":[95,95,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.167],"y":[0,0,0]},"t":249,"s":[95,95,100]},{"t":269,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[-0.125,102.005],[-13.125,89.005],[-13.125,-89.005],[-0.125,-102.005],[147,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.8,"y":0},"t":187,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[13,102.005],[0,89.005],[0,-89.005],[13,-102.005],[147,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":249,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[13,102.005],[0,89.005],[0,-89.005],[13,-102.005],[147,-102.005]],"c":true}]},{"t":269,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[-0.125,102.005],[-13.125,89.005],[-13.125,-89.005],[-0.125,-102.005],[147,-102.005]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.996078431373,0.937254901961,0.764705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":286,"st":0,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".grey800","cl":"grey800","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[287,150,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,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[320,204.01],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":13,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":100,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":".black","cl":"black","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[15.3,0],[0,0],[0,15.7],[0,0],[-15.3,0],[0,0],[0,-15.7],[0,0]],"o":[[0,0],[-15.3,0],[0,0],[0,-15.7],[0,0],[15.2,0],[0,0],[0,15.5]],"v":[[178.2,150],[-178.2,150],[-206,121.5],[-206,-121.5],[-178.2,-150],[178.3,-150],[206,-121.5],[206,121.7]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11400,"st":0,"bm":0}],"markers":[{"tm":94,"cm":"","dr":0},{"tm":249,"cm":"","dr":0},{"tm":474,"cm":"","dr":0}]}
\ No newline at end of file
diff --git a/quickstep/res/raw-night/taskbar_edu_stashing_transient.json b/quickstep/res/raw-night/taskbar_edu_stashing_transient.json
new file mode 100644
index 0000000..7714b41
--- /dev/null
+++ b/quickstep/res/raw-night/taskbar_edu_stashing_transient.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":94,"w":412,"h":300,"nm":"Taskbar_Transient_Step_1","ddd":0,"assets":[{"id":"comp_0","nm":"Pre-comp_TaskBar_Transient","fr":60,"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 1","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[237.75,183,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[50,50,100],"ix":6,"l":2}},"ao":0,"ip":102,"op":255,"st":90,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"swipe up 2","parent":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":100,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":110,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":167,"s":[100]},{"t":177,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":100,"s":[-45.3,33.917,0],"to":[5.5,9,0],"ti":[-12,-36,0]},{"i":{"x":0.3,"y":0.3},"o":{"x":0.333,"y":0.333},"t":127,"s":[-12.3,87.917,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[-12.3,87.917,0],"to":[0,0,0],"ti":[-99.967,45.186,0]},{"t":187,"s":[89.5,-76.7,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.1,0.1,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":127,"s":[100,100,100]},{"i":{"x":[0.1,0.1,0.667],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.333],"y":[0,0,0]},"t":137,"s":[80,80,100]},{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.333],"y":[0,0,0]},"t":167,"s":[80,80,100]},{"t":179,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"ef":[{"ty":25,"nm":"Drop Shadow","np":8,"mn":"ADBE Drop Shadow","ix":1,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":63.75,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":8,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":30,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]},{"ty":25,"nm":"Drop Shadow 2","np":8,"mn":"ADBE Drop Shadow","ix":2,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":40,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]}],"shapes":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.5,0.5],"y":[1,1]},"o":{"x":[0.5,0.5],"y":[0,0]},"t":240,"s":[56,56]},{"i":{"x":[0.5,0.5],"y":[1,1]},"o":{"x":[0.5,0.5],"y":[0,0]},"t":262,"s":[56,98]},{"t":288,"s":[56,56]}],"ix":2},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[0,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.8,"y":0},"t":187,"s":[0,14],"to":[0,0],"ti":[0,0]},{"t":189,"s":[0,0]}],"ix":3},"r":{"a":0,"k":67,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,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":[1,1,1,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":102,"op":234,"st":77,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".grey300","cl":"grey300","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-83.044,0.454,0],"ix":2,"l":2},"a":{"a":0,"k":[121.456,227.454,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[125.547,231.545],"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":"Layer 18","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[125.547,227.455],"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":"Layer 17","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[125.547,223.364],"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":"Layer 16","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[121.453,231.545],"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":"Layer 15","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[121.453,227.455],"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":"Layer 14","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[121.453,223.364],"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":"Layer 13","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[117.364,231.545],"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":"Layer 12","np":1,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[117.364,227.455],"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":"Layer 11","np":1,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[117.364,223.364],"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":"Layer 10","np":1,"cix":2,"bm":0,"ix":9,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"green circle matte","parent":20,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84.409,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.807843148708,0.917647063732,0.839215695858,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.807843137255,0.917647058824,0.839215686275,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".green400","cl":"green400","parent":20,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":550,"s":[84.522,0.001,0],"to":[0,-3.333,0],"ti":[0,3.333,0]},{"t":580,"s":[84.522,-19.999,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[80,80,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.35686275363,0.72549021244,0.454901963472,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"green circle matte 2","parent":20,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84.409,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.807843148708,0.917647063732,0.839215695858,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.807843137255,0.917647058824,0.839215686275,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".green400","cl":"green400","parent":20,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":553,"s":[84.522,20.001,0],"to":[0,-3.333,0],"ti":[0,3.333,0]},{"t":583,"s":[84.522,0.001,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[80,80,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.35686275363,0.72549021244,0.454901963472,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".green100","cl":"green100","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84.409,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.807843148708,0.917647063732,0.839215695858,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.807843137255,0.917647058824,0.839215686275,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"blue circle matte 2","parent":20,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[55.772,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.541175991881,0.705881993911,0.972549019608,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".blue400","cl":"blue400","parent":20,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":544,"s":[55.772,0.001,0],"to":[0,-3.333,0],"ti":[0,3.333,0]},{"t":574,"s":[55.772,-19.999,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[80,80,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"blue circle matte","parent":20,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[55.772,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.541175991881,0.705881993911,0.972549019608,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".blue400","cl":"blue400","parent":20,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":547,"s":[55.772,20.001,0],"to":[0,-3.333,0],"ti":[0,3.333,0]},{"t":577,"s":[55.772,0.001,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[80,80,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".blue100","cl":"blue100","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[55.772,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.823529422283,0.890196084976,0.988235294819,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.823529411765,0.890196078431,0.988235294118,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":".yellow400","cl":"yellow400","parent":20,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":177,"s":[0]},{"t":187,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":947,"s":[27.135,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":957,"s":[27.135,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":967,"s":[27.135,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.988235294819,0.78823530674,0.203921571374,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":177,"op":11577,"st":177,"bm":0},{"ddd":0,"ind":15,"ty":4,"nm":".yellow400","cl":"yellow400","parent":20,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":167,"s":[100]},{"t":177,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[27.135,0.001,0],"to":[0.728,-9.542,0],"ti":[-45.478,19.292,0]},{"t":187,"s":[78,-76.749,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.988235294819,0.78823530674,0.203921571374,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":16,"ty":4,"nm":".grey400","cl":"grey400","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":944,"s":[27.135,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":954,"s":[27.135,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":964,"s":[27.135,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.741176470588,0.756862745098,0.776470588235,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":137,"op":252,"st":0,"bm":0},{"ddd":0,"ind":17,"ty":4,"nm":".green400","cl":"green400","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":941,"s":[-1.498,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":951,"s":[-1.498,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":961,"s":[-1.498,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.35686275363,0.72549021244,0.454901963472,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":18,"ty":4,"nm":".red400","cl":"red400","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":938,"s":[-30.134,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":948,"s":[-30.134,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":958,"s":[-30.134,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333337307,0.403921574354,0.360784322023,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":19,"ty":4,"nm":".blue400","cl":"blue400","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":935,"s":[-58.771,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":945,"s":[-58.771,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":955,"s":[-58.771,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":20,"ty":4,"nm":".grey800","cl":"grey800","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[204.5,227.001,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0.001,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":[[-8.284,0],[0,0],[0,-8.284],[0,0],[8.284,0],[0,0],[0,8.284],[0,0]],"o":[[0,0],[8.284,0],[0,0],[0,8.284],[0,0],[-8.284,0],[0,0],[0,-8.284]],"v":[[-86.5,-15],[86.5,-15],[101.5,0],[101.5,0],[86.5,15],[-86.5,15],[-101.5,0],[-101.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.235294118524,0.250980407,0.262745112181,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0}]},{"id":"comp_1","nm":"Pre-comp_Toggle","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".blue900","cl":"blue900","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":336,"s":[-12.5,0,0],"to":[3.75,0,0],"ti":[-3.75,0,0]},{"i":{"x":0.2,"y":0.2},"o":{"x":0.7,"y":0.7},"t":356,"s":[10,0,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":416,"s":[10,0,0],"to":[-3.75,0,0],"ti":[3.75,0,0]},{"t":436,"s":[-12.5,0,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":336,"s":[{"i":[[-6.627,0],[0,0],[0,-6.627],[0,0],[6.627,0],[0,0],[0,6.627],[0,0]],"o":[[0,0],[6.627,0],[0,0],[0,6.627],[0,0],[-6.627,0],[0,0],[0,-6.627]],"v":[[0,-12],[0,-12],[12,0],[12,0],[0,12],[0,12],[-12,0],[-12,0]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.8,"y":0},"t":356,"s":[{"i":[[-7.732,0],[0,0],[0,-7.732],[0,0],[7.732,0],[0,0],[0,7.732],[0,0]],"o":[[0,0],[7.732,0],[0,0],[0,7.732],[0,0],[-7.732,0],[0,0],[0,-7.732]],"v":[[0,-14],[0,-14],[14,0],[14,0],[0,14],[0,14],[-14,0],[-14,0]],"c":true}]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0},"t":416,"s":[{"i":[[-7.732,0],[0,0],[0,-7.732],[0,0],[7.732,0],[0,0],[0,7.732],[0,0]],"o":[[0,0],[7.732,0],[0,0],[0,7.732],[0,0],[-7.732,0],[0,0],[0,-7.732]],"v":[[0,-14],[0,-14],[14,0],[14,0],[0,14],[0,14],[-14,0],[-14,0]],"c":true}]},{"t":436,"s":[{"i":[[-6.627,0],[0,0],[0,-6.627],[0,0],[6.627,0],[0,0],[0,6.627],[0,0]],"o":[[0,0],[6.627,0],[0,0],[0,6.627],[0,0],[-6.627,0],[0,0],[0,-6.627]],"v":[[0,-12],[0,-12],[12,0],[12,0],[0,12],[0,12],[-12,0],[-12,0]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.090196078431,0.305882352941,0.650980392157,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".blue200","cl":"blue200","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[254.5,133.75,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[50,50,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-8.837,0],[0,0],[0,-8.837],[0,0],[8.837,0],[0,0],[0,8.837],[0,0]],"o":[[0,0],[8.837,0],[0,0],[0,8.837],[0,0],[-8.837,0],[0,0],[0,-8.837]],"v":[[-10,-16],[10,-16],[26,0],[26,0],[10,16],[-10,16],[-26,0],[-26,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.682352941176,0.796078431373,0.980392156863,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".blue100","cl":"blue100","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,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,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.784,0],[0,1.783],[1.783,0],[0,-1.783]],"o":[[1.783,0],[0,-1.783],[-1.784,0],[0,1.783]],"v":[[-43.755,-12.577],[-40.526,-15.806],[-43.755,-19.035],[-46.984,-15.806]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.823529411765,0.890196078431,0.988235294118,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0.053]],"o":[[0,0.053],[0,0]],"v":[[-44.937,-23.822],[-44.937,-23.822]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[0,0.054]],"o":[[0,0.054],[0,0]],"v":[[-49.458,-21.078],[-49.458,-21.078]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[-0.431,0.269],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0.215],[0,0.215],[0,0],[0,0],[0,0],[0,0],[0,0],[0.431,0.161],[0,0],[0,0],[0,0],[0,0],[0,0],[0.431,-0.269],[0,0],[0,0],[0,0],[0,0],[0,0],[0,-0.216],[-0.054,-0.215],[0,0],[0,0],[0,0],[0,0],[0,0],[-0.431,-0.162],[0,0]],"o":[[0,0],[0,0],[0,0],[0.431,-0.162],[0,0],[0,0],[0,0],[0,0],[0,0],[0.054,-0.215],[0,-0.269],[0,0],[0,0],[0,0],[0,0],[0,0],[-0.377,-0.269],[0,0],[0,0],[0,0],[0,0],[0,0],[-0.431,0.161],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0.215],[0,0.215],[0,0],[0,0],[0,0],[0,0],[0,0],[0.377,0.323],[0,0],[0,0]],"v":[[-45.045,-8.215],[-42.515,-8.215],[-42.138,-10.529],[-41.654,-10.744],[-40.416,-11.444],[-39.986,-11.767],[-37.779,-10.906],[-36.487,-13.112],[-38.371,-14.565],[-38.317,-15.104],[-38.263,-15.803],[-38.317,-16.503],[-38.371,-17.041],[-36.487,-18.494],[-37.779,-20.701],[-39.986,-19.84],[-40.416,-20.163],[-41.654,-20.862],[-42.138,-21.078],[-42.461,-23.392],[-44.991,-23.392],[-45.314,-21.078],[-45.798,-20.862],[-47.036,-20.163],[-47.467,-19.84],[-49.673,-20.701],[-50.965,-18.494],[-49.081,-17.041],[-49.135,-16.503],[-49.189,-15.803],[-49.135,-15.104],[-49.081,-14.565],[-50.965,-13.112],[-49.673,-10.906],[-47.467,-11.767],[-47.036,-11.444],[-45.798,-10.744],[-45.368,-10.529]],"c":true},"ix":2},"nm":"Path 3","mn":"ADBE Vector Shape - Group","hd":false},{"ind":3,"ty":"sh","ix":4,"ks":{"a":0,"k":{"i":[[0,0],[0.054,0]],"o":[[0.054,0],[0,0]],"v":[[-44.991,-7.784],[-44.991,-7.784]],"c":true},"ix":2},"nm":"Path 4","mn":"ADBE Vector Shape - Group","hd":false},{"ind":4,"ty":"sh","ix":5,"ks":{"a":0,"k":{"i":[[0.7,0],[0,0],[0.108,0.7],[0,0],[0.215,0.162],[0,0],[0.323,0.592],[0,0],[-0.484,0.376],[0,0],[0,0.161],[0,0.162],[0,0],[-0.376,0.593],[0,0],[-0.592,-0.215],[0,0],[-0.215,0.162],[0,0],[-0.7,0],[0,0],[-0.107,-0.7],[0,0],[-0.269,-0.162],[0,0],[-0.323,-0.592],[0,0],[0.484,-0.377],[0,0],[0,-0.162],[0,-0.161],[0,0],[0.323,-0.592],[0,0],[0.646,0.215],[0,0],[0.216,-0.162],[0,0]],"o":[[0,0],[-0.7,0],[0,0],[-0.269,-0.108],[0,0],[-0.646,0.215],[0,0],[-0.323,-0.592],[0,0],[0,-0.161],[0,-0.162],[0,0],[-0.538,-0.431],[0,0],[0.323,-0.592],[0,0],[0.215,-0.162],[0,0],[0.053,-0.646],[0,0],[0.699,0],[0,0],[0.269,0.108],[0,0],[0.646,-0.215],[0,0],[0.323,0.592],[0,0],[0,0.161],[0,0.161],[0,0],[0.538,0.431],[0,0],[-0.323,0.592],[0,0],[-0.215,0.161],[0,0],[0,0.538]],"v":[[-42.085,-6.385],[-45.475,-6.385],[-46.821,-7.569],[-47.09,-9.291],[-47.789,-9.722],[-49.458,-9.076],[-51.126,-9.668],[-52.795,-12.574],[-52.472,-14.296],[-51.072,-15.373],[-51.072,-15.803],[-51.072,-16.234],[-52.472,-17.31],[-52.795,-19.033],[-51.126,-21.939],[-49.512,-22.531],[-47.843,-21.831],[-47.144,-22.262],[-46.874,-24.038],[-45.529,-25.168],[-42.138,-25.168],[-40.793,-23.984],[-40.524,-22.262],[-39.77,-21.831],[-38.102,-22.477],[-36.433,-21.885],[-34.765,-18.979],[-35.088,-17.256],[-36.487,-16.18],[-36.487,-15.749],[-36.487,-15.319],[-35.088,-14.243],[-34.765,-12.52],[-36.487,-9.56],[-38.156,-8.968],[-39.824,-9.614],[-40.524,-9.183],[-40.793,-7.407]],"c":true},"ix":2},"nm":"Path 5","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.823529411765,0.890196078431,0.988235294118,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 2","np":6,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[-43.792,-15.776],"ix":2},"a":{"a":0,"k":[-43.792,-15.776],"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":"Group 6","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-8.284,0],[0,0],[0,-8.284],[0,0],[8.284,0],[0,0],[0,8.284],[0,0]],"o":[[0,0],[8.284,0],[0,0],[0,8.284],[0,0],[-8.284,0],[0,0],[0,-8.284]],"v":[[-54,-33],[60,-33],[75,-18],[75,-13],[60,2],[-54,2],[-69,-13],[-69,-18]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.61568627451,0.964705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 5","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11400,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"press 2","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":381,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":391,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":416,"s":[100]},{"t":426,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[259.25,134,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":406,"s":[50,50,100]},{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":416,"s":[40,40,100]},{"t":426,"s":[50,50,100]}],"ix":6,"l":2}},"ao":0,"ef":[{"ty":25,"nm":"Drop Shadow","np":8,"mn":"ADBE Drop Shadow","ix":1,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":63.75,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":8,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":30,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]},{"ty":25,"nm":"Drop Shadow 2","np":8,"mn":"ADBE Drop Shadow","ix":2,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":40,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]}],"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[56,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":67,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,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":[1,1,1,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.167,"y":0.186},"t":381,"s":[-37,67],"to":[15,-7],"ti":[7,20]},{"t":406,"s":[0,0],"h":1}],"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":"Touch","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":381,"op":437,"st":176,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"press","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":301,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":311,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":336,"s":[100]},{"t":346,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[248,134,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":326,"s":[50,50,100]},{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":336,"s":[40,40,100]},{"t":346,"s":[50,50,100]}],"ix":6,"l":2}},"ao":0,"ef":[{"ty":25,"nm":"Drop Shadow","np":8,"mn":"ADBE Drop Shadow","ix":1,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":63.75,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":8,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":30,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]},{"ty":25,"nm":"Drop Shadow 2","np":8,"mn":"ADBE Drop Shadow","ix":2,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":40,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]}],"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[56,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":67,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,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":[1,1,1,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.167,"y":0.186},"t":301,"s":[-37,67],"to":[15,-7],"ti":[7,20]},{"t":326,"s":[0,0],"h":1}],"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":"Touch","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":301,"op":357,"st":96,"bm":0},{"ddd":0,"ind":3,"ty":3,"nm":"Null 1","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,194,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[50,50,100],"ix":6,"l":2}},"ao":0,"ip":15,"op":73,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"swipe up","parent":3,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":15,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":25,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":60,"s":[100]},{"t":70,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":15,"s":[-48.3,37.417,0],"to":[5.5,9,0],"ti":[-12,-36,0]},{"i":{"x":0.667,"y":0.667},"o":{"x":0.333,"y":0.333},"t":40,"s":[-15.3,91.417,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.45,"y":0},"t":50,"s":[-15.3,91.417,0],"to":[0,0,0],"ti":[0,0,0]},{"t":70,"s":[-15.3,-62.583,0],"h":1}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":40,"s":[100,100,100]},{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":50,"s":[80,80,100]},{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":60,"s":[80,80,100]},{"t":70,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"ef":[{"ty":25,"nm":"Drop Shadow","np":8,"mn":"ADBE Drop Shadow","ix":1,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":63.75,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":8,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":30,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]},{"ty":25,"nm":"Drop Shadow 2","np":8,"mn":"ADBE Drop Shadow","ix":2,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":40,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]}],"shapes":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.5,0.5],"y":[1,1]},"o":{"x":[0.5,0.5],"y":[0,0]},"t":52,"s":[56,56]},{"i":{"x":[0.5,0.5],"y":[1,1]},"o":{"x":[0.5,0.5],"y":[0,0]},"t":62,"s":[56,98]},{"t":72,"s":[56,56]}],"ix":2},"p":{"a":1,"k":[{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":52,"s":[0,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":62,"s":[0,14],"to":[0,0],"ti":[0,0]},{"t":72,"s":[0,0]}],"ix":3},"r":{"a":0,"k":67,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,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":[1,1,1,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":15,"op":73,"st":-10,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"screen_matte","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[287,150,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,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[320,204.01],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":13,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":0,"nm":"Pre-comp_TaskBar_Transient","tt":1,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.1,"y":1},"o":{"x":0.7,"y":0},"t":50,"s":[204.5,244.501,0],"to":[0,-2.5,0],"ti":[0,2.5,0]},{"i":{"x":0.1,"y":0.1},"o":{"x":0.167,"y":0.167},"t":94,"s":[204.5,229.501,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":356,"s":[204.5,229.501,0],"to":[0,1.333,0],"ti":[0,-1.333,0]},{"i":{"x":0.1,"y":0.1},"o":{"x":0.167,"y":0.167},"t":386,"s":[204.5,237.501,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":436,"s":[204.5,237.501,0],"to":[0,-1.333,0],"ti":[0,1.333,0]},{"i":{"x":0.3,"y":0.3},"o":{"x":0.3,"y":0.3},"t":466,"s":[204.5,229.501,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":474,"s":[204.5,229.501,0],"to":[-42.083,-12.917,0],"ti":[42.083,12.917,0]},{"t":554,"s":[-48,152.001,0]}],"ix":2,"l":2},"a":{"a":0,"k":[206,227.625,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.7,0.7,0.167],"y":[0,0,0]},"t":50,"s":[70,70,100]},{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":94,"s":[100,100,100]},{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":474,"s":[100,100,100]},{"t":554,"s":[370,370,100]}],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".grey800","cl":"grey800","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.9,"y":0},"t":46,"s":[205.5,241.5,0],"to":[0,-1.667,0],"ti":[0,1.667,0]},{"t":76,"s":[205.5,231.5,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.381,0],[0,0],[0,-1.381],[0,0],[1.381,0],[0,0],[0,1.381],[0,0]],"o":[[0,0],[1.381,0],[0,0],[0,1.381],[0,0],[-1.381,0],[0,0],[0,-1.381]],"v":[[-42,-2.5],[42,-2.5],[44.5,0],[44.5,0],[42,2.5],[-42,2.5],[-44.5,0],[-44.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":65,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":0,"nm":"Pre-comp_Toggle","refId":"comp_1","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":256,"s":[0]},{"i":{"x":[0.316],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":286,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":474,"s":[100]},{"t":554,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":235,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".blue100","cl":"blue100","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":249,"s":[0]},{"t":269,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[287,150,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,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.3,0.3],"y":[0,0]},"t":356,"s":[320,204.01]},{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0,0]},"t":386,"s":[320,173.01]},{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.3,0.3],"y":[0,0]},"t":436,"s":[320,173.01]},{"t":466,"s":[320,204.01]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":12,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.823529411765,0.890196078431,0.988235294118,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":356,"s":[-81,0],"to":[0,-2.542],"ti":[0,2.542]},{"i":{"x":0.1,"y":0.1},"o":{"x":0.167,"y":0.167},"t":386,"s":[-81,-15.25],"to":[0,0],"ti":[0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":436,"s":[-81,-15.25],"to":[0,2.542],"ti":[0,-2.542]},{"t":466,"s":[-81,0]}],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":286,"op":11635,"st":235,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".blue100","cl":"blue100","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[132.562,150,0],"ix":2,"l":2},"a":{"a":0,"k":[-154.438,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":137,"s":[100,100,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":187,"s":[95,95,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.167],"y":[0,0,0]},"t":249,"s":[95,95,100]},{"t":269,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[13.125,-89.005],[13.125,89.005],[0.125,102.005],[-147,102.005],[-160,89.005],[-160,-89.005],[-147,-102.005],[0.125,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.8,"y":0},"t":187,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[0,-89.005],[0,89.005],[-13,102.005],[-147,102.005],[-160,89.005],[-160,-89.005],[-147,-102.005],[-13,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":249,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[0,-89.005],[0,89.005],[-13,102.005],[-147,102.005],[-160,89.005],[-160,-89.005],[-147,-102.005],[-13,-102.005]],"c":true}]},{"t":269,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[13.125,-89.005],[13.125,89.005],[0.125,102.005],[-147,102.005],[-160,89.005],[-160,-89.005],[-147,-102.005],[0.125,-102.005]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.823529411765,0.890196078431,0.988235294118,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":286,"st":0,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":".blue100","cl":"blue100","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.8],"y":[0]},"t":137,"s":[100]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.8],"y":[0]},"t":187,"s":[0]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.3],"y":[0]},"t":249,"s":[0]},{"t":259,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[279.438,150,0],"ix":2,"l":2},"a":{"a":0,"k":[-7.562,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":137,"s":[100,100,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":187,"s":[95,95,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.167],"y":[0,0,0]},"t":249,"s":[95,95,100]},{"t":269,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[-0.125,102.005],[-13.125,89.005],[-13.125,-89.005],[-0.125,-102.005],[147,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.8,"y":0},"t":187,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[13,102.005],[0,89.005],[0,-89.005],[13,-102.005],[147,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":249,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[13,102.005],[0,89.005],[0,-89.005],[13,-102.005],[147,-102.005]],"c":true}]},{"t":269,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[-0.125,102.005],[-13.125,89.005],[-13.125,-89.005],[-0.125,-102.005],[147,-102.005]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.823529411765,0.890196078431,0.988235294118,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":286,"st":0,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".yellow100","cl":"yellow100","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[279.438,150,0],"ix":2,"l":2},"a":{"a":0,"k":[-7.562,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":137,"s":[100,100,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":187,"s":[95,95,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.167],"y":[0,0,0]},"t":249,"s":[95,95,100]},{"t":269,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[-0.125,102.005],[-13.125,89.005],[-13.125,-89.005],[-0.125,-102.005],[147,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.8,"y":0},"t":187,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[13,102.005],[0,89.005],[0,-89.005],[13,-102.005],[147,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":249,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[13,102.005],[0,89.005],[0,-89.005],[13,-102.005],[147,-102.005]],"c":true}]},{"t":269,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[-0.125,102.005],[-13.125,89.005],[-13.125,-89.005],[-0.125,-102.005],[147,-102.005]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.996078431373,0.937254901961,0.764705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":286,"st":0,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".grey800","cl":"grey800","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[287,150,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,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[320,204.01],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":13,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":100,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":".black","cl":"black","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[15.3,0],[0,0],[0,15.7],[0,0],[-15.3,0],[0,0],[0,-15.7],[0,0]],"o":[[0,0],[-15.3,0],[0,0],[0,-15.7],[0,0],[15.2,0],[0,0],[0,15.5]],"v":[[178.2,150],[-178.2,150],[-206,121.5],[-206,-121.5],[-178.2,-150],[178.3,-150],[206,-121.5],[206,121.7]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11400,"st":0,"bm":0}],"markers":[{"tm":94,"cm":"","dr":0},{"tm":249,"cm":"","dr":0},{"tm":474,"cm":"","dr":0}]}
\ No newline at end of file
diff --git a/quickstep/res/raw-night/taskbar_edu_suggestions_persistent.json b/quickstep/res/raw-night/taskbar_edu_suggestions_persistent.json
new file mode 100644
index 0000000..eec2c24
--- /dev/null
+++ b/quickstep/res/raw-night/taskbar_edu_suggestions_persistent.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":186,"op":360,"w":412,"h":300,"nm":"Taskbar_Persistent_DT_Step_2","ddd":0,"assets":[{"id":"comp_0","nm":"Pre-comp_Taskbar_Persistent_Layers","fr":60,"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 2","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,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":11306,"st":-94,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"screen_matte","parent":1,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[81,0,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,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[320,204.01],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":13,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11306,"st":-94,"bm":0},{"ddd":0,"ind":3,"ty":0,"nm":"Pre-comp_TaskBar_Persistent","parent":1,"tt":1,"refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-1.5,87.892,0],"ix":2,"l":2},"a":{"a":0,"k":[206,227.625,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":11306,"st":-94,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"screen 3","parent":1,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[81,0,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,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[320,204.01],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":13,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":175,"op":11306,"st":-94,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".blue100","cl":"blue100","parent":1,"tt":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":155,"s":[0]},{"t":175,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[81,0.5,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,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[320,174.01],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":13,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.823529411765,0.890196078431,0.988235294118,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,-15.25],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":175,"op":11541,"st":141,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".blue100","cl":"blue100","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-73.438,0,0],"ix":2,"l":2},"a":{"a":0,"k":[-154.438,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":43,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":93,"s":[95,95,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":155,"s":[95,95,100]},{"t":175,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":43,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[13.125,-89.005],[13.125,59.005],[0.125,72.005],[-147,72.005],[-160,59.005],[-160,-89.005],[-147,-102.005],[0.125,-102.005]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.8,"y":0},"t":93,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[0,-89.005],[0,62.689],[-13,75.689],[-147,75.689],[-160,62.689],[-160,-89.005],[-147,-102.005],[-13,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.167,"y":0},"t":155,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[0,-89.005],[0,62.689],[-13,75.689],[-147,75.689],[-160,62.689],[-160,-89.005],[-147,-102.005],[-13,-102.005]],"c":true}]},{"t":175,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[13.125,-89.005],[13.125,59.005],[0.125,72.005],[-147,72.005],[-160,59.005],[-160,-89.005],[-147,-102.005],[0.125,-102.005]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.823529411765,0.890196078431,0.988235294118,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":175,"st":-94,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".blue100","cl":"blue100","parent":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.8],"y":[0]},"t":43,"s":[100]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.8],"y":[0]},"t":93,"s":[0]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":155,"s":[0]},{"t":175,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[73.438,0,0],"ix":2,"l":2},"a":{"a":0,"k":[-7.562,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":43,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":93,"s":[95,95,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":155,"s":[95,95,100]},{"t":175,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":43,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,59.005],[147,72.005],[-0.125,72.005],[-13.125,59.005],[-13.125,-89.005],[-0.125,-102.005],[147,-102.005]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.8,"y":0},"t":93,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,62.689],[147,75.689],[13,75.689],[0,62.689],[0,-89.005],[13,-102.005],[147,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.167,"y":0},"t":155,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,62.689],[147,75.689],[13,75.689],[0,62.689],[0,-89.005],[13,-102.005],[147,-102.005]],"c":true}]},{"t":175,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,59.005],[147,72.005],[-0.125,72.005],[-13.125,59.005],[-13.125,-89.005],[-0.125,-102.005],[147,-102.005]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.823529411765,0.890196078431,0.988235294118,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":175,"st":-94,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".yellow100","cl":"yellow100","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[73.438,0,0],"ix":2,"l":2},"a":{"a":0,"k":[-7.562,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":43,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":93,"s":[95,95,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":155,"s":[95,95,100]},{"t":175,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":43,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,59.005],[147,72.005],[-0.125,72.005],[-13.125,59.005],[-13.125,-89.005],[-0.125,-102.005],[147,-102.005]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.8,"y":0},"t":93,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,62.689],[147,75.689],[13,75.689],[0,62.689],[0,-89.005],[13,-102.005],[147,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.167,"y":0},"t":155,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,62.689],[147,75.689],[13,75.689],[0,62.689],[0,-89.005],[13,-102.005],[147,-102.005]],"c":true}]},{"t":175,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,59.005],[147,72.005],[-0.125,72.005],[-13.125,59.005],[-13.125,-89.005],[-0.125,-102.005],[147,-102.005]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.996078431373,0.937254901961,0.764705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":175,"st":-94,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".grey800","cl":"grey800","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[81,0,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,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[320,204.01],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":13,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11306,"st":-94,"bm":0}]},{"id":"comp_1","nm":"Pre-comp_TaskBar_Persistent","fr":60,"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 1","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[237.75,183,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[50,50,100],"ix":6,"l":2}},"ao":0,"ip":102,"op":255,"st":90,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"swipe up 2","parent":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":100,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":110,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":167,"s":[100]},{"t":177,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":100,"s":[-45.3,33.917,0],"to":[5.5,9,0],"ti":[-12,-36,0]},{"i":{"x":0.3,"y":0.3},"o":{"x":0.333,"y":0.333},"t":127,"s":[-12.3,87.917,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[-12.3,87.917,0],"to":[0,0,0],"ti":[-99.967,45.186,0]},{"t":187,"s":[89.5,-76.7,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.1,0.1,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":127,"s":[100,100,100]},{"i":{"x":[0.1,0.1,0.667],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.333],"y":[0,0,0]},"t":137,"s":[80,80,100]},{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.333],"y":[0,0,0]},"t":167,"s":[80,80,100]},{"t":179,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"ef":[{"ty":25,"nm":"Drop Shadow","np":8,"mn":"ADBE Drop Shadow","ix":1,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":63.75,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":8,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":30,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]},{"ty":25,"nm":"Drop Shadow 2","np":8,"mn":"ADBE Drop Shadow","ix":2,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":40,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]}],"shapes":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.5,0.5],"y":[1,1]},"o":{"x":[0.5,0.5],"y":[0,0]},"t":240,"s":[56,56]},{"i":{"x":[0.5,0.5],"y":[1,1]},"o":{"x":[0.5,0.5],"y":[0,0]},"t":262,"s":[56,98]},{"t":288,"s":[56,56]}],"ix":2},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[0,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.8,"y":0},"t":187,"s":[0,14],"to":[0,0],"ti":[0,0]},{"t":189,"s":[0,0]}],"ix":3},"r":{"a":0,"k":67,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,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":[1,1,1,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":102,"op":234,"st":77,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".grey300","cl":"grey300","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-83.044,0.454,0],"ix":2,"l":2},"a":{"a":0,"k":[121.456,227.454,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[125.547,231.545],"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":"Layer 18","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[125.547,227.455],"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":"Layer 17","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[125.547,223.364],"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":"Layer 16","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[121.453,231.545],"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":"Layer 15","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[121.453,227.455],"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":"Layer 14","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[121.453,223.364],"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":"Layer 13","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[117.364,231.545],"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":"Layer 12","np":1,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[117.364,227.455],"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":"Layer 11","np":1,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[117.364,223.364],"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":"Layer 10","np":1,"cix":2,"bm":0,"ix":9,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"green circle matte","parent":20,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84.409,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.807843148708,0.917647063732,0.839215695858,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.807843137255,0.917647058824,0.839215686275,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".green400","cl":"green400","parent":20,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":362,"s":[84.522,0.001,0],"to":[0,-3.333,0],"ti":[0,3.333,0]},{"t":392,"s":[84.522,-19.999,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[80,80,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.35686275363,0.72549021244,0.454901963472,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"green circle matte 2","parent":20,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84.409,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.807843148708,0.917647063732,0.839215695858,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.807843137255,0.917647058824,0.839215686275,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".green400","cl":"green400","parent":20,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":365,"s":[84.522,20.001,0],"to":[0,-3.333,0],"ti":[0,3.333,0]},{"t":395,"s":[84.522,0.001,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[80,80,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.35686275363,0.72549021244,0.454901963472,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".green100","cl":"green100","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84.409,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.807843148708,0.917647063732,0.839215695858,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.807843137255,0.917647058824,0.839215686275,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"blue circle matte 2","parent":20,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[55.772,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.541175991881,0.705881993911,0.972549019608,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".blue400","cl":"blue400","parent":20,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":356,"s":[55.772,0.001,0],"to":[0,-3.333,0],"ti":[0,3.333,0]},{"t":386,"s":[55.772,-19.999,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[80,80,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"blue circle matte","parent":20,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[55.772,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.541175991881,0.705881993911,0.972549019608,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".blue400","cl":"blue400","parent":20,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":359,"s":[55.772,20.001,0],"to":[0,-3.333,0],"ti":[0,3.333,0]},{"t":389,"s":[55.772,0.001,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[80,80,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".blue100","cl":"blue100","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[55.772,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.823529422283,0.890196084976,0.988235294819,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.823529411765,0.890196078431,0.988235294118,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":".yellow400","cl":"yellow400","parent":20,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":177,"s":[0]},{"t":187,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":947,"s":[27.135,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":957,"s":[27.135,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":967,"s":[27.135,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.988235294819,0.78823530674,0.203921571374,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":177,"op":11577,"st":177,"bm":0},{"ddd":0,"ind":15,"ty":4,"nm":".yelllow400","cl":"yelllow400","parent":20,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":167,"s":[100]},{"t":177,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[27.135,0.001,0],"to":[0.728,-9.542,0],"ti":[-45.478,19.292,0]},{"t":187,"s":[78,-76.749,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.988235294118,0.788235294118,0.203921568627,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":16,"ty":4,"nm":".grey400","cl":"grey400","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":944,"s":[27.135,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":954,"s":[27.135,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":964,"s":[27.135,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.741176470588,0.756862745098,0.776470588235,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":137,"op":252,"st":0,"bm":0},{"ddd":0,"ind":17,"ty":4,"nm":".green400","cl":"green400","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":941,"s":[-1.498,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":951,"s":[-1.498,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":961,"s":[-1.498,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.35686275363,0.72549021244,0.454901963472,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":18,"ty":4,"nm":".red400","cl":"red400","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":938,"s":[-30.134,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":948,"s":[-30.134,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":958,"s":[-30.134,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333337307,0.403921574354,0.360784322023,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":19,"ty":4,"nm":".blue400","cl":"blue400","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":935,"s":[-58.771,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":945,"s":[-58.771,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":955,"s":[-58.771,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.61568627451,0.964705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":20,"ty":4,"nm":".grey800","cl":"grey800","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[204.5,227.001,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0.001,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":[[-8.284,0],[0,0],[0,-8.284],[0,0],[8.284,0],[0,0],[0,8.284],[0,0]],"o":[[0,0],[8.284,0],[0,0],[0,8.284],[0,0],[-8.284,0],[0,0],[0,-8.284]],"v":[[-86.5,-15],[86.5,-15],[101.5,0],[101.5,0],[86.5,15],[-86.5,15],[-101.5,0],[-101.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"screen","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[287,150,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,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[320,204.01],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":13,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11306,"st":-94,"bm":0},{"ddd":0,"ind":2,"ty":0,"nm":"Pre-comp_Taskbar_Persistent_Layers","tt":1,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":205,"s":[206,150,0],"to":[-41.417,-45.917,0],"ti":[41.417,45.917,0]},{"t":285,"s":[-42.5,-125.5,0]}],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":205,"s":[100,100,100]},{"t":285,"s":[370,370,100]}],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".black","cl":"black","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[15.3,0],[0,0],[0,15.7],[0,0],[-15.3,0],[0,0],[0,-15.7],[0,0]],"o":[[0,0],[-15.3,0],[0,0],[0,-15.7],[0,0],[15.2,0],[0,0],[0,15.5]],"v":[[178.2,150],[-178.2,150],[-206,121.5],[-206,-121.5],[-178.2,-150],[178.3,-150],[206,-121.5],[206,121.7]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11306,"st":-94,"bm":0}],"markers":[{"tm":154,"cm":"","dr":0},{"tm":360,"cm":"","dr":0}]}
\ No newline at end of file
diff --git a/quickstep/res/raw-night/taskbar_edu_suggestions_transient.json b/quickstep/res/raw-night/taskbar_edu_suggestions_transient.json
new file mode 100644
index 0000000..e7a4cac
--- /dev/null
+++ b/quickstep/res/raw-night/taskbar_edu_suggestions_transient.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":548,"op":628,"w":412,"h":300,"nm":"Taskbar_Transient_Step_4","ddd":0,"assets":[{"id":"comp_0","nm":"Pre-comp_TaskBar_Transient","fr":60,"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 1","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[237.75,183,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[50,50,100],"ix":6,"l":2}},"ao":0,"ip":102,"op":255,"st":90,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"swipe up 2","parent":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":100,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":110,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":167,"s":[100]},{"t":177,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":100,"s":[-45.3,33.917,0],"to":[5.5,9,0],"ti":[-12,-36,0]},{"i":{"x":0.3,"y":0.3},"o":{"x":0.333,"y":0.333},"t":127,"s":[-12.3,87.917,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[-12.3,87.917,0],"to":[0,0,0],"ti":[-99.967,45.186,0]},{"t":187,"s":[89.5,-76.7,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.1,0.1,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":127,"s":[100,100,100]},{"i":{"x":[0.1,0.1,0.667],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.333],"y":[0,0,0]},"t":137,"s":[80,80,100]},{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.333],"y":[0,0,0]},"t":167,"s":[80,80,100]},{"t":179,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"ef":[{"ty":25,"nm":"Drop Shadow","np":8,"mn":"ADBE Drop Shadow","ix":1,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":63.75,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":8,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":30,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]},{"ty":25,"nm":"Drop Shadow 2","np":8,"mn":"ADBE Drop Shadow","ix":2,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":40,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]}],"shapes":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.5,0.5],"y":[1,1]},"o":{"x":[0.5,0.5],"y":[0,0]},"t":240,"s":[56,56]},{"i":{"x":[0.5,0.5],"y":[1,1]},"o":{"x":[0.5,0.5],"y":[0,0]},"t":262,"s":[56,98]},{"t":288,"s":[56,56]}],"ix":2},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[0,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.8,"y":0},"t":187,"s":[0,14],"to":[0,0],"ti":[0,0]},{"t":189,"s":[0,0]}],"ix":3},"r":{"a":0,"k":67,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,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":[1,1,1,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":102,"op":234,"st":77,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".grey300","cl":"grey300","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-83.044,0.454,0],"ix":2,"l":2},"a":{"a":0,"k":[121.456,227.454,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[125.547,231.545],"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":"Layer 18","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[125.547,227.455],"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":"Layer 17","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[125.547,223.364],"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":"Layer 16","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[121.453,231.545],"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":"Layer 15","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[121.453,227.455],"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":"Layer 14","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[121.453,223.364],"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":"Layer 13","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[117.364,231.545],"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":"Layer 12","np":1,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[117.364,227.455],"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":"Layer 11","np":1,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[117.364,223.364],"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":"Layer 10","np":1,"cix":2,"bm":0,"ix":9,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"green circle matte","parent":20,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84.409,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.807843148708,0.917647063732,0.839215695858,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.807843137255,0.917647058824,0.839215686275,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".green400","cl":"green400","parent":20,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":550,"s":[84.522,0.001,0],"to":[0,-3.333,0],"ti":[0,3.333,0]},{"t":580,"s":[84.522,-19.999,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[80,80,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.35686275363,0.72549021244,0.454901963472,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"green circle matte 2","parent":20,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84.409,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.807843148708,0.917647063732,0.839215695858,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.807843137255,0.917647058824,0.839215686275,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".green400","cl":"green400","parent":20,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":553,"s":[84.522,20.001,0],"to":[0,-3.333,0],"ti":[0,3.333,0]},{"t":583,"s":[84.522,0.001,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[80,80,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.35686275363,0.72549021244,0.454901963472,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".green100","cl":"green100","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84.409,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.807843148708,0.917647063732,0.839215695858,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.807843137255,0.917647058824,0.839215686275,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"blue circle matte 2","parent":20,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[55.772,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.541175991881,0.705881993911,0.972549019608,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".blue400","cl":"blue400","parent":20,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":544,"s":[55.772,0.001,0],"to":[0,-3.333,0],"ti":[0,3.333,0]},{"t":574,"s":[55.772,-19.999,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[80,80,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"blue circle matte","parent":20,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[55.772,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.541175991881,0.705881993911,0.972549019608,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".blue400","cl":"blue400","parent":20,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":547,"s":[55.772,20.001,0],"to":[0,-3.333,0],"ti":[0,3.333,0]},{"t":577,"s":[55.772,0.001,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[80,80,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".blue100","cl":"blue100","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[55.772,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.823529422283,0.890196084976,0.988235294819,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.823529411765,0.890196078431,0.988235294118,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":".yellow400","cl":"yellow400","parent":20,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":177,"s":[0]},{"t":187,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":947,"s":[27.135,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":957,"s":[27.135,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":967,"s":[27.135,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.988235294819,0.78823530674,0.203921571374,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":177,"op":11577,"st":177,"bm":0},{"ddd":0,"ind":15,"ty":4,"nm":".yellow400","cl":"yellow400","parent":20,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":167,"s":[100]},{"t":177,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[27.135,0.001,0],"to":[0.728,-9.542,0],"ti":[-45.478,19.292,0]},{"t":187,"s":[78,-76.749,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.988235294819,0.78823530674,0.203921571374,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":16,"ty":4,"nm":".grey400","cl":"grey400","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":944,"s":[27.135,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":954,"s":[27.135,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":964,"s":[27.135,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.741176470588,0.756862745098,0.776470588235,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":137,"op":252,"st":0,"bm":0},{"ddd":0,"ind":17,"ty":4,"nm":".green400","cl":"green400","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":941,"s":[-1.498,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":951,"s":[-1.498,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":961,"s":[-1.498,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.35686275363,0.72549021244,0.454901963472,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":18,"ty":4,"nm":".red400","cl":"red400","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":938,"s":[-30.134,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":948,"s":[-30.134,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":958,"s":[-30.134,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333337307,0.403921574354,0.360784322023,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":19,"ty":4,"nm":".blue400","cl":"blue400","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":935,"s":[-58.771,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":945,"s":[-58.771,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":955,"s":[-58.771,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":20,"ty":4,"nm":".grey800","cl":"grey800","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[204.5,227.001,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0.001,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":[[-8.284,0],[0,0],[0,-8.284],[0,0],[8.284,0],[0,0],[0,8.284],[0,0]],"o":[[0,0],[8.284,0],[0,0],[0,8.284],[0,0],[-8.284,0],[0,0],[0,-8.284]],"v":[[-86.5,-15],[86.5,-15],[101.5,0],[101.5,0],[86.5,15],[-86.5,15],[-101.5,0],[-101.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.235294118524,0.250980407,0.262745112181,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0}]},{"id":"comp_1","nm":"Pre-comp_Toggle","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".blue900","cl":"blue900","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":336,"s":[-12.5,0,0],"to":[3.75,0,0],"ti":[-3.75,0,0]},{"i":{"x":0.2,"y":0.2},"o":{"x":0.7,"y":0.7},"t":356,"s":[10,0,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":416,"s":[10,0,0],"to":[-3.75,0,0],"ti":[3.75,0,0]},{"t":436,"s":[-12.5,0,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":336,"s":[{"i":[[-6.627,0],[0,0],[0,-6.627],[0,0],[6.627,0],[0,0],[0,6.627],[0,0]],"o":[[0,0],[6.627,0],[0,0],[0,6.627],[0,0],[-6.627,0],[0,0],[0,-6.627]],"v":[[0,-12],[0,-12],[12,0],[12,0],[0,12],[0,12],[-12,0],[-12,0]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.8,"y":0},"t":356,"s":[{"i":[[-7.732,0],[0,0],[0,-7.732],[0,0],[7.732,0],[0,0],[0,7.732],[0,0]],"o":[[0,0],[7.732,0],[0,0],[0,7.732],[0,0],[-7.732,0],[0,0],[0,-7.732]],"v":[[0,-14],[0,-14],[14,0],[14,0],[0,14],[0,14],[-14,0],[-14,0]],"c":true}]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0},"t":416,"s":[{"i":[[-7.732,0],[0,0],[0,-7.732],[0,0],[7.732,0],[0,0],[0,7.732],[0,0]],"o":[[0,0],[7.732,0],[0,0],[0,7.732],[0,0],[-7.732,0],[0,0],[0,-7.732]],"v":[[0,-14],[0,-14],[14,0],[14,0],[0,14],[0,14],[-14,0],[-14,0]],"c":true}]},{"t":436,"s":[{"i":[[-6.627,0],[0,0],[0,-6.627],[0,0],[6.627,0],[0,0],[0,6.627],[0,0]],"o":[[0,0],[6.627,0],[0,0],[0,6.627],[0,0],[-6.627,0],[0,0],[0,-6.627]],"v":[[0,-12],[0,-12],[12,0],[12,0],[0,12],[0,12],[-12,0],[-12,0]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.090196078431,0.305882352941,0.650980392157,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".blue200","cl":"blue200","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[254.5,133.75,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[50,50,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-8.837,0],[0,0],[0,-8.837],[0,0],[8.837,0],[0,0],[0,8.837],[0,0]],"o":[[0,0],[8.837,0],[0,0],[0,8.837],[0,0],[-8.837,0],[0,0],[0,-8.837]],"v":[[-10,-16],[10,-16],[26,0],[26,0],[10,16],[-10,16],[-26,0],[-26,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.682352941176,0.796078431373,0.980392156863,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".blue100","cl":"blue100","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,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,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.784,0],[0,1.783],[1.783,0],[0,-1.783]],"o":[[1.783,0],[0,-1.783],[-1.784,0],[0,1.783]],"v":[[-43.755,-12.577],[-40.526,-15.806],[-43.755,-19.035],[-46.984,-15.806]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.823529411765,0.890196078431,0.988235294118,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0.053]],"o":[[0,0.053],[0,0]],"v":[[-44.937,-23.822],[-44.937,-23.822]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[0,0.054]],"o":[[0,0.054],[0,0]],"v":[[-49.458,-21.078],[-49.458,-21.078]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[-0.431,0.269],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0.215],[0,0.215],[0,0],[0,0],[0,0],[0,0],[0,0],[0.431,0.161],[0,0],[0,0],[0,0],[0,0],[0,0],[0.431,-0.269],[0,0],[0,0],[0,0],[0,0],[0,0],[0,-0.216],[-0.054,-0.215],[0,0],[0,0],[0,0],[0,0],[0,0],[-0.431,-0.162],[0,0]],"o":[[0,0],[0,0],[0,0],[0.431,-0.162],[0,0],[0,0],[0,0],[0,0],[0,0],[0.054,-0.215],[0,-0.269],[0,0],[0,0],[0,0],[0,0],[0,0],[-0.377,-0.269],[0,0],[0,0],[0,0],[0,0],[0,0],[-0.431,0.161],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0.215],[0,0.215],[0,0],[0,0],[0,0],[0,0],[0,0],[0.377,0.323],[0,0],[0,0]],"v":[[-45.045,-8.215],[-42.515,-8.215],[-42.138,-10.529],[-41.654,-10.744],[-40.416,-11.444],[-39.986,-11.767],[-37.779,-10.906],[-36.487,-13.112],[-38.371,-14.565],[-38.317,-15.104],[-38.263,-15.803],[-38.317,-16.503],[-38.371,-17.041],[-36.487,-18.494],[-37.779,-20.701],[-39.986,-19.84],[-40.416,-20.163],[-41.654,-20.862],[-42.138,-21.078],[-42.461,-23.392],[-44.991,-23.392],[-45.314,-21.078],[-45.798,-20.862],[-47.036,-20.163],[-47.467,-19.84],[-49.673,-20.701],[-50.965,-18.494],[-49.081,-17.041],[-49.135,-16.503],[-49.189,-15.803],[-49.135,-15.104],[-49.081,-14.565],[-50.965,-13.112],[-49.673,-10.906],[-47.467,-11.767],[-47.036,-11.444],[-45.798,-10.744],[-45.368,-10.529]],"c":true},"ix":2},"nm":"Path 3","mn":"ADBE Vector Shape - Group","hd":false},{"ind":3,"ty":"sh","ix":4,"ks":{"a":0,"k":{"i":[[0,0],[0.054,0]],"o":[[0.054,0],[0,0]],"v":[[-44.991,-7.784],[-44.991,-7.784]],"c":true},"ix":2},"nm":"Path 4","mn":"ADBE Vector Shape - Group","hd":false},{"ind":4,"ty":"sh","ix":5,"ks":{"a":0,"k":{"i":[[0.7,0],[0,0],[0.108,0.7],[0,0],[0.215,0.162],[0,0],[0.323,0.592],[0,0],[-0.484,0.376],[0,0],[0,0.161],[0,0.162],[0,0],[-0.376,0.593],[0,0],[-0.592,-0.215],[0,0],[-0.215,0.162],[0,0],[-0.7,0],[0,0],[-0.107,-0.7],[0,0],[-0.269,-0.162],[0,0],[-0.323,-0.592],[0,0],[0.484,-0.377],[0,0],[0,-0.162],[0,-0.161],[0,0],[0.323,-0.592],[0,0],[0.646,0.215],[0,0],[0.216,-0.162],[0,0]],"o":[[0,0],[-0.7,0],[0,0],[-0.269,-0.108],[0,0],[-0.646,0.215],[0,0],[-0.323,-0.592],[0,0],[0,-0.161],[0,-0.162],[0,0],[-0.538,-0.431],[0,0],[0.323,-0.592],[0,0],[0.215,-0.162],[0,0],[0.053,-0.646],[0,0],[0.699,0],[0,0],[0.269,0.108],[0,0],[0.646,-0.215],[0,0],[0.323,0.592],[0,0],[0,0.161],[0,0.161],[0,0],[0.538,0.431],[0,0],[-0.323,0.592],[0,0],[-0.215,0.161],[0,0],[0,0.538]],"v":[[-42.085,-6.385],[-45.475,-6.385],[-46.821,-7.569],[-47.09,-9.291],[-47.789,-9.722],[-49.458,-9.076],[-51.126,-9.668],[-52.795,-12.574],[-52.472,-14.296],[-51.072,-15.373],[-51.072,-15.803],[-51.072,-16.234],[-52.472,-17.31],[-52.795,-19.033],[-51.126,-21.939],[-49.512,-22.531],[-47.843,-21.831],[-47.144,-22.262],[-46.874,-24.038],[-45.529,-25.168],[-42.138,-25.168],[-40.793,-23.984],[-40.524,-22.262],[-39.77,-21.831],[-38.102,-22.477],[-36.433,-21.885],[-34.765,-18.979],[-35.088,-17.256],[-36.487,-16.18],[-36.487,-15.749],[-36.487,-15.319],[-35.088,-14.243],[-34.765,-12.52],[-36.487,-9.56],[-38.156,-8.968],[-39.824,-9.614],[-40.524,-9.183],[-40.793,-7.407]],"c":true},"ix":2},"nm":"Path 5","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.823529411765,0.890196078431,0.988235294118,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 2","np":6,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[-43.792,-15.776],"ix":2},"a":{"a":0,"k":[-43.792,-15.776],"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":"Group 6","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-8.284,0],[0,0],[0,-8.284],[0,0],[8.284,0],[0,0],[0,8.284],[0,0]],"o":[[0,0],[8.284,0],[0,0],[0,8.284],[0,0],[-8.284,0],[0,0],[0,-8.284]],"v":[[-54,-33],[60,-33],[75,-18],[75,-13],[60,2],[-54,2],[-69,-13],[-69,-18]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.61568627451,0.964705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 5","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11400,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"press 2","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":381,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":391,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":416,"s":[100]},{"t":426,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[259.25,134,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":406,"s":[50,50,100]},{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":416,"s":[40,40,100]},{"t":426,"s":[50,50,100]}],"ix":6,"l":2}},"ao":0,"ef":[{"ty":25,"nm":"Drop Shadow","np":8,"mn":"ADBE Drop Shadow","ix":1,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":63.75,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":8,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":30,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]},{"ty":25,"nm":"Drop Shadow 2","np":8,"mn":"ADBE Drop Shadow","ix":2,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":40,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]}],"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[56,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":67,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,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":[1,1,1,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.167,"y":0.186},"t":381,"s":[-37,67],"to":[15,-7],"ti":[7,20]},{"t":406,"s":[0,0],"h":1}],"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":"Touch","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":381,"op":437,"st":176,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"press","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":301,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":311,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":336,"s":[100]},{"t":346,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[248,134,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":326,"s":[50,50,100]},{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":336,"s":[40,40,100]},{"t":346,"s":[50,50,100]}],"ix":6,"l":2}},"ao":0,"ef":[{"ty":25,"nm":"Drop Shadow","np":8,"mn":"ADBE Drop Shadow","ix":1,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":63.75,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":8,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":30,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]},{"ty":25,"nm":"Drop Shadow 2","np":8,"mn":"ADBE Drop Shadow","ix":2,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":40,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]}],"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[56,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":67,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,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":[1,1,1,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.167,"y":0.186},"t":301,"s":[-37,67],"to":[15,-7],"ti":[7,20]},{"t":326,"s":[0,0],"h":1}],"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":"Touch","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":301,"op":357,"st":96,"bm":0},{"ddd":0,"ind":3,"ty":3,"nm":"Null 1","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,194,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[50,50,100],"ix":6,"l":2}},"ao":0,"ip":15,"op":73,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"swipe up","parent":3,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":15,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":25,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":60,"s":[100]},{"t":70,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":15,"s":[-48.3,37.417,0],"to":[5.5,9,0],"ti":[-12,-36,0]},{"i":{"x":0.667,"y":0.667},"o":{"x":0.333,"y":0.333},"t":40,"s":[-15.3,91.417,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.45,"y":0},"t":50,"s":[-15.3,91.417,0],"to":[0,0,0],"ti":[0,0,0]},{"t":70,"s":[-15.3,-62.583,0],"h":1}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":40,"s":[100,100,100]},{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":50,"s":[80,80,100]},{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":60,"s":[80,80,100]},{"t":70,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"ef":[{"ty":25,"nm":"Drop Shadow","np":8,"mn":"ADBE Drop Shadow","ix":1,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":63.75,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":8,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":30,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]},{"ty":25,"nm":"Drop Shadow 2","np":8,"mn":"ADBE Drop Shadow","ix":2,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":40,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]}],"shapes":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.5,0.5],"y":[1,1]},"o":{"x":[0.5,0.5],"y":[0,0]},"t":52,"s":[56,56]},{"i":{"x":[0.5,0.5],"y":[1,1]},"o":{"x":[0.5,0.5],"y":[0,0]},"t":62,"s":[56,98]},{"t":72,"s":[56,56]}],"ix":2},"p":{"a":1,"k":[{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":52,"s":[0,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":62,"s":[0,14],"to":[0,0],"ti":[0,0]},{"t":72,"s":[0,0]}],"ix":3},"r":{"a":0,"k":67,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,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":[1,1,1,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":15,"op":73,"st":-10,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"screen_matte","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[287,150,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,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[320,204.01],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":13,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":0,"nm":"Pre-comp_TaskBar_Transient","tt":1,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.1,"y":1},"o":{"x":0.7,"y":0},"t":50,"s":[204.5,244.501,0],"to":[0,-2.5,0],"ti":[0,2.5,0]},{"i":{"x":0.1,"y":0.1},"o":{"x":0.167,"y":0.167},"t":94,"s":[204.5,229.501,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":356,"s":[204.5,229.501,0],"to":[0,1.333,0],"ti":[0,-1.333,0]},{"i":{"x":0.1,"y":0.1},"o":{"x":0.167,"y":0.167},"t":386,"s":[204.5,237.501,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":436,"s":[204.5,237.501,0],"to":[0,-1.333,0],"ti":[0,1.333,0]},{"i":{"x":0.3,"y":0.3},"o":{"x":0.3,"y":0.3},"t":466,"s":[204.5,229.501,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":474,"s":[204.5,229.501,0],"to":[-42.083,-12.917,0],"ti":[42.083,12.917,0]},{"t":554,"s":[-48,152.001,0]}],"ix":2,"l":2},"a":{"a":0,"k":[206,227.625,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.7,0.7,0.167],"y":[0,0,0]},"t":50,"s":[70,70,100]},{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":94,"s":[100,100,100]},{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":474,"s":[100,100,100]},{"t":554,"s":[370,370,100]}],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".grey800","cl":"grey800","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.9,"y":0},"t":46,"s":[205.5,241.5,0],"to":[0,-1.667,0],"ti":[0,1.667,0]},{"t":76,"s":[205.5,231.5,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.381,0],[0,0],[0,-1.381],[0,0],[1.381,0],[0,0],[0,1.381],[0,0]],"o":[[0,0],[1.381,0],[0,0],[0,1.381],[0,0],[-1.381,0],[0,0],[0,-1.381]],"v":[[-42,-2.5],[42,-2.5],[44.5,0],[44.5,0],[42,2.5],[-42,2.5],[-44.5,0],[-44.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":65,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":0,"nm":"Pre-comp_Toggle","refId":"comp_1","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":256,"s":[0]},{"i":{"x":[0.316],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":286,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":474,"s":[100]},{"t":554,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":235,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".blue100","cl":"blue100","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":249,"s":[0]},{"t":269,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[287,150,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,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.3,0.3],"y":[0,0]},"t":356,"s":[320,204.01]},{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0,0]},"t":386,"s":[320,173.01]},{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.3,0.3],"y":[0,0]},"t":436,"s":[320,173.01]},{"t":466,"s":[320,204.01]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":12,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.823529411765,0.890196078431,0.988235294118,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":356,"s":[-81,0],"to":[0,-2.542],"ti":[0,2.542]},{"i":{"x":0.1,"y":0.1},"o":{"x":0.167,"y":0.167},"t":386,"s":[-81,-15.25],"to":[0,0],"ti":[0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":436,"s":[-81,-15.25],"to":[0,2.542],"ti":[0,-2.542]},{"t":466,"s":[-81,0]}],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":286,"op":11635,"st":235,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".blue100","cl":"blue100","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[132.562,150,0],"ix":2,"l":2},"a":{"a":0,"k":[-154.438,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":137,"s":[100,100,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":187,"s":[95,95,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.167],"y":[0,0,0]},"t":249,"s":[95,95,100]},{"t":269,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[13.125,-89.005],[13.125,89.005],[0.125,102.005],[-147,102.005],[-160,89.005],[-160,-89.005],[-147,-102.005],[0.125,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.8,"y":0},"t":187,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[0,-89.005],[0,89.005],[-13,102.005],[-147,102.005],[-160,89.005],[-160,-89.005],[-147,-102.005],[-13,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":249,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[0,-89.005],[0,89.005],[-13,102.005],[-147,102.005],[-160,89.005],[-160,-89.005],[-147,-102.005],[-13,-102.005]],"c":true}]},{"t":269,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[13.125,-89.005],[13.125,89.005],[0.125,102.005],[-147,102.005],[-160,89.005],[-160,-89.005],[-147,-102.005],[0.125,-102.005]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.823529411765,0.890196078431,0.988235294118,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":286,"st":0,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":".blue100","cl":"blue100","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.8],"y":[0]},"t":137,"s":[100]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.8],"y":[0]},"t":187,"s":[0]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.3],"y":[0]},"t":249,"s":[0]},{"t":259,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[279.438,150,0],"ix":2,"l":2},"a":{"a":0,"k":[-7.562,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":137,"s":[100,100,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":187,"s":[95,95,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.167],"y":[0,0,0]},"t":249,"s":[95,95,100]},{"t":269,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[-0.125,102.005],[-13.125,89.005],[-13.125,-89.005],[-0.125,-102.005],[147,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.8,"y":0},"t":187,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[13,102.005],[0,89.005],[0,-89.005],[13,-102.005],[147,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":249,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[13,102.005],[0,89.005],[0,-89.005],[13,-102.005],[147,-102.005]],"c":true}]},{"t":269,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[-0.125,102.005],[-13.125,89.005],[-13.125,-89.005],[-0.125,-102.005],[147,-102.005]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.823529411765,0.890196078431,0.988235294118,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":286,"st":0,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".yellow100","cl":"yellow100","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[279.438,150,0],"ix":2,"l":2},"a":{"a":0,"k":[-7.562,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":137,"s":[100,100,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":187,"s":[95,95,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.167],"y":[0,0,0]},"t":249,"s":[95,95,100]},{"t":269,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[-0.125,102.005],[-13.125,89.005],[-13.125,-89.005],[-0.125,-102.005],[147,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.8,"y":0},"t":187,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[13,102.005],[0,89.005],[0,-89.005],[13,-102.005],[147,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":249,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[13,102.005],[0,89.005],[0,-89.005],[13,-102.005],[147,-102.005]],"c":true}]},{"t":269,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[-0.125,102.005],[-13.125,89.005],[-13.125,-89.005],[-0.125,-102.005],[147,-102.005]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.996078431373,0.937254901961,0.764705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":286,"st":0,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".grey800","cl":"grey800","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[287,150,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,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[320,204.01],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":13,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":100,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":".black","cl":"black","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[15.3,0],[0,0],[0,15.7],[0,0],[-15.3,0],[0,0],[0,-15.7],[0,0]],"o":[[0,0],[-15.3,0],[0,0],[0,-15.7],[0,0],[15.2,0],[0,0],[0,15.5]],"v":[[178.2,150],[-178.2,150],[-206,121.5],[-206,-121.5],[-178.2,-150],[178.3,-150],[206,-121.5],[206,121.7]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11400,"st":0,"bm":0}],"markers":[{"tm":94,"cm":"","dr":0},{"tm":249,"cm":"","dr":0},{"tm":474,"cm":"","dr":0}]}
\ 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/raw/taskbar_edu_splitscreen.json b/quickstep/res/raw/taskbar_edu_splitscreen.json
new file mode 100644
index 0000000..09775b0
--- /dev/null
+++ b/quickstep/res/raw/taskbar_edu_splitscreen.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":240,"w":412,"h":300,"nm":"Taskbar_Transient_EDU_2","ddd":0,"assets":[{"id":"comp_0","nm":"Pre-comp_TaskBar_Transient_Step_2","fr":60,"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 1","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[237.75,183,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[50,50,100],"ix":6,"l":2}},"ao":0,"ip":102,"op":255,"st":90,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"swipe up 2","parent":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":100,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":110,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":207,"s":[100]},{"t":217,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":100,"s":[-45.3,33.917,0],"to":[5.5,9,0],"ti":[-12,-36,0]},{"i":{"x":0.3,"y":0.3},"o":{"x":0.333,"y":0.333},"t":127,"s":[-12.3,87.917,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[-12.3,87.917,0],"to":[0,0,0],"ti":[0,0,0]},{"t":187,"s":[59.5,-26.7,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.1,0.1,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":127,"s":[100,100,100]},{"t":137,"s":[80,80,100]}],"ix":6,"l":2}},"ao":0,"ef":[{"ty":25,"nm":"Drop Shadow","np":8,"mn":"ADBE Drop Shadow","ix":1,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":63.75,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":8,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":30,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]},{"ty":25,"nm":"Drop Shadow 2","np":8,"mn":"ADBE Drop Shadow","ix":2,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":40,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]}],"shapes":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.5,0.5],"y":[1,1]},"o":{"x":[0.5,0.5],"y":[0,0]},"t":240,"s":[56,56]},{"i":{"x":[0.5,0.5],"y":[1,1]},"o":{"x":[0.5,0.5],"y":[0,0]},"t":262,"s":[56,98]},{"t":288,"s":[56,56]}],"ix":2},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[0,0],"to":[0,0],"ti":[0,0]},{"t":187,"s":[0,14]}],"ix":3},"r":{"a":0,"k":67,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,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":[1,1,1,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":102,"op":234,"st":77,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".grey300","cl":"grey300","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-83.044,0.454,0],"ix":2,"l":2},"a":{"a":0,"k":[121.456,227.454,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[125.547,231.545],"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":"Layer 18","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[125.547,227.455],"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":"Layer 17","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[125.547,223.364],"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":"Layer 16","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[121.453,231.545],"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":"Layer 15","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[121.453,227.455],"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":"Layer 14","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[121.453,223.364],"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":"Layer 13","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[117.364,231.545],"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":"Layer 12","np":1,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[117.364,227.455],"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":"Layer 11","np":1,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[117.364,223.364],"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":"Layer 10","np":1,"cix":2,"bm":0,"ix":9,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"green circle matte","parent":20,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84.409,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.807843148708,0.917647063732,0.839215695858,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.807843137255,0.917647058824,0.839215686275,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".green400","cl":"green400","parent":20,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":550,"s":[84.522,0.001,0],"to":[0,-3.333,0],"ti":[0,3.333,0]},{"t":580,"s":[84.522,-19.999,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[80,80,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.35686275363,0.72549021244,0.454901963472,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"green circle matte 2","parent":20,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84.409,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.807843148708,0.917647063732,0.839215695858,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.807843137255,0.917647058824,0.839215686275,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".green400","cl":"green400","parent":20,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":553,"s":[84.522,20.001,0],"to":[0,-3.333,0],"ti":[0,3.333,0]},{"t":583,"s":[84.522,0.001,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[80,80,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.35686275363,0.72549021244,0.454901963472,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".green100","cl":"green100","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84.409,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.807843148708,0.917647063732,0.839215695858,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.807843137255,0.917647058824,0.839215686275,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"blue circle matte 2","parent":20,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[55.772,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.541175991881,0.705881993911,0.972549019608,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".blue400","cl":"blue400","parent":20,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":544,"s":[55.772,0.001,0],"to":[0,-3.333,0],"ti":[0,3.333,0]},{"t":574,"s":[55.772,-19.999,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[80,80,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"blue circle matte","parent":20,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[55.772,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.541175991881,0.705881993911,0.972549019608,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".blue400","cl":"blue400","parent":20,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":547,"s":[55.772,20.001,0],"to":[0,-3.333,0],"ti":[0,3.333,0]},{"t":577,"s":[55.772,0.001,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[80,80,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".blue100","cl":"blue100","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[55.772,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.823529422283,0.890196084976,0.988235294819,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.823529411765,0.890196078431,0.988235294118,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":".yellow400","cl":"yellow400","parent":20,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":207,"s":[0]},{"t":217,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":977,"s":[27.135,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":987,"s":[27.135,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":997,"s":[27.135,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.988235294819,0.78823530674,0.203921571374,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":207,"op":11607,"st":207,"bm":0},{"ddd":0,"ind":15,"ty":4,"nm":".yellow400","cl":"yellow400","parent":20,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":207,"s":[100]},{"t":217,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[27.135,0.001,0],"to":[0,0,0],"ti":[0,0,0]},{"t":187,"s":[63,-51.749,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.988235294819,0.78823530674,0.203921571374,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":16,"ty":4,"nm":".grey400","cl":"grey400","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":944,"s":[27.135,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":954,"s":[27.135,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":964,"s":[27.135,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.741176470588,0.756862745098,0.776470588235,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":137,"op":252,"st":0,"bm":0},{"ddd":0,"ind":17,"ty":4,"nm":".green400","cl":"green400","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":941,"s":[-1.498,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":951,"s":[-1.498,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":961,"s":[-1.498,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.35686275363,0.72549021244,0.454901963472,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":18,"ty":4,"nm":".red400","cl":"red400","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":938,"s":[-30.134,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":948,"s":[-30.134,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":958,"s":[-30.134,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333337307,0.403921574354,0.360784322023,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":19,"ty":4,"nm":".blue400","cl":"blue400","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":935,"s":[-58.771,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":945,"s":[-58.771,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":955,"s":[-58.771,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":20,"ty":4,"nm":".grey800","cl":"grey800","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[204.5,227.001,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0.001,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":[[-8.284,0],[0,0],[0,-8.284],[0,0],[8.284,0],[0,0],[0,8.284],[0,0]],"o":[[0,0],[8.284,0],[0,0],[0,8.284],[0,0],[-8.284,0],[0,0],[0,-8.284]],"v":[[-86.5,-15],[86.5,-15],[101.5,0],[101.5,0],[86.5,15],[-86.5,15],[-101.5,0],[-101.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.235294118524,0.250980407,0.262745112181,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null_MasterScale","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[120,120,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"screen_matte","parent":1,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[81,0,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,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[320,204.01],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":13,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11306,"st":-94,"bm":0},{"ddd":0,"ind":3,"ty":0,"nm":"Pre-comp_TaskBar_Transient_Step_2","parent":1,"tt":1,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":262,"s":[-1.5,79.501,0],"to":[0,1.333,0],"ti":[0,-1.333,0]},{"i":{"x":0.1,"y":0.1},"o":{"x":0.167,"y":0.167},"t":292,"s":[-1.5,87.501,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":342,"s":[-1.5,87.501,0],"to":[0,-1.333,0],"ti":[0,1.333,0]},{"i":{"x":0.3,"y":0.3},"o":{"x":0.3,"y":0.3},"t":372,"s":[-1.5,79.501,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":380,"s":[-1.5,79.501,0],"to":[-42.083,-12.917,0],"ti":[42.083,12.917,0]},{"t":460,"s":[-254,2.001,0]}],"ix":2,"l":2},"a":{"a":0,"k":[206,227.625,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":380,"s":[100,100,100]},{"t":460,"s":[370,370,100]}],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":11306,"st":-94,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".blue100","cl":"blue100","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-73.438,0,0],"ix":2,"l":2},"a":{"a":0,"k":[-154.438,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":43,"s":[100,100,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":93,"s":[90,90,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.167],"y":[0,0,0]},"t":113,"s":[90,90,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":133,"s":[100,100,100]},{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":197,"s":[100,100,100]},{"t":217,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":43,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[13.125,-89.005],[13.125,89.005],[0.125,102.005],[-147,102.005],[-160,89.005],[-160,-89.005],[-147,-102.005],[0.125,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.8,"y":0},"t":93,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[0,-89.005],[0,89.005],[-13,102.005],[-147,102.005],[-160,89.005],[-160,-89.005],[-147,-102.005],[-13,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":113,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[0,-89.005],[0,89.005],[-13,102.005],[-147,102.005],[-160,89.005],[-160,-89.005],[-147,-102.005],[-13,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.167,"y":0},"t":133,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[-2.561,-89.005],[-2.561,89.005],[-15.561,102.005],[-147,102.005],[-160,89.005],[-160,-89.005],[-147,-102.005],[-15.561,-102.005]],"c":true}]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0},"t":197,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[-2.561,-89.005],[-2.561,89.005],[-15.561,102.005],[-147,102.005],[-160,89.005],[-160,-89.005],[-147,-102.005],[-15.561,-102.005]],"c":true}]},{"t":217,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[13.125,-89.005],[13.125,89.005],[0.125,102.005],[-147,102.005],[-160,89.005],[-160,-89.005],[-147,-102.005],[0.125,-102.005]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.823529411765,0.890196078431,0.988235294118,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":491,"st":-94,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".blue100","cl":"blue100","parent":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.8],"y":[0]},"t":43,"s":[100]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.8],"y":[0]},"t":93,"s":[0]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.3],"y":[0]},"t":113,"s":[0]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":123,"s":[100]},{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":207,"s":[100]},{"t":217,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[73.438,0,0],"ix":2,"l":2},"a":{"a":0,"k":[-7.562,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":43,"s":[100,100,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":93,"s":[90,90,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.167],"y":[0,0,0]},"t":113,"s":[90,90,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":133,"s":[100,100,100]},{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":197,"s":[100,100,100]},{"t":217,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":43,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[-0.125,102.005],[-13.125,89.005],[-13.125,-89.005],[-0.125,-102.005],[147,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.8,"y":0},"t":93,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[13,102.005],[0,89.005],[0,-89.005],[13,-102.005],[147,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":113,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[13,102.005],[0,89.005],[0,-89.005],[13,-102.005],[147,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.167,"y":0},"t":133,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[15.561,102.005],[2.561,89.005],[2.561,-89.005],[15.561,-102.005],[147,-102.005]],"c":true}]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0},"t":197,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[15.561,102.005],[2.561,89.005],[2.561,-89.005],[15.561,-102.005],[147,-102.005]],"c":true}]},{"t":217,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[-0.125,102.005],[-13.125,89.005],[-13.125,-89.005],[-0.125,-102.005],[147,-102.005]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.823529411765,0.890196078431,0.988235294118,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":491,"st":-94,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".yellow100","cl":"yellow100","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[73.438,0,0],"ix":2,"l":2},"a":{"a":0,"k":[-7.562,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":43,"s":[100,100,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":93,"s":[90,90,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.167],"y":[0,0,0]},"t":113,"s":[90,90,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":133,"s":[100,100,100]},{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":197,"s":[100,100,100]},{"t":217,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":43,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[-0.125,102.005],[-13.125,89.005],[-13.125,-89.005],[-0.125,-102.005],[147,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.8,"y":0},"t":93,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[13,102.005],[0,89.005],[0,-89.005],[13,-102.005],[147,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":113,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[13,102.005],[0,89.005],[0,-89.005],[13,-102.005],[147,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.167,"y":0},"t":133,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[15.561,102.005],[2.561,89.005],[2.561,-89.005],[15.561,-102.005],[147,-102.005]],"c":true}]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0},"t":197,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[15.561,102.005],[2.561,89.005],[2.561,-89.005],[15.561,-102.005],[147,-102.005]],"c":true}]},{"t":217,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[-0.125,102.005],[-13.125,89.005],[-13.125,-89.005],[-0.125,-102.005],[147,-102.005]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.996078431373,0.937254901961,0.764705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":491,"st":-94,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".grey800","cl":"grey800","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[81,0,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,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[320,204.01],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":13,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11306,"st":-94,"bm":0}],"markers":[{"tm":94,"cm":"","dr":0},{"tm":249,"cm":"","dr":0},{"tm":474,"cm":"","dr":0}]}
\ No newline at end of file
diff --git a/quickstep/res/raw/taskbar_edu_splitscreen_persistent.json b/quickstep/res/raw/taskbar_edu_splitscreen_persistent.json
new file mode 100644
index 0000000..1399828
--- /dev/null
+++ b/quickstep/res/raw/taskbar_edu_splitscreen_persistent.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":154,"w":412,"h":300,"nm":"Taskbar_Persistent_LT_Step_1","ddd":0,"assets":[{"id":"comp_0","nm":"Pre-comp_Taskbar_Persistent_Layers_LT","fr":60,"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 2","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,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":11306,"st":-94,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"screen_matte","parent":1,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[81,0,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,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[320,204.01],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":13,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11306,"st":-94,"bm":0},{"ddd":0,"ind":3,"ty":0,"nm":"Pre-comp_TaskBar_Persistent_LT","parent":1,"tt":1,"refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-1.5,87.892,0],"ix":2,"l":2},"a":{"a":0,"k":[206,227.625,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":11306,"st":-94,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"screen 3","parent":1,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[81,0,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,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[320,204.01],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":13,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":175,"op":11306,"st":-94,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".blue100","cl":"blue100","parent":1,"tt":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":155,"s":[0]},{"t":175,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[81,0.5,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,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[320,174.01],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":13,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.549019607843,0.713725490196,0.964705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,-15.25],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":175,"op":11541,"st":141,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".blue100","cl":"blue100","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-73.438,0,0],"ix":2,"l":2},"a":{"a":0,"k":[-154.438,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":43,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":93,"s":[95,95,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":155,"s":[95,95,100]},{"t":175,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":43,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[13.125,-89.005],[13.125,59.005],[0.125,72.005],[-147,72.005],[-160,59.005],[-160,-89.005],[-147,-102.005],[0.125,-102.005]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.8,"y":0},"t":93,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[0,-89.005],[0,62.689],[-13,75.689],[-147,75.689],[-160,62.689],[-160,-89.005],[-147,-102.005],[-13,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.167,"y":0},"t":155,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[0,-89.005],[0,62.689],[-13,75.689],[-147,75.689],[-160,62.689],[-160,-89.005],[-147,-102.005],[-13,-102.005]],"c":true}]},{"t":175,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[13.125,-89.005],[13.125,59.005],[0.125,72.005],[-147,72.005],[-160,59.005],[-160,-89.005],[-147,-102.005],[0.125,-102.005]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.549019607843,0.713725490196,0.964705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":175,"st":-94,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".blue100","cl":"blue100","parent":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.8],"y":[0]},"t":43,"s":[100]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.8],"y":[0]},"t":93,"s":[0]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":155,"s":[0]},{"t":175,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[73.438,0,0],"ix":2,"l":2},"a":{"a":0,"k":[-7.562,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":43,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":93,"s":[95,95,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":155,"s":[95,95,100]},{"t":175,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":43,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,59.005],[147,72.005],[-0.125,72.005],[-13.125,59.005],[-13.125,-89.005],[-0.125,-102.005],[147,-102.005]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.8,"y":0},"t":93,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,62.689],[147,75.689],[13,75.689],[0,62.689],[0,-89.005],[13,-102.005],[147,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.167,"y":0},"t":155,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,62.689],[147,75.689],[13,75.689],[0,62.689],[0,-89.005],[13,-102.005],[147,-102.005]],"c":true}]},{"t":175,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,59.005],[147,72.005],[-0.125,72.005],[-13.125,59.005],[-13.125,-89.005],[-0.125,-102.005],[147,-102.005]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.549019607843,0.713725490196,0.964705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":175,"st":-94,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".yellow100","cl":"yellow100","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[73.438,0,0],"ix":2,"l":2},"a":{"a":0,"k":[-7.562,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":43,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":93,"s":[95,95,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":155,"s":[95,95,100]},{"t":175,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":43,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,59.005],[147,72.005],[-0.125,72.005],[-13.125,59.005],[-13.125,-89.005],[-0.125,-102.005],[147,-102.005]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.8,"y":0},"t":93,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,62.689],[147,75.689],[13,75.689],[0,62.689],[0,-89.005],[13,-102.005],[147,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.167,"y":0},"t":155,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,62.689],[147,75.689],[13,75.689],[0,62.689],[0,-89.005],[13,-102.005],[147,-102.005]],"c":true}]},{"t":175,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,59.005],[147,72.005],[-0.125,72.005],[-13.125,59.005],[-13.125,-89.005],[-0.125,-102.005],[147,-102.005]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.98431372549,0.78431372549,0.274509803922,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":175,"st":-94,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".grey800","cl":"grey800","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[81,0,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,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[320,204.01],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":13,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.909803921569,0.917647058824,0.929411764706,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11306,"st":-94,"bm":0}]},{"id":"comp_1","nm":"Pre-comp_TaskBar_Persistent_LT","fr":60,"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 1","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[237.75,183,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[50,50,100],"ix":6,"l":2}},"ao":0,"ip":102,"op":255,"st":90,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"swipe up 2","parent":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":100,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":110,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":167,"s":[100]},{"t":177,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":100,"s":[-45.3,33.917,0],"to":[5.5,9,0],"ti":[-12,-36,0]},{"i":{"x":0.3,"y":0.3},"o":{"x":0.333,"y":0.333},"t":127,"s":[-12.3,87.917,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[-12.3,87.917,0],"to":[0,0,0],"ti":[-99.967,45.186,0]},{"t":187,"s":[89.5,-76.7,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.1,0.1,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":127,"s":[100,100,100]},{"i":{"x":[0.1,0.1,0.667],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.333],"y":[0,0,0]},"t":137,"s":[80,80,100]},{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.333],"y":[0,0,0]},"t":167,"s":[80,80,100]},{"t":179,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"ef":[{"ty":25,"nm":"Drop Shadow","np":8,"mn":"ADBE Drop Shadow","ix":1,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":63.75,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":8,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":30,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]},{"ty":25,"nm":"Drop Shadow 2","np":8,"mn":"ADBE Drop Shadow","ix":2,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":40,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]}],"shapes":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.5,0.5],"y":[1,1]},"o":{"x":[0.5,0.5],"y":[0,0]},"t":240,"s":[56,56]},{"i":{"x":[0.5,0.5],"y":[1,1]},"o":{"x":[0.5,0.5],"y":[0,0]},"t":262,"s":[56,98]},{"t":288,"s":[56,56]}],"ix":2},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[0,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.8,"y":0},"t":187,"s":[0,14],"to":[0,0],"ti":[0,0]},{"t":189,"s":[0,0]}],"ix":3},"r":{"a":0,"k":67,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,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":[1,1,1,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":102,"op":234,"st":77,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".grey300","cl":"grey300","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-83.044,0.454,0],"ix":2,"l":2},"a":{"a":0,"k":[121.456,227.454,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.454901960784,0.470588235294,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[125.547,231.545],"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":"Layer 18","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.454901960784,0.470588235294,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[125.547,227.455],"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":"Layer 17","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.454901960784,0.470588235294,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[125.547,223.364],"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":"Layer 16","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.454901960784,0.470588235294,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[121.453,231.545],"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":"Layer 15","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.454901960784,0.470588235294,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[121.453,227.455],"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":"Layer 14","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.454901960784,0.470588235294,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[121.453,223.364],"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":"Layer 13","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.454901960784,0.470588235294,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[117.364,231.545],"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":"Layer 12","np":1,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.454901960784,0.470588235294,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[117.364,227.455],"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":"Layer 11","np":1,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.454901960784,0.470588235294,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[117.364,223.364],"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":"Layer 10","np":1,"cix":2,"bm":0,"ix":9,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"green circle matte","parent":20,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84.409,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.807843148708,0.917647063732,0.839215695858,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.807843137255,0.917647058824,0.839215686275,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".green400","cl":"green400","parent":20,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":362,"s":[84.522,0.001,0],"to":[0,-3.333,0],"ti":[0,3.333,0]},{"t":392,"s":[84.522,-19.999,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[80,80,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.35686275363,0.72549021244,0.454901963472,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"green circle matte 2","parent":20,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84.409,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.807843148708,0.917647063732,0.839215695858,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.807843137255,0.917647058824,0.839215686275,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".green400","cl":"green400","parent":20,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":365,"s":[84.522,20.001,0],"to":[0,-3.333,0],"ti":[0,3.333,0]},{"t":395,"s":[84.522,0.001,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[80,80,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.35686275363,0.72549021244,0.454901963472,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".green100","cl":"green100","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84.409,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"blue circle matte 2","parent":20,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[55.772,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.541175991881,0.705881993911,0.972549019608,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".blue400","cl":"blue400","parent":20,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":356,"s":[55.772,0.001,0],"to":[0,-3.333,0],"ti":[0,3.333,0]},{"t":386,"s":[55.772,-19.999,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[80,80,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"blue circle matte","parent":20,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[55.772,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.541175991881,0.705881993911,0.972549019608,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".blue400","cl":"blue400","parent":20,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":359,"s":[55.772,20.001,0],"to":[0,-3.333,0],"ti":[0,3.333,0]},{"t":389,"s":[55.772,0.001,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[80,80,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".blue100","cl":"blue100","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[55.772,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":".yellow400","cl":"yellow400","parent":20,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":177,"s":[0]},{"t":187,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":947,"s":[27.135,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":957,"s":[27.135,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":967,"s":[27.135,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.941176470588,0.596078431373,0.145098039216,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":177,"op":11577,"st":177,"bm":0},{"ddd":0,"ind":15,"ty":4,"nm":".yelllow400","cl":"yelllow400","parent":20,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":167,"s":[100]},{"t":177,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[27.135,0.001,0],"to":[0.728,-9.542,0],"ti":[-45.478,19.292,0]},{"t":187,"s":[78,-76.749,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.941176470588,0.596078431373,0.145098039216,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":16,"ty":4,"nm":".grey400","cl":"grey400","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":944,"s":[27.135,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":954,"s":[27.135,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":964,"s":[27.135,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.741176470588,0.756862745098,0.776470588235,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":137,"op":252,"st":0,"bm":0},{"ddd":0,"ind":17,"ty":4,"nm":".green400","cl":"green400","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":941,"s":[-1.498,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":951,"s":[-1.498,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":961,"s":[-1.498,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.35686275363,0.72549021244,0.454901963472,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":18,"ty":4,"nm":".red400","cl":"red400","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":938,"s":[-30.134,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":948,"s":[-30.134,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":958,"s":[-30.134,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333337307,0.403921574354,0.360784322023,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":19,"ty":4,"nm":".blue400","cl":"blue400","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":935,"s":[-58.771,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":945,"s":[-58.771,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":955,"s":[-58.771,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.61568627451,0.964705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":20,"ty":4,"nm":".grey800","cl":"grey800","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[204.5,227.001,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0.001,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":[[-8.284,0],[0,0],[0,-8.284],[0,0],[8.284,0],[0,0],[0,8.284],[0,0]],"o":[[0,0],[8.284,0],[0,0],[0,8.284],[0,0],[-8.284,0],[0,0],[0,-8.284]],"v":[[-86.5,-15],[86.5,-15],[101.5,0],[101.5,0],[86.5,15],[-86.5,15],[-101.5,0],[-101.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.909803921569,0.917647058824,0.929411764706,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"screen","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[287,150,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,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[320,204.01],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":13,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11306,"st":-94,"bm":0},{"ddd":0,"ind":2,"ty":0,"nm":"Pre-comp_Taskbar_Persistent_Layers_LT","tt":1,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":205,"s":[206,150,0],"to":[-41.417,-45.917,0],"ti":[41.417,45.917,0]},{"t":285,"s":[-42.5,-125.5,0]}],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":205,"s":[100,100,100]},{"t":285,"s":[370,370,100]}],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".white","cl":"white","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[15.3,0],[0,0],[0,15.7],[0,0],[-15.3,0],[0,0],[0,-15.7],[0,0]],"o":[[0,0],[-15.3,0],[0,0],[0,-15.7],[0,0],[15.2,0],[0,0],[0,15.5]],"v":[[178.2,150],[-178.2,150],[-206,121.5],[-206,-121.5],[-178.2,-150],[178.3,-150],[206,-121.5],[206,121.7]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11306,"st":-94,"bm":0}],"markers":[{"tm":154,"cm":"","dr":0},{"tm":360,"cm":"","dr":0}]}
\ No newline at end of file
diff --git a/quickstep/res/raw/taskbar_edu_splitscreen_transient.json b/quickstep/res/raw/taskbar_edu_splitscreen_transient.json
new file mode 100644
index 0000000..0c65a06
--- /dev/null
+++ b/quickstep/res/raw/taskbar_edu_splitscreen_transient.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":94,"op":249,"w":412,"h":300,"nm":"Taskbar_Transient_Step_2_LT","ddd":0,"assets":[{"id":"comp_0","nm":"Taskbar_Transient_LT","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"press 2","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":381,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":391,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":416,"s":[100]},{"t":426,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[259.25,134,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":406,"s":[50,50,100]},{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":416,"s":[40,40,100]},{"t":426,"s":[50,50,100]}],"ix":6,"l":2}},"ao":0,"ef":[{"ty":25,"nm":"Drop Shadow","np":8,"mn":"ADBE Drop Shadow","ix":1,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":63.75,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":8,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":30,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]},{"ty":25,"nm":"Drop Shadow 2","np":8,"mn":"ADBE Drop Shadow","ix":2,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":40,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]}],"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[56,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":67,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,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":[1,1,1,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.167,"y":0.186},"t":381,"s":[-37,67],"to":[15,-7],"ti":[7,20]},{"t":406,"s":[0,0],"h":1}],"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":"Touch","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":381,"op":437,"st":176,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"press","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":301,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":311,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":336,"s":[100]},{"t":346,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[248,134,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":326,"s":[50,50,100]},{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":336,"s":[40,40,100]},{"t":346,"s":[50,50,100]}],"ix":6,"l":2}},"ao":0,"ef":[{"ty":25,"nm":"Drop Shadow","np":8,"mn":"ADBE Drop Shadow","ix":1,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":63.75,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":8,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":30,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]},{"ty":25,"nm":"Drop Shadow 2","np":8,"mn":"ADBE Drop Shadow","ix":2,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":40,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]}],"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[56,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":67,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,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":[1,1,1,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.167,"y":0.186},"t":301,"s":[-37,67],"to":[15,-7],"ti":[7,20]},{"t":326,"s":[0,0],"h":1}],"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":"Touch","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":301,"op":357,"st":96,"bm":0},{"ddd":0,"ind":3,"ty":3,"nm":"Null 1","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,194,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[50,50,100],"ix":6,"l":2}},"ao":0,"ip":15,"op":73,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"swipe up","parent":3,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":15,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":25,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":60,"s":[100]},{"t":70,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":15,"s":[-48.3,37.417,0],"to":[5.5,9,0],"ti":[-12,-36,0]},{"i":{"x":0.667,"y":0.667},"o":{"x":0.333,"y":0.333},"t":40,"s":[-15.3,91.417,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.45,"y":0},"t":50,"s":[-15.3,91.417,0],"to":[0,0,0],"ti":[0,0,0]},{"t":70,"s":[-15.3,-62.583,0],"h":1}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":40,"s":[100,100,100]},{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":50,"s":[80,80,100]},{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":60,"s":[80,80,100]},{"t":70,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"ef":[{"ty":25,"nm":"Drop Shadow","np":8,"mn":"ADBE Drop Shadow","ix":1,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":63.75,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":8,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":30,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]},{"ty":25,"nm":"Drop Shadow 2","np":8,"mn":"ADBE Drop Shadow","ix":2,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":40,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]}],"shapes":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.5,0.5],"y":[1,1]},"o":{"x":[0.5,0.5],"y":[0,0]},"t":52,"s":[56,56]},{"i":{"x":[0.5,0.5],"y":[1,1]},"o":{"x":[0.5,0.5],"y":[0,0]},"t":62,"s":[56,98]},{"t":72,"s":[56,56]}],"ix":2},"p":{"a":1,"k":[{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":52,"s":[0,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":62,"s":[0,14],"to":[0,0],"ti":[0,0]},{"t":72,"s":[0,0]}],"ix":3},"r":{"a":0,"k":67,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,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":[1,1,1,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":15,"op":73,"st":-10,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"screen_matte","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[287,150,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,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[320,204.01],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":13,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":0,"nm":"Pre-comp_TaskBar_Transient_LT","tt":1,"refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.1,"y":1},"o":{"x":0.7,"y":0},"t":50,"s":[204.5,244.501,0],"to":[0,-2.5,0],"ti":[0,2.5,0]},{"i":{"x":0.1,"y":0.1},"o":{"x":0.167,"y":0.167},"t":94,"s":[204.5,229.501,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":356,"s":[204.5,229.501,0],"to":[0,1.333,0],"ti":[0,-1.333,0]},{"i":{"x":0.1,"y":0.1},"o":{"x":0.167,"y":0.167},"t":386,"s":[204.5,237.501,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":436,"s":[204.5,237.501,0],"to":[0,-1.333,0],"ti":[0,1.333,0]},{"i":{"x":0.3,"y":0.3},"o":{"x":0.3,"y":0.3},"t":466,"s":[204.5,229.501,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":474,"s":[204.5,229.501,0],"to":[-42.083,-12.917,0],"ti":[42.083,12.917,0]},{"t":554,"s":[-48,152.001,0]}],"ix":2,"l":2},"a":{"a":0,"k":[206,227.625,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.7,0.7,0.167],"y":[0,0,0]},"t":50,"s":[70,70,100]},{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":94,"s":[100,100,100]},{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":474,"s":[100,100,100]},{"t":554,"s":[370,370,100]}],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".grey800","cl":"grey800","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.9,"y":0},"t":46,"s":[205.5,241.5,0],"to":[0,-1.667,0],"ti":[0,1.667,0]},{"t":76,"s":[205.5,231.5,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.381,0],[0,0],[0,-1.381],[0,0],[1.381,0],[0,0],[0,1.381],[0,0]],"o":[[0,0],[1.381,0],[0,0],[0,1.381],[0,0],[-1.381,0],[0,0],[0,-1.381]],"v":[[-42,-2.5],[42,-2.5],[44.5,0],[44.5,0],[42,2.5],[-42,2.5],[-44.5,0],[-44.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.909803921569,0.917647058824,0.929411764706,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":65,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":0,"nm":"Pre-comp_Toggle_LT","refId":"comp_2","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":256,"s":[0]},{"i":{"x":[0.316],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":286,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":474,"s":[100]},{"t":554,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":235,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".blue100","cl":"blue100","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":249,"s":[0]},{"t":269,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[287,150,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,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.3,0.3],"y":[0,0]},"t":356,"s":[320,204.01]},{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0,0]},"t":386,"s":[320,173.01]},{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.3,0.3],"y":[0,0]},"t":436,"s":[320,173.01]},{"t":466,"s":[320,204.01]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":12,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.549019607843,0.713725490196,0.964705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":356,"s":[-81,0],"to":[0,-2.542],"ti":[0,2.542]},{"i":{"x":0.1,"y":0.1},"o":{"x":0.167,"y":0.167},"t":386,"s":[-81,-15.25],"to":[0,0],"ti":[0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":436,"s":[-81,-15.25],"to":[0,2.542],"ti":[0,-2.542]},{"t":466,"s":[-81,0]}],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":286,"op":11635,"st":235,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".blue100","cl":"blue100","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[132.562,150,0],"ix":2,"l":2},"a":{"a":0,"k":[-154.438,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":137,"s":[100,100,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":187,"s":[95,95,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.167],"y":[0,0,0]},"t":249,"s":[95,95,100]},{"t":269,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[13.125,-89.005],[13.125,89.005],[0.125,102.005],[-147,102.005],[-160,89.005],[-160,-89.005],[-147,-102.005],[0.125,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.8,"y":0},"t":187,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[0,-89.005],[0,89.005],[-13,102.005],[-147,102.005],[-160,89.005],[-160,-89.005],[-147,-102.005],[-13,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":249,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[0,-89.005],[0,89.005],[-13,102.005],[-147,102.005],[-160,89.005],[-160,-89.005],[-147,-102.005],[-13,-102.005]],"c":true}]},{"t":269,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[13.125,-89.005],[13.125,89.005],[0.125,102.005],[-147,102.005],[-160,89.005],[-160,-89.005],[-147,-102.005],[0.125,-102.005]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.549019607843,0.713725490196,0.964705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":286,"st":0,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":".blue100","cl":"blue100","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.8],"y":[0]},"t":137,"s":[100]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.8],"y":[0]},"t":187,"s":[0]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.3],"y":[0]},"t":249,"s":[0]},{"t":259,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[279.438,150,0],"ix":2,"l":2},"a":{"a":0,"k":[-7.562,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":137,"s":[100,100,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":187,"s":[95,95,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.167],"y":[0,0,0]},"t":249,"s":[95,95,100]},{"t":269,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[-0.125,102.005],[-13.125,89.005],[-13.125,-89.005],[-0.125,-102.005],[147,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.8,"y":0},"t":187,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[13,102.005],[0,89.005],[0,-89.005],[13,-102.005],[147,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":249,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[13,102.005],[0,89.005],[0,-89.005],[13,-102.005],[147,-102.005]],"c":true}]},{"t":269,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[-0.125,102.005],[-13.125,89.005],[-13.125,-89.005],[-0.125,-102.005],[147,-102.005]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.549019607843,0.713725490196,0.964705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":286,"st":0,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".yellow100","cl":"yellow100","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[279.438,150,0],"ix":2,"l":2},"a":{"a":0,"k":[-7.562,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":137,"s":[100,100,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":187,"s":[95,95,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.167],"y":[0,0,0]},"t":249,"s":[95,95,100]},{"t":269,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[-0.125,102.005],[-13.125,89.005],[-13.125,-89.005],[-0.125,-102.005],[147,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.8,"y":0},"t":187,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[13,102.005],[0,89.005],[0,-89.005],[13,-102.005],[147,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":249,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[13,102.005],[0,89.005],[0,-89.005],[13,-102.005],[147,-102.005]],"c":true}]},{"t":269,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[-0.125,102.005],[-13.125,89.005],[-13.125,-89.005],[-0.125,-102.005],[147,-102.005]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.98431372549,0.78431372549,0.274509803922,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":286,"st":0,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".grey800","cl":"grey800","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[287,150,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,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[320,204.01],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":13,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.909803921569,0.917647058824,0.929411764706,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":100,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":".black","cl":"black","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[15.3,0],[0,0],[0,15.7],[0,0],[-15.3,0],[0,0],[0,-15.7],[0,0]],"o":[[0,0],[-15.3,0],[0,0],[0,-15.7],[0,0],[15.2,0],[0,0],[0,15.5]],"v":[[178.2,150],[-178.2,150],[-206,121.5],[-206,-121.5],[-178.2,-150],[178.3,-150],[206,-121.5],[206,121.7]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11400,"st":0,"bm":0}]},{"id":"comp_1","nm":"Pre-comp_TaskBar_Transient_LT","fr":60,"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 1","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[237.75,183,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[50,50,100],"ix":6,"l":2}},"ao":0,"ip":102,"op":255,"st":90,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"swipe up 2","parent":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":100,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":110,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":167,"s":[100]},{"t":177,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":100,"s":[-45.3,33.917,0],"to":[5.5,9,0],"ti":[-12,-36,0]},{"i":{"x":0.3,"y":0.3},"o":{"x":0.333,"y":0.333},"t":127,"s":[-12.3,87.917,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[-12.3,87.917,0],"to":[0,0,0],"ti":[-99.967,45.186,0]},{"t":187,"s":[89.5,-76.7,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.1,0.1,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":127,"s":[100,100,100]},{"i":{"x":[0.1,0.1,0.667],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.333],"y":[0,0,0]},"t":137,"s":[80,80,100]},{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.333],"y":[0,0,0]},"t":167,"s":[80,80,100]},{"t":179,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"ef":[{"ty":25,"nm":"Drop Shadow","np":8,"mn":"ADBE Drop Shadow","ix":1,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":63.75,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":8,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":30,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]},{"ty":25,"nm":"Drop Shadow 2","np":8,"mn":"ADBE Drop Shadow","ix":2,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":40,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]}],"shapes":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.5,0.5],"y":[1,1]},"o":{"x":[0.5,0.5],"y":[0,0]},"t":240,"s":[56,56]},{"i":{"x":[0.5,0.5],"y":[1,1]},"o":{"x":[0.5,0.5],"y":[0,0]},"t":262,"s":[56,98]},{"t":288,"s":[56,56]}],"ix":2},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[0,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.8,"y":0},"t":187,"s":[0,14],"to":[0,0],"ti":[0,0]},{"t":189,"s":[0,0]}],"ix":3},"r":{"a":0,"k":67,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,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":[1,1,1,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":102,"op":234,"st":77,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".grey300","cl":"grey300","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-83.044,0.454,0],"ix":2,"l":2},"a":{"a":0,"k":[121.456,227.454,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.454901960784,0.470588235294,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[125.547,231.545],"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":"Layer 18","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.454901960784,0.470588235294,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[125.547,227.455],"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":"Layer 17","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.454901960784,0.470588235294,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[125.547,223.364],"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":"Layer 16","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.454901960784,0.470588235294,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[121.453,231.545],"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":"Layer 15","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.454901960784,0.470588235294,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[121.453,227.455],"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":"Layer 14","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.454901960784,0.470588235294,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[121.453,223.364],"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":"Layer 13","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.454901960784,0.470588235294,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[117.364,231.545],"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":"Layer 12","np":1,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.454901960784,0.470588235294,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[117.364,227.455],"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":"Layer 11","np":1,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.454901960784,0.470588235294,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[117.364,223.364],"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":"Layer 10","np":1,"cix":2,"bm":0,"ix":9,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"green circle matte","parent":20,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84.409,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.807843148708,0.917647063732,0.839215695858,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.807843137255,0.917647058824,0.839215686275,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".green400","cl":"green400","parent":20,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":550,"s":[84.522,0.001,0],"to":[0,-3.333,0],"ti":[0,3.333,0]},{"t":580,"s":[84.522,-19.999,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[80,80,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.35686275363,0.72549021244,0.454901963472,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"green circle matte 2","parent":20,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84.409,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.807843148708,0.917647063732,0.839215695858,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.807843137255,0.917647058824,0.839215686275,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".green400","cl":"green400","parent":20,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":553,"s":[84.522,20.001,0],"to":[0,-3.333,0],"ti":[0,3.333,0]},{"t":583,"s":[84.522,0.001,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[80,80,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.35686275363,0.72549021244,0.454901963472,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".green100","cl":"green100","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84.409,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"blue circle matte 2","parent":20,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[55.772,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.541175991881,0.705881993911,0.972549019608,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".blue400","cl":"blue400","parent":20,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":544,"s":[55.772,0.001,0],"to":[0,-3.333,0],"ti":[0,3.333,0]},{"t":574,"s":[55.772,-19.999,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[80,80,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"blue circle matte","parent":20,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[55.772,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.541175991881,0.705881993911,0.972549019608,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".blue400","cl":"blue400","parent":20,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":547,"s":[55.772,20.001,0],"to":[0,-3.333,0],"ti":[0,3.333,0]},{"t":577,"s":[55.772,0.001,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[80,80,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".blue100","cl":"blue100","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[55.772,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":".yellow400","cl":"yellow400","parent":20,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":177,"s":[0]},{"t":187,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":947,"s":[27.135,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":957,"s":[27.135,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":967,"s":[27.135,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.941176470588,0.596078431373,0.145098039216,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":177,"op":11577,"st":177,"bm":0},{"ddd":0,"ind":15,"ty":4,"nm":".yellow400","cl":"yellow400","parent":20,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":167,"s":[100]},{"t":177,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[27.135,0.001,0],"to":[0.728,-9.542,0],"ti":[-45.478,19.292,0]},{"t":187,"s":[78,-76.749,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.941176470588,0.596078431373,0.145098039216,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":16,"ty":4,"nm":".grey400","cl":"grey400","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":944,"s":[27.135,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":954,"s":[27.135,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":964,"s":[27.135,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.741176470588,0.756862745098,0.776470588235,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":137,"op":252,"st":0,"bm":0},{"ddd":0,"ind":17,"ty":4,"nm":".green400","cl":"green400","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":941,"s":[-1.498,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":951,"s":[-1.498,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":961,"s":[-1.498,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.35686275363,0.72549021244,0.454901963472,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":18,"ty":4,"nm":".red400","cl":"red400","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":938,"s":[-30.134,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":948,"s":[-30.134,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":958,"s":[-30.134,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333337307,0.403921574354,0.360784322023,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":19,"ty":4,"nm":".blue400","cl":"blue400","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":935,"s":[-58.771,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":945,"s":[-58.771,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":955,"s":[-58.771,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":20,"ty":4,"nm":".grey800","cl":"grey800","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[204.5,227.001,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0.001,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":[[-8.284,0],[0,0],[0,-8.284],[0,0],[8.284,0],[0,0],[0,8.284],[0,0]],"o":[[0,0],[8.284,0],[0,0],[0,8.284],[0,0],[-8.284,0],[0,0],[0,-8.284]],"v":[[-86.5,-15],[86.5,-15],[101.5,0],[101.5,0],[86.5,15],[-86.5,15],[-101.5,0],[-101.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.909803921569,0.917647058824,0.929411764706,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0}]},{"id":"comp_2","nm":"Pre-comp_Toggle_LT","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".blue900","cl":"blue900","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":336,"s":[-12.5,0,0],"to":[3.75,0,0],"ti":[-3.75,0,0]},{"i":{"x":0.2,"y":0.2},"o":{"x":0.7,"y":0.7},"t":356,"s":[10,0,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":416,"s":[10,0,0],"to":[-3.75,0,0],"ti":[3.75,0,0]},{"t":436,"s":[-12.5,0,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":336,"s":[{"i":[[-6.627,0],[0,0],[0,-6.627],[0,0],[6.627,0],[0,0],[0,6.627],[0,0]],"o":[[0,0],[6.627,0],[0,0],[0,6.627],[0,0],[-6.627,0],[0,0],[0,-6.627]],"v":[[0,-12],[0,-12],[12,0],[12,0],[0,12],[0,12],[-12,0],[-12,0]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.8,"y":0},"t":356,"s":[{"i":[[-7.732,0],[0,0],[0,-7.732],[0,0],[7.732,0],[0,0],[0,7.732],[0,0]],"o":[[0,0],[7.732,0],[0,0],[0,7.732],[0,0],[-7.732,0],[0,0],[0,-7.732]],"v":[[0,-14],[0,-14],[14,0],[14,0],[0,14],[0,14],[-14,0],[-14,0]],"c":true}]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0},"t":416,"s":[{"i":[[-7.732,0],[0,0],[0,-7.732],[0,0],[7.732,0],[0,0],[0,7.732],[0,0]],"o":[[0,0],[7.732,0],[0,0],[0,7.732],[0,0],[-7.732,0],[0,0],[0,-7.732]],"v":[[0,-14],[0,-14],[14,0],[14,0],[0,14],[0,14],[-14,0],[-14,0]],"c":true}]},{"t":436,"s":[{"i":[[-6.627,0],[0,0],[0,-6.627],[0,0],[6.627,0],[0,0],[0,6.627],[0,0]],"o":[[0,0],[6.627,0],[0,0],[0,6.627],[0,0],[-6.627,0],[0,0],[0,-6.627]],"v":[[0,-12],[0,-12],[12,0],[12,0],[0,12],[0,12],[-12,0],[-12,0]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.090196078431,0.305882352941,0.650980392157,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".blue200","cl":"blue200","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[254.5,133.75,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[50,50,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-8.837,0],[0,0],[0,-8.837],[0,0],[8.837,0],[0,0],[0,8.837],[0,0]],"o":[[0,0],[8.837,0],[0,0],[0,8.837],[0,0],[-8.837,0],[0,0],[0,-8.837]],"v":[[-10,-16],[10,-16],[26,0],[26,0],[10,16],[-10,16],[-26,0],[-26,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.682352941176,0.796078431373,0.980392156863,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".blue100_T","cl":"blue100_T","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,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,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.784,0],[0,1.783],[1.783,0],[0,-1.783]],"o":[[1.783,0],[0,-1.783],[-1.784,0],[0,1.783]],"v":[[-43.755,-12.577],[-40.526,-15.806],[-43.755,-19.035],[-46.984,-15.806]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.823529411765,0.890196078431,0.988235294118,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0.053]],"o":[[0,0.053],[0,0]],"v":[[-44.937,-23.822],[-44.937,-23.822]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[0,0.054]],"o":[[0,0.054],[0,0]],"v":[[-49.458,-21.078],[-49.458,-21.078]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[-0.431,0.269],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0.215],[0,0.215],[0,0],[0,0],[0,0],[0,0],[0,0],[0.431,0.161],[0,0],[0,0],[0,0],[0,0],[0,0],[0.431,-0.269],[0,0],[0,0],[0,0],[0,0],[0,0],[0,-0.216],[-0.054,-0.215],[0,0],[0,0],[0,0],[0,0],[0,0],[-0.431,-0.162],[0,0]],"o":[[0,0],[0,0],[0,0],[0.431,-0.162],[0,0],[0,0],[0,0],[0,0],[0,0],[0.054,-0.215],[0,-0.269],[0,0],[0,0],[0,0],[0,0],[0,0],[-0.377,-0.269],[0,0],[0,0],[0,0],[0,0],[0,0],[-0.431,0.161],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0.215],[0,0.215],[0,0],[0,0],[0,0],[0,0],[0,0],[0.377,0.323],[0,0],[0,0]],"v":[[-45.045,-8.215],[-42.515,-8.215],[-42.138,-10.529],[-41.654,-10.744],[-40.416,-11.444],[-39.986,-11.767],[-37.779,-10.906],[-36.487,-13.112],[-38.371,-14.565],[-38.317,-15.104],[-38.263,-15.803],[-38.317,-16.503],[-38.371,-17.041],[-36.487,-18.494],[-37.779,-20.701],[-39.986,-19.84],[-40.416,-20.163],[-41.654,-20.862],[-42.138,-21.078],[-42.461,-23.392],[-44.991,-23.392],[-45.314,-21.078],[-45.798,-20.862],[-47.036,-20.163],[-47.467,-19.84],[-49.673,-20.701],[-50.965,-18.494],[-49.081,-17.041],[-49.135,-16.503],[-49.189,-15.803],[-49.135,-15.104],[-49.081,-14.565],[-50.965,-13.112],[-49.673,-10.906],[-47.467,-11.767],[-47.036,-11.444],[-45.798,-10.744],[-45.368,-10.529]],"c":true},"ix":2},"nm":"Path 3","mn":"ADBE Vector Shape - Group","hd":false},{"ind":3,"ty":"sh","ix":4,"ks":{"a":0,"k":{"i":[[0,0],[0.054,0]],"o":[[0.054,0],[0,0]],"v":[[-44.991,-7.784],[-44.991,-7.784]],"c":true},"ix":2},"nm":"Path 4","mn":"ADBE Vector Shape - Group","hd":false},{"ind":4,"ty":"sh","ix":5,"ks":{"a":0,"k":{"i":[[0.7,0],[0,0],[0.108,0.7],[0,0],[0.215,0.162],[0,0],[0.323,0.592],[0,0],[-0.484,0.376],[0,0],[0,0.161],[0,0.162],[0,0],[-0.376,0.593],[0,0],[-0.592,-0.215],[0,0],[-0.215,0.162],[0,0],[-0.7,0],[0,0],[-0.107,-0.7],[0,0],[-0.269,-0.162],[0,0],[-0.323,-0.592],[0,0],[0.484,-0.377],[0,0],[0,-0.162],[0,-0.161],[0,0],[0.323,-0.592],[0,0],[0.646,0.215],[0,0],[0.216,-0.162],[0,0]],"o":[[0,0],[-0.7,0],[0,0],[-0.269,-0.108],[0,0],[-0.646,0.215],[0,0],[-0.323,-0.592],[0,0],[0,-0.161],[0,-0.162],[0,0],[-0.538,-0.431],[0,0],[0.323,-0.592],[0,0],[0.215,-0.162],[0,0],[0.053,-0.646],[0,0],[0.699,0],[0,0],[0.269,0.108],[0,0],[0.646,-0.215],[0,0],[0.323,0.592],[0,0],[0,0.161],[0,0.161],[0,0],[0.538,0.431],[0,0],[-0.323,0.592],[0,0],[-0.215,0.161],[0,0],[0,0.538]],"v":[[-42.085,-6.385],[-45.475,-6.385],[-46.821,-7.569],[-47.09,-9.291],[-47.789,-9.722],[-49.458,-9.076],[-51.126,-9.668],[-52.795,-12.574],[-52.472,-14.296],[-51.072,-15.373],[-51.072,-15.803],[-51.072,-16.234],[-52.472,-17.31],[-52.795,-19.033],[-51.126,-21.939],[-49.512,-22.531],[-47.843,-21.831],[-47.144,-22.262],[-46.874,-24.038],[-45.529,-25.168],[-42.138,-25.168],[-40.793,-23.984],[-40.524,-22.262],[-39.77,-21.831],[-38.102,-22.477],[-36.433,-21.885],[-34.765,-18.979],[-35.088,-17.256],[-36.487,-16.18],[-36.487,-15.749],[-36.487,-15.319],[-35.088,-14.243],[-34.765,-12.52],[-36.487,-9.56],[-38.156,-8.968],[-39.824,-9.614],[-40.524,-9.183],[-40.793,-7.407]],"c":true},"ix":2},"nm":"Path 5","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.823529411765,0.890196078431,0.988235294118,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 2","np":6,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[-43.792,-15.776],"ix":2},"a":{"a":0,"k":[-43.792,-15.776],"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":"Group 6","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-8.284,0],[0,0],[0,-8.284],[0,0],[8.284,0],[0,0],[0,8.284],[0,0]],"o":[[0,0],[8.284,0],[0,0],[0,8.284],[0,0],[-8.284,0],[0,0],[0,-8.284]],"v":[[-54,-33],[60,-33],[75,-18],[75,-13],[60,2],[-54,2],[-69,-13],[-69,-18]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.137254901961,0.462745098039,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 5","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11400,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"Taskbar_Transient_LT","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":11400,"st":0,"bm":0}],"markers":[{"tm":94,"cm":"","dr":0},{"tm":249,"cm":"","dr":0},{"tm":474,"cm":"","dr":0}]}
\ No newline at end of file
diff --git a/quickstep/res/raw/taskbar_edu_stashing.json b/quickstep/res/raw/taskbar_edu_stashing.json
new file mode 100644
index 0000000..5515a23
--- /dev/null
+++ b/quickstep/res/raw/taskbar_edu_stashing.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":212,"w":412,"h":300,"nm":"Taskbar_Transient_EDU_1","ddd":0,"assets":[{"id":"comp_0","nm":"Pre-comp_TaskBar_Transient_Step_1","fr":60,"layers":[{"ddd":0,"ind":3,"ty":4,"nm":".grey300","cl":"grey300","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-83.044,0.454,0],"ix":2,"l":2},"a":{"a":0,"k":[121.456,227.454,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[125.547,231.545],"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":"Layer 18","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[125.547,227.455],"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":"Layer 17","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[125.547,223.364],"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":"Layer 16","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[121.453,231.545],"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":"Layer 15","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[121.453,227.455],"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":"Layer 14","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[121.453,223.364],"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":"Layer 13","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[117.364,231.545],"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":"Layer 12","np":1,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[117.364,227.455],"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":"Layer 11","np":1,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[117.364,223.364],"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":"Layer 10","np":1,"cix":2,"bm":0,"ix":9,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"green circle matte","parent":20,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84.409,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.807843148708,0.917647063732,0.839215695858,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.807843137255,0.917647058824,0.839215686275,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".green400","cl":"green400","parent":20,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":550,"s":[84.522,0.001,0],"to":[0,-3.333,0],"ti":[0,3.333,0]},{"t":580,"s":[84.522,-19.999,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[80,80,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.35686275363,0.72549021244,0.454901963472,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"green circle matte 2","parent":20,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84.409,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.807843148708,0.917647063732,0.839215695858,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.807843137255,0.917647058824,0.839215686275,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".green400","cl":"green400","parent":20,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":553,"s":[84.522,20.001,0],"to":[0,-3.333,0],"ti":[0,3.333,0]},{"t":583,"s":[84.522,0.001,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[80,80,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.35686275363,0.72549021244,0.454901963472,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".green100","cl":"green100","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84.409,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.807843148708,0.917647063732,0.839215695858,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.807843137255,0.917647058824,0.839215686275,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"blue circle matte 2","parent":20,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[55.772,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.541175991881,0.705881993911,0.972549019608,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".blue400","cl":"blue400","parent":20,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":544,"s":[55.772,0.001,0],"to":[0,-3.333,0],"ti":[0,3.333,0]},{"t":574,"s":[55.772,-19.999,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[80,80,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"blue circle matte","parent":20,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[55.772,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.541175991881,0.705881993911,0.972549019608,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".blue400","cl":"blue400","parent":20,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":547,"s":[55.772,20.001,0],"to":[0,-3.333,0],"ti":[0,3.333,0]},{"t":577,"s":[55.772,0.001,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[80,80,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".blue100","cl":"blue100","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[55.772,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.823529422283,0.890196084976,0.988235294819,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.823529411765,0.890196078431,0.988235294118,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":".yellow400","cl":"yellow400","parent":20,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":208,"s":[0]},{"t":218,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":947,"s":[27.135,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":957,"s":[27.135,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":967,"s":[27.135,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.988235294819,0.78823530674,0.203921571374,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":208,"op":11577,"st":177,"bm":0},{"ddd":0,"ind":15,"ty":4,"nm":".yellow400","cl":"yellow400","parent":20,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":198,"s":[100]},{"t":208,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":168,"s":[27.135,0.001,0],"to":[0.728,-9.542,0],"ti":[-45.478,19.292,0]},{"t":218,"s":[78,-76.749,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.988235294819,0.78823530674,0.203921571374,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":16,"ty":4,"nm":".grey400","cl":"grey400","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":944,"s":[27.135,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":954,"s":[27.135,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":964,"s":[27.135,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.741176470588,0.756862745098,0.776470588235,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":168,"op":252,"st":0,"bm":0},{"ddd":0,"ind":17,"ty":4,"nm":".green400","cl":"green400","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":941,"s":[-1.498,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":951,"s":[-1.498,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":961,"s":[-1.498,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.35686275363,0.72549021244,0.454901963472,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":18,"ty":4,"nm":".red400","cl":"red400","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":938,"s":[-30.134,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":948,"s":[-30.134,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":958,"s":[-30.134,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333337307,0.403921574354,0.360784322023,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":19,"ty":4,"nm":".blue400","cl":"blue400","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":935,"s":[-58.771,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":945,"s":[-58.771,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":955,"s":[-58.771,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":20,"ty":4,"nm":".grey800","cl":"grey800","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[204.5,227.001,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0.001,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":[[-8.284,0],[0,0],[0,-8.284],[0,0],[8.284,0],[0,0],[0,8.284],[0,0]],"o":[[0,0],[8.284,0],[0,0],[0,8.284],[0,0],[-8.284,0],[0,0],[0,-8.284]],"v":[[-86.5,-15],[86.5,-15],[101.5,0],[101.5,0],[86.5,15],[-86.5,15],[-101.5,0],[-101.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.235294118524,0.250980407,0.262745112181,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11400,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null_MasterScale","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[120,120,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":3,"nm":"Null 1","parent":1,"sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,44,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[50,50,100],"ix":6,"l":2}},"ao":0,"ip":15,"op":73,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"swipe up","parent":2,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":15,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":25,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":60,"s":[100]},{"t":70,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":15,"s":[-48.3,37.417,0],"to":[5.5,9,0],"ti":[-12,-36,0]},{"i":{"x":0.667,"y":0.667},"o":{"x":0.333,"y":0.333},"t":40,"s":[-15.3,91.417,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.45,"y":0},"t":50,"s":[-15.3,91.417,0],"to":[0,0,0],"ti":[0,0,0]},{"t":70,"s":[-15.3,10.917,0],"h":1}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":40,"s":[100,100,100]},{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":50,"s":[80,80,100]},{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":60,"s":[80,80,100]},{"t":70,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"ef":[{"ty":25,"nm":"Drop Shadow","np":8,"mn":"ADBE Drop Shadow","ix":1,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":63.75,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":8,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":30,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]},{"ty":25,"nm":"Drop Shadow 2","np":8,"mn":"ADBE Drop Shadow","ix":2,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":40,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]}],"shapes":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.5,0.5],"y":[1,1]},"o":{"x":[0.5,0.5],"y":[0,0]},"t":52,"s":[56,56]},{"i":{"x":[0.5,0.5],"y":[1,1]},"o":{"x":[0.5,0.5],"y":[0,0]},"t":62,"s":[56,98]},{"t":72,"s":[56,56]}],"ix":2},"p":{"a":1,"k":[{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":52,"s":[0,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":62,"s":[0,-16],"to":[0,0],"ti":[0,0]},{"t":72,"s":[0,0]}],"ix":3},"r":{"a":0,"k":67,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,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":[1,1,1,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":15,"op":73,"st":-10,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".grey800","cl":"grey800","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.1,"y":1},"o":{"x":0.7,"y":0},"t":168,"s":[-0.5,81.5,0],"to":[0,1.667,0],"ti":[0,-1.667,0]},{"t":212,"s":[-0.5,91.5,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.381,0],[0,0],[0,-1.381],[0,0],[1.381,0],[0,0],[0,1.381],[0,0]],"o":[[0,0],[1.381,0],[0,0],[0,1.381],[0,0],[-1.381,0],[0,0],[0,-1.381]],"v":[[-42,-2.5],[42,-2.5],[44.5,0],[44.5,0],[42,2.5],[-42,2.5],[-44.5,0],[-44.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":186,"op":213,"st":89,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"screen_matte","parent":1,"td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":168,"s":[100]},{"t":212,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[81,0,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,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[320,204.01],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":13,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":186,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":0,"nm":"Pre-comp_TaskBar_Transient_Step_1","parent":1,"tt":1,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.1,"y":1},"o":{"x":0.7,"y":0},"t":50,"s":[-1.5,94.501,0],"to":[0,-2.5,0],"ti":[0,2.5,0]},{"i":{"x":0.1,"y":0.1},"o":{"x":0.167,"y":0.167},"t":94,"s":[-1.5,79.501,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.7,"y":0},"t":168,"s":[-1.5,79.501,0],"to":[0,2.5,0],"ti":[0,0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.7,"y":0},"t":212,"s":[-1.5,94.501,0],"to":[0,0,0],"ti":[0,1.167,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":356,"s":[-1.5,79.501,0],"to":[0,-1.167,0],"ti":[0,-1.333,0]},{"i":{"x":0.1,"y":0.1},"o":{"x":0.167,"y":0.167},"t":386,"s":[-1.5,87.501,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":436,"s":[-1.5,87.501,0],"to":[0,-1.333,0],"ti":[0,1.333,0]},{"i":{"x":0.3,"y":0.3},"o":{"x":0.3,"y":0.3},"t":466,"s":[-1.5,79.501,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":474,"s":[-1.5,79.501,0],"to":[-42.083,-12.917,0],"ti":[42.083,12.917,0]},{"t":554,"s":[-254,2.001,0]}],"ix":2,"l":2},"a":{"a":0,"k":[206,227.625,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.7,0.7,0.167],"y":[0,0,0]},"t":50,"s":[70,70,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":94,"s":[100,100,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.7,0.7,0.167],"y":[0,0,0]},"t":168,"s":[100,100,100]},{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.7,0.7,0.167],"y":[0,0,0]},"t":212,"s":[70,70,100]},{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":474,"s":[100,100,100]},{"t":554,"s":[370,370,100]}],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":65,"op":186,"st":65,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".grey800","cl":"grey800","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.9,"y":0},"t":46,"s":[-0.5,91.5,0],"to":[0,-1.667,0],"ti":[0,1.667,0]},{"t":76,"s":[-0.5,81.5,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.381,0],[0,0],[0,-1.381],[0,0],[1.381,0],[0,0],[0,1.381],[0,0]],"o":[[0,0],[1.381,0],[0,0],[0,1.381],[0,0],[-1.381,0],[0,0],[0,-1.381]],"v":[[-42,-2.5],[42,-2.5],[44.5,0],[44.5,0],[42,2.5],[-42,2.5],[-44.5,0],[-44.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":65,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".blue100","cl":"blue100","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-73.438,0,0],"ix":2,"l":2},"a":{"a":0,"k":[-154.438,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":257,"s":[100,100,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":307,"s":[95,95,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.167],"y":[0,0,0]},"t":369,"s":[95,95,100]},{"t":389,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":257,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[13.125,-89.005],[13.125,89.005],[0.125,102.005],[-147,102.005],[-160,89.005],[-160,-89.005],[-147,-102.005],[0.125,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.8,"y":0},"t":307,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[0,-89.005],[0,89.005],[-13,102.005],[-147,102.005],[-160,89.005],[-160,-89.005],[-147,-102.005],[-13,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":369,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[0,-89.005],[0,89.005],[-13,102.005],[-147,102.005],[-160,89.005],[-160,-89.005],[-147,-102.005],[-13,-102.005]],"c":true}]},{"t":389,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[13.125,-89.005],[13.125,89.005],[0.125,102.005],[-147,102.005],[-160,89.005],[-160,-89.005],[-147,-102.005],[0.125,-102.005]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.823529411765,0.890196078431,0.988235294118,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":286,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".blue100","cl":"blue100","parent":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.8],"y":[0]},"t":257,"s":[100]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.8],"y":[0]},"t":307,"s":[0]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.3],"y":[0]},"t":369,"s":[0]},{"t":379,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[73.438,0,0],"ix":2,"l":2},"a":{"a":0,"k":[-7.562,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":257,"s":[100,100,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":307,"s":[95,95,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.167],"y":[0,0,0]},"t":369,"s":[95,95,100]},{"t":389,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":257,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[-0.125,102.005],[-13.125,89.005],[-13.125,-89.005],[-0.125,-102.005],[147,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.8,"y":0},"t":307,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[13,102.005],[0,89.005],[0,-89.005],[13,-102.005],[147,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":369,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[13,102.005],[0,89.005],[0,-89.005],[13,-102.005],[147,-102.005]],"c":true}]},{"t":389,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[-0.125,102.005],[-13.125,89.005],[-13.125,-89.005],[-0.125,-102.005],[147,-102.005]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.823529411765,0.890196078431,0.988235294118,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":286,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".yellow100","cl":"yellow100","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[73.438,0,0],"ix":2,"l":2},"a":{"a":0,"k":[-7.562,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":257,"s":[100,100,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":307,"s":[95,95,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.167],"y":[0,0,0]},"t":369,"s":[95,95,100]},{"t":389,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":257,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[-0.125,102.005],[-13.125,89.005],[-13.125,-89.005],[-0.125,-102.005],[147,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.8,"y":0},"t":307,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[13,102.005],[0,89.005],[0,-89.005],[13,-102.005],[147,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":369,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[13,102.005],[0,89.005],[0,-89.005],[13,-102.005],[147,-102.005]],"c":true}]},{"t":389,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[-0.125,102.005],[-13.125,89.005],[-13.125,-89.005],[-0.125,-102.005],[147,-102.005]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.996078431373,0.937254901961,0.764705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":286,"st":0,"bm":0}],"markers":[{"tm":94,"cm":"","dr":0},{"tm":249,"cm":"","dr":0},{"tm":474,"cm":"","dr":0}]}
\ No newline at end of file
diff --git a/quickstep/res/raw/taskbar_edu_stashing_transient.json b/quickstep/res/raw/taskbar_edu_stashing_transient.json
new file mode 100644
index 0000000..847a607
--- /dev/null
+++ b/quickstep/res/raw/taskbar_edu_stashing_transient.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":94,"w":412,"h":300,"nm":"Taskbar_Transient_Step_1_LT","ddd":0,"assets":[{"id":"comp_0","nm":"Taskbar_Transient_LT","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"press 2","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":381,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":391,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":416,"s":[100]},{"t":426,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[259.25,134,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":406,"s":[50,50,100]},{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":416,"s":[40,40,100]},{"t":426,"s":[50,50,100]}],"ix":6,"l":2}},"ao":0,"ef":[{"ty":25,"nm":"Drop Shadow","np":8,"mn":"ADBE Drop Shadow","ix":1,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":63.75,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":8,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":30,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]},{"ty":25,"nm":"Drop Shadow 2","np":8,"mn":"ADBE Drop Shadow","ix":2,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":40,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]}],"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[56,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":67,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,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":[1,1,1,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.167,"y":0.186},"t":381,"s":[-37,67],"to":[15,-7],"ti":[7,20]},{"t":406,"s":[0,0],"h":1}],"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":"Touch","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":381,"op":437,"st":176,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"press","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":301,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":311,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":336,"s":[100]},{"t":346,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[248,134,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":326,"s":[50,50,100]},{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":336,"s":[40,40,100]},{"t":346,"s":[50,50,100]}],"ix":6,"l":2}},"ao":0,"ef":[{"ty":25,"nm":"Drop Shadow","np":8,"mn":"ADBE Drop Shadow","ix":1,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":63.75,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":8,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":30,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]},{"ty":25,"nm":"Drop Shadow 2","np":8,"mn":"ADBE Drop Shadow","ix":2,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":40,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]}],"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[56,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":67,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,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":[1,1,1,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.167,"y":0.186},"t":301,"s":[-37,67],"to":[15,-7],"ti":[7,20]},{"t":326,"s":[0,0],"h":1}],"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":"Touch","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":301,"op":357,"st":96,"bm":0},{"ddd":0,"ind":3,"ty":3,"nm":"Null 1","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,194,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[50,50,100],"ix":6,"l":2}},"ao":0,"ip":15,"op":73,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"swipe up","parent":3,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":15,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":25,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":60,"s":[100]},{"t":70,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":15,"s":[-48.3,37.417,0],"to":[5.5,9,0],"ti":[-12,-36,0]},{"i":{"x":0.667,"y":0.667},"o":{"x":0.333,"y":0.333},"t":40,"s":[-15.3,91.417,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.45,"y":0},"t":50,"s":[-15.3,91.417,0],"to":[0,0,0],"ti":[0,0,0]},{"t":70,"s":[-15.3,-62.583,0],"h":1}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":40,"s":[100,100,100]},{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":50,"s":[80,80,100]},{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":60,"s":[80,80,100]},{"t":70,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"ef":[{"ty":25,"nm":"Drop Shadow","np":8,"mn":"ADBE Drop Shadow","ix":1,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":63.75,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":8,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":30,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]},{"ty":25,"nm":"Drop Shadow 2","np":8,"mn":"ADBE Drop Shadow","ix":2,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":40,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]}],"shapes":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.5,0.5],"y":[1,1]},"o":{"x":[0.5,0.5],"y":[0,0]},"t":52,"s":[56,56]},{"i":{"x":[0.5,0.5],"y":[1,1]},"o":{"x":[0.5,0.5],"y":[0,0]},"t":62,"s":[56,98]},{"t":72,"s":[56,56]}],"ix":2},"p":{"a":1,"k":[{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":52,"s":[0,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":62,"s":[0,14],"to":[0,0],"ti":[0,0]},{"t":72,"s":[0,0]}],"ix":3},"r":{"a":0,"k":67,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,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":[1,1,1,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":15,"op":73,"st":-10,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"screen_matte","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[287,150,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,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[320,204.01],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":13,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":0,"nm":"Pre-comp_TaskBar_Transient_LT","tt":1,"refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.1,"y":1},"o":{"x":0.7,"y":0},"t":50,"s":[204.5,244.501,0],"to":[0,-2.5,0],"ti":[0,2.5,0]},{"i":{"x":0.1,"y":0.1},"o":{"x":0.167,"y":0.167},"t":94,"s":[204.5,229.501,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":356,"s":[204.5,229.501,0],"to":[0,1.333,0],"ti":[0,-1.333,0]},{"i":{"x":0.1,"y":0.1},"o":{"x":0.167,"y":0.167},"t":386,"s":[204.5,237.501,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":436,"s":[204.5,237.501,0],"to":[0,-1.333,0],"ti":[0,1.333,0]},{"i":{"x":0.3,"y":0.3},"o":{"x":0.3,"y":0.3},"t":466,"s":[204.5,229.501,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":474,"s":[204.5,229.501,0],"to":[-42.083,-12.917,0],"ti":[42.083,12.917,0]},{"t":554,"s":[-48,152.001,0]}],"ix":2,"l":2},"a":{"a":0,"k":[206,227.625,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.7,0.7,0.167],"y":[0,0,0]},"t":50,"s":[70,70,100]},{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":94,"s":[100,100,100]},{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":474,"s":[100,100,100]},{"t":554,"s":[370,370,100]}],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".grey800","cl":"grey800","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.9,"y":0},"t":46,"s":[205.5,241.5,0],"to":[0,-1.667,0],"ti":[0,1.667,0]},{"t":76,"s":[205.5,231.5,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.381,0],[0,0],[0,-1.381],[0,0],[1.381,0],[0,0],[0,1.381],[0,0]],"o":[[0,0],[1.381,0],[0,0],[0,1.381],[0,0],[-1.381,0],[0,0],[0,-1.381]],"v":[[-42,-2.5],[42,-2.5],[44.5,0],[44.5,0],[42,2.5],[-42,2.5],[-44.5,0],[-44.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.909803921569,0.917647058824,0.929411764706,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":65,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":0,"nm":"Pre-comp_Toggle_LT","refId":"comp_2","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":256,"s":[0]},{"i":{"x":[0.316],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":286,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":474,"s":[100]},{"t":554,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":235,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".blue100","cl":"blue100","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":249,"s":[0]},{"t":269,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[287,150,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,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.3,0.3],"y":[0,0]},"t":356,"s":[320,204.01]},{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0,0]},"t":386,"s":[320,173.01]},{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.3,0.3],"y":[0,0]},"t":436,"s":[320,173.01]},{"t":466,"s":[320,204.01]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":12,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.549019607843,0.713725490196,0.964705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":356,"s":[-81,0],"to":[0,-2.542],"ti":[0,2.542]},{"i":{"x":0.1,"y":0.1},"o":{"x":0.167,"y":0.167},"t":386,"s":[-81,-15.25],"to":[0,0],"ti":[0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":436,"s":[-81,-15.25],"to":[0,2.542],"ti":[0,-2.542]},{"t":466,"s":[-81,0]}],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":286,"op":11635,"st":235,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".blue100","cl":"blue100","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[132.562,150,0],"ix":2,"l":2},"a":{"a":0,"k":[-154.438,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":137,"s":[100,100,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":187,"s":[95,95,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.167],"y":[0,0,0]},"t":249,"s":[95,95,100]},{"t":269,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[13.125,-89.005],[13.125,89.005],[0.125,102.005],[-147,102.005],[-160,89.005],[-160,-89.005],[-147,-102.005],[0.125,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.8,"y":0},"t":187,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[0,-89.005],[0,89.005],[-13,102.005],[-147,102.005],[-160,89.005],[-160,-89.005],[-147,-102.005],[-13,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":249,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[0,-89.005],[0,89.005],[-13,102.005],[-147,102.005],[-160,89.005],[-160,-89.005],[-147,-102.005],[-13,-102.005]],"c":true}]},{"t":269,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[13.125,-89.005],[13.125,89.005],[0.125,102.005],[-147,102.005],[-160,89.005],[-160,-89.005],[-147,-102.005],[0.125,-102.005]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.549019607843,0.713725490196,0.964705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":286,"st":0,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":".blue100","cl":"blue100","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.8],"y":[0]},"t":137,"s":[100]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.8],"y":[0]},"t":187,"s":[0]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.3],"y":[0]},"t":249,"s":[0]},{"t":259,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[279.438,150,0],"ix":2,"l":2},"a":{"a":0,"k":[-7.562,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":137,"s":[100,100,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":187,"s":[95,95,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.167],"y":[0,0,0]},"t":249,"s":[95,95,100]},{"t":269,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[-0.125,102.005],[-13.125,89.005],[-13.125,-89.005],[-0.125,-102.005],[147,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.8,"y":0},"t":187,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[13,102.005],[0,89.005],[0,-89.005],[13,-102.005],[147,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":249,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[13,102.005],[0,89.005],[0,-89.005],[13,-102.005],[147,-102.005]],"c":true}]},{"t":269,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[-0.125,102.005],[-13.125,89.005],[-13.125,-89.005],[-0.125,-102.005],[147,-102.005]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.549019607843,0.713725490196,0.964705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":286,"st":0,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".yellow100","cl":"yellow100","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[279.438,150,0],"ix":2,"l":2},"a":{"a":0,"k":[-7.562,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":137,"s":[100,100,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":187,"s":[95,95,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.167],"y":[0,0,0]},"t":249,"s":[95,95,100]},{"t":269,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[-0.125,102.005],[-13.125,89.005],[-13.125,-89.005],[-0.125,-102.005],[147,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.8,"y":0},"t":187,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[13,102.005],[0,89.005],[0,-89.005],[13,-102.005],[147,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":249,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[13,102.005],[0,89.005],[0,-89.005],[13,-102.005],[147,-102.005]],"c":true}]},{"t":269,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[-0.125,102.005],[-13.125,89.005],[-13.125,-89.005],[-0.125,-102.005],[147,-102.005]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.98431372549,0.78431372549,0.274509803922,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":286,"st":0,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".grey800","cl":"grey800","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[287,150,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,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[320,204.01],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":13,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.909803921569,0.917647058824,0.929411764706,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":100,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":".black","cl":"black","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[15.3,0],[0,0],[0,15.7],[0,0],[-15.3,0],[0,0],[0,-15.7],[0,0]],"o":[[0,0],[-15.3,0],[0,0],[0,-15.7],[0,0],[15.2,0],[0,0],[0,15.5]],"v":[[178.2,150],[-178.2,150],[-206,121.5],[-206,-121.5],[-178.2,-150],[178.3,-150],[206,-121.5],[206,121.7]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11400,"st":0,"bm":0}]},{"id":"comp_1","nm":"Pre-comp_TaskBar_Transient_LT","fr":60,"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 1","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[237.75,183,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[50,50,100],"ix":6,"l":2}},"ao":0,"ip":102,"op":255,"st":90,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"swipe up 2","parent":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":100,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":110,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":167,"s":[100]},{"t":177,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":100,"s":[-45.3,33.917,0],"to":[5.5,9,0],"ti":[-12,-36,0]},{"i":{"x":0.3,"y":0.3},"o":{"x":0.333,"y":0.333},"t":127,"s":[-12.3,87.917,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[-12.3,87.917,0],"to":[0,0,0],"ti":[-99.967,45.186,0]},{"t":187,"s":[89.5,-76.7,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.1,0.1,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":127,"s":[100,100,100]},{"i":{"x":[0.1,0.1,0.667],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.333],"y":[0,0,0]},"t":137,"s":[80,80,100]},{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.333],"y":[0,0,0]},"t":167,"s":[80,80,100]},{"t":179,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"ef":[{"ty":25,"nm":"Drop Shadow","np":8,"mn":"ADBE Drop Shadow","ix":1,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":63.75,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":8,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":30,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]},{"ty":25,"nm":"Drop Shadow 2","np":8,"mn":"ADBE Drop Shadow","ix":2,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":40,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]}],"shapes":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.5,0.5],"y":[1,1]},"o":{"x":[0.5,0.5],"y":[0,0]},"t":240,"s":[56,56]},{"i":{"x":[0.5,0.5],"y":[1,1]},"o":{"x":[0.5,0.5],"y":[0,0]},"t":262,"s":[56,98]},{"t":288,"s":[56,56]}],"ix":2},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[0,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.8,"y":0},"t":187,"s":[0,14],"to":[0,0],"ti":[0,0]},{"t":189,"s":[0,0]}],"ix":3},"r":{"a":0,"k":67,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,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":[1,1,1,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":102,"op":234,"st":77,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".grey300","cl":"grey300","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-83.044,0.454,0],"ix":2,"l":2},"a":{"a":0,"k":[121.456,227.454,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.454901960784,0.470588235294,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[125.547,231.545],"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":"Layer 18","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.454901960784,0.470588235294,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[125.547,227.455],"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":"Layer 17","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.454901960784,0.470588235294,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[125.547,223.364],"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":"Layer 16","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.454901960784,0.470588235294,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[121.453,231.545],"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":"Layer 15","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.454901960784,0.470588235294,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[121.453,227.455],"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":"Layer 14","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.454901960784,0.470588235294,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[121.453,223.364],"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":"Layer 13","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.454901960784,0.470588235294,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[117.364,231.545],"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":"Layer 12","np":1,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.454901960784,0.470588235294,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[117.364,227.455],"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":"Layer 11","np":1,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.454901960784,0.470588235294,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[117.364,223.364],"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":"Layer 10","np":1,"cix":2,"bm":0,"ix":9,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"green circle matte","parent":20,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84.409,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.807843148708,0.917647063732,0.839215695858,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.807843137255,0.917647058824,0.839215686275,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".green400","cl":"green400","parent":20,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":550,"s":[84.522,0.001,0],"to":[0,-3.333,0],"ti":[0,3.333,0]},{"t":580,"s":[84.522,-19.999,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[80,80,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.35686275363,0.72549021244,0.454901963472,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"green circle matte 2","parent":20,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84.409,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.807843148708,0.917647063732,0.839215695858,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.807843137255,0.917647058824,0.839215686275,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".green400","cl":"green400","parent":20,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":553,"s":[84.522,20.001,0],"to":[0,-3.333,0],"ti":[0,3.333,0]},{"t":583,"s":[84.522,0.001,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[80,80,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.35686275363,0.72549021244,0.454901963472,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".green100","cl":"green100","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84.409,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"blue circle matte 2","parent":20,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[55.772,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.541175991881,0.705881993911,0.972549019608,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".blue400","cl":"blue400","parent":20,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":544,"s":[55.772,0.001,0],"to":[0,-3.333,0],"ti":[0,3.333,0]},{"t":574,"s":[55.772,-19.999,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[80,80,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"blue circle matte","parent":20,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[55.772,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.541175991881,0.705881993911,0.972549019608,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".blue400","cl":"blue400","parent":20,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":547,"s":[55.772,20.001,0],"to":[0,-3.333,0],"ti":[0,3.333,0]},{"t":577,"s":[55.772,0.001,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[80,80,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".blue100","cl":"blue100","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[55.772,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":".yellow400","cl":"yellow400","parent":20,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":177,"s":[0]},{"t":187,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":947,"s":[27.135,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":957,"s":[27.135,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":967,"s":[27.135,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.941176470588,0.596078431373,0.145098039216,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":177,"op":11577,"st":177,"bm":0},{"ddd":0,"ind":15,"ty":4,"nm":".yellow400","cl":"yellow400","parent":20,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":167,"s":[100]},{"t":177,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[27.135,0.001,0],"to":[0.728,-9.542,0],"ti":[-45.478,19.292,0]},{"t":187,"s":[78,-76.749,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.941176470588,0.596078431373,0.145098039216,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":16,"ty":4,"nm":".grey400","cl":"grey400","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":944,"s":[27.135,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":954,"s":[27.135,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":964,"s":[27.135,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.741176470588,0.756862745098,0.776470588235,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":137,"op":252,"st":0,"bm":0},{"ddd":0,"ind":17,"ty":4,"nm":".green400","cl":"green400","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":941,"s":[-1.498,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":951,"s":[-1.498,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":961,"s":[-1.498,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.35686275363,0.72549021244,0.454901963472,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":18,"ty":4,"nm":".red400","cl":"red400","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":938,"s":[-30.134,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":948,"s":[-30.134,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":958,"s":[-30.134,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333337307,0.403921574354,0.360784322023,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":19,"ty":4,"nm":".blue400","cl":"blue400","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":935,"s":[-58.771,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":945,"s":[-58.771,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":955,"s":[-58.771,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":20,"ty":4,"nm":".grey800","cl":"grey800","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[204.5,227.001,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0.001,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":[[-8.284,0],[0,0],[0,-8.284],[0,0],[8.284,0],[0,0],[0,8.284],[0,0]],"o":[[0,0],[8.284,0],[0,0],[0,8.284],[0,0],[-8.284,0],[0,0],[0,-8.284]],"v":[[-86.5,-15],[86.5,-15],[101.5,0],[101.5,0],[86.5,15],[-86.5,15],[-101.5,0],[-101.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.909803921569,0.917647058824,0.929411764706,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0}]},{"id":"comp_2","nm":"Pre-comp_Toggle_LT","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".blue900","cl":"blue900","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":336,"s":[-12.5,0,0],"to":[3.75,0,0],"ti":[-3.75,0,0]},{"i":{"x":0.2,"y":0.2},"o":{"x":0.7,"y":0.7},"t":356,"s":[10,0,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":416,"s":[10,0,0],"to":[-3.75,0,0],"ti":[3.75,0,0]},{"t":436,"s":[-12.5,0,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":336,"s":[{"i":[[-6.627,0],[0,0],[0,-6.627],[0,0],[6.627,0],[0,0],[0,6.627],[0,0]],"o":[[0,0],[6.627,0],[0,0],[0,6.627],[0,0],[-6.627,0],[0,0],[0,-6.627]],"v":[[0,-12],[0,-12],[12,0],[12,0],[0,12],[0,12],[-12,0],[-12,0]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.8,"y":0},"t":356,"s":[{"i":[[-7.732,0],[0,0],[0,-7.732],[0,0],[7.732,0],[0,0],[0,7.732],[0,0]],"o":[[0,0],[7.732,0],[0,0],[0,7.732],[0,0],[-7.732,0],[0,0],[0,-7.732]],"v":[[0,-14],[0,-14],[14,0],[14,0],[0,14],[0,14],[-14,0],[-14,0]],"c":true}]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0},"t":416,"s":[{"i":[[-7.732,0],[0,0],[0,-7.732],[0,0],[7.732,0],[0,0],[0,7.732],[0,0]],"o":[[0,0],[7.732,0],[0,0],[0,7.732],[0,0],[-7.732,0],[0,0],[0,-7.732]],"v":[[0,-14],[0,-14],[14,0],[14,0],[0,14],[0,14],[-14,0],[-14,0]],"c":true}]},{"t":436,"s":[{"i":[[-6.627,0],[0,0],[0,-6.627],[0,0],[6.627,0],[0,0],[0,6.627],[0,0]],"o":[[0,0],[6.627,0],[0,0],[0,6.627],[0,0],[-6.627,0],[0,0],[0,-6.627]],"v":[[0,-12],[0,-12],[12,0],[12,0],[0,12],[0,12],[-12,0],[-12,0]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.090196078431,0.305882352941,0.650980392157,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".blue200","cl":"blue200","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[254.5,133.75,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[50,50,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-8.837,0],[0,0],[0,-8.837],[0,0],[8.837,0],[0,0],[0,8.837],[0,0]],"o":[[0,0],[8.837,0],[0,0],[0,8.837],[0,0],[-8.837,0],[0,0],[0,-8.837]],"v":[[-10,-16],[10,-16],[26,0],[26,0],[10,16],[-10,16],[-26,0],[-26,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.682352941176,0.796078431373,0.980392156863,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".blue100_T","cl":"blue100_T","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,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,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.784,0],[0,1.783],[1.783,0],[0,-1.783]],"o":[[1.783,0],[0,-1.783],[-1.784,0],[0,1.783]],"v":[[-43.755,-12.577],[-40.526,-15.806],[-43.755,-19.035],[-46.984,-15.806]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.823529411765,0.890196078431,0.988235294118,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0.053]],"o":[[0,0.053],[0,0]],"v":[[-44.937,-23.822],[-44.937,-23.822]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[0,0.054]],"o":[[0,0.054],[0,0]],"v":[[-49.458,-21.078],[-49.458,-21.078]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[-0.431,0.269],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0.215],[0,0.215],[0,0],[0,0],[0,0],[0,0],[0,0],[0.431,0.161],[0,0],[0,0],[0,0],[0,0],[0,0],[0.431,-0.269],[0,0],[0,0],[0,0],[0,0],[0,0],[0,-0.216],[-0.054,-0.215],[0,0],[0,0],[0,0],[0,0],[0,0],[-0.431,-0.162],[0,0]],"o":[[0,0],[0,0],[0,0],[0.431,-0.162],[0,0],[0,0],[0,0],[0,0],[0,0],[0.054,-0.215],[0,-0.269],[0,0],[0,0],[0,0],[0,0],[0,0],[-0.377,-0.269],[0,0],[0,0],[0,0],[0,0],[0,0],[-0.431,0.161],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0.215],[0,0.215],[0,0],[0,0],[0,0],[0,0],[0,0],[0.377,0.323],[0,0],[0,0]],"v":[[-45.045,-8.215],[-42.515,-8.215],[-42.138,-10.529],[-41.654,-10.744],[-40.416,-11.444],[-39.986,-11.767],[-37.779,-10.906],[-36.487,-13.112],[-38.371,-14.565],[-38.317,-15.104],[-38.263,-15.803],[-38.317,-16.503],[-38.371,-17.041],[-36.487,-18.494],[-37.779,-20.701],[-39.986,-19.84],[-40.416,-20.163],[-41.654,-20.862],[-42.138,-21.078],[-42.461,-23.392],[-44.991,-23.392],[-45.314,-21.078],[-45.798,-20.862],[-47.036,-20.163],[-47.467,-19.84],[-49.673,-20.701],[-50.965,-18.494],[-49.081,-17.041],[-49.135,-16.503],[-49.189,-15.803],[-49.135,-15.104],[-49.081,-14.565],[-50.965,-13.112],[-49.673,-10.906],[-47.467,-11.767],[-47.036,-11.444],[-45.798,-10.744],[-45.368,-10.529]],"c":true},"ix":2},"nm":"Path 3","mn":"ADBE Vector Shape - Group","hd":false},{"ind":3,"ty":"sh","ix":4,"ks":{"a":0,"k":{"i":[[0,0],[0.054,0]],"o":[[0.054,0],[0,0]],"v":[[-44.991,-7.784],[-44.991,-7.784]],"c":true},"ix":2},"nm":"Path 4","mn":"ADBE Vector Shape - Group","hd":false},{"ind":4,"ty":"sh","ix":5,"ks":{"a":0,"k":{"i":[[0.7,0],[0,0],[0.108,0.7],[0,0],[0.215,0.162],[0,0],[0.323,0.592],[0,0],[-0.484,0.376],[0,0],[0,0.161],[0,0.162],[0,0],[-0.376,0.593],[0,0],[-0.592,-0.215],[0,0],[-0.215,0.162],[0,0],[-0.7,0],[0,0],[-0.107,-0.7],[0,0],[-0.269,-0.162],[0,0],[-0.323,-0.592],[0,0],[0.484,-0.377],[0,0],[0,-0.162],[0,-0.161],[0,0],[0.323,-0.592],[0,0],[0.646,0.215],[0,0],[0.216,-0.162],[0,0]],"o":[[0,0],[-0.7,0],[0,0],[-0.269,-0.108],[0,0],[-0.646,0.215],[0,0],[-0.323,-0.592],[0,0],[0,-0.161],[0,-0.162],[0,0],[-0.538,-0.431],[0,0],[0.323,-0.592],[0,0],[0.215,-0.162],[0,0],[0.053,-0.646],[0,0],[0.699,0],[0,0],[0.269,0.108],[0,0],[0.646,-0.215],[0,0],[0.323,0.592],[0,0],[0,0.161],[0,0.161],[0,0],[0.538,0.431],[0,0],[-0.323,0.592],[0,0],[-0.215,0.161],[0,0],[0,0.538]],"v":[[-42.085,-6.385],[-45.475,-6.385],[-46.821,-7.569],[-47.09,-9.291],[-47.789,-9.722],[-49.458,-9.076],[-51.126,-9.668],[-52.795,-12.574],[-52.472,-14.296],[-51.072,-15.373],[-51.072,-15.803],[-51.072,-16.234],[-52.472,-17.31],[-52.795,-19.033],[-51.126,-21.939],[-49.512,-22.531],[-47.843,-21.831],[-47.144,-22.262],[-46.874,-24.038],[-45.529,-25.168],[-42.138,-25.168],[-40.793,-23.984],[-40.524,-22.262],[-39.77,-21.831],[-38.102,-22.477],[-36.433,-21.885],[-34.765,-18.979],[-35.088,-17.256],[-36.487,-16.18],[-36.487,-15.749],[-36.487,-15.319],[-35.088,-14.243],[-34.765,-12.52],[-36.487,-9.56],[-38.156,-8.968],[-39.824,-9.614],[-40.524,-9.183],[-40.793,-7.407]],"c":true},"ix":2},"nm":"Path 5","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.823529411765,0.890196078431,0.988235294118,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 2","np":6,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[-43.792,-15.776],"ix":2},"a":{"a":0,"k":[-43.792,-15.776],"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":"Group 6","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-8.284,0],[0,0],[0,-8.284],[0,0],[8.284,0],[0,0],[0,8.284],[0,0]],"o":[[0,0],[8.284,0],[0,0],[0,8.284],[0,0],[-8.284,0],[0,0],[0,-8.284]],"v":[[-54,-33],[60,-33],[75,-18],[75,-13],[60,2],[-54,2],[-69,-13],[-69,-18]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.137254901961,0.462745098039,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 5","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11400,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"Taskbar_Transient_LT","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":11400,"st":0,"bm":0}],"markers":[{"tm":94,"cm":"","dr":0},{"tm":249,"cm":"","dr":0},{"tm":474,"cm":"","dr":0}]}
\ No newline at end of file
diff --git a/quickstep/res/raw/taskbar_edu_suggestions.json b/quickstep/res/raw/taskbar_edu_suggestions.json
new file mode 100644
index 0000000..c8ba29c
--- /dev/null
+++ b/quickstep/res/raw/taskbar_edu_suggestions.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":120,"w":412,"h":300,"nm":"Taskbar_Transient_EDU_4","ddd":0,"assets":[{"id":"comp_0","nm":"Pre-comp_TaskBar_Transient","fr":60,"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 1","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[237.75,183,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[50,50,100],"ix":6,"l":2}},"ao":0,"ip":102,"op":255,"st":90,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"swipe up 2","parent":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":100,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":110,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":167,"s":[100]},{"t":177,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":100,"s":[-45.3,33.917,0],"to":[5.5,9,0],"ti":[-12,-36,0]},{"i":{"x":0.3,"y":0.3},"o":{"x":0.333,"y":0.333},"t":127,"s":[-12.3,87.917,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[-12.3,87.917,0],"to":[0,0,0],"ti":[-99.967,45.186,0]},{"t":187,"s":[89.5,-76.7,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.1,0.1,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":127,"s":[100,100,100]},{"i":{"x":[0.1,0.1,0.667],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.333],"y":[0,0,0]},"t":137,"s":[80,80,100]},{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.333],"y":[0,0,0]},"t":167,"s":[80,80,100]},{"t":179,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"ef":[{"ty":25,"nm":"Drop Shadow","np":8,"mn":"ADBE Drop Shadow","ix":1,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":63.75,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":8,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":30,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]},{"ty":25,"nm":"Drop Shadow 2","np":8,"mn":"ADBE Drop Shadow","ix":2,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":40,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]}],"shapes":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.5,0.5],"y":[1,1]},"o":{"x":[0.5,0.5],"y":[0,0]},"t":240,"s":[56,56]},{"i":{"x":[0.5,0.5],"y":[1,1]},"o":{"x":[0.5,0.5],"y":[0,0]},"t":262,"s":[56,98]},{"t":288,"s":[56,56]}],"ix":2},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[0,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.8,"y":0},"t":187,"s":[0,14],"to":[0,0],"ti":[0,0]},{"t":189,"s":[0,0]}],"ix":3},"r":{"a":0,"k":67,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,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":[1,1,1,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":102,"op":234,"st":77,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".grey300","cl":"grey300","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-83.044,0.454,0],"ix":2,"l":2},"a":{"a":0,"k":[121.456,227.454,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[125.547,231.545],"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":"Layer 18","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[125.547,227.455],"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":"Layer 17","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[125.547,223.364],"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":"Layer 16","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[121.453,231.545],"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":"Layer 15","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[121.453,227.455],"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":"Layer 14","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[121.453,223.364],"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":"Layer 13","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[117.364,231.545],"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":"Layer 12","np":1,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[117.364,227.455],"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":"Layer 11","np":1,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[117.364,223.364],"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":"Layer 10","np":1,"cix":2,"bm":0,"ix":9,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"green circle matte","parent":20,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84.409,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.807843148708,0.917647063732,0.839215695858,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.807843137255,0.917647058824,0.839215686275,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".green400","cl":"green400","parent":20,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":550,"s":[84.522,0.001,0],"to":[0,-3.333,0],"ti":[0,3.333,0]},{"t":580,"s":[84.522,-19.999,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[80,80,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.35686275363,0.72549021244,0.454901963472,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"green circle matte 2","parent":20,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84.409,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.807843148708,0.917647063732,0.839215695858,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.807843137255,0.917647058824,0.839215686275,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".green400","cl":"green400","parent":20,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":553,"s":[84.522,20.001,0],"to":[0,-3.333,0],"ti":[0,3.333,0]},{"t":583,"s":[84.522,0.001,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[80,80,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.35686275363,0.72549021244,0.454901963472,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".green100","cl":"green100","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84.409,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.807843148708,0.917647063732,0.839215695858,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.807843137255,0.917647058824,0.839215686275,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"blue circle matte 2","parent":20,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[55.772,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.541175991881,0.705881993911,0.972549019608,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".blue400","cl":"blue400","parent":20,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":544,"s":[55.772,0.001,0],"to":[0,-3.333,0],"ti":[0,3.333,0]},{"t":574,"s":[55.772,-19.999,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[80,80,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"blue circle matte","parent":20,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[55.772,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.541175991881,0.705881993911,0.972549019608,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".blue400","cl":"blue400","parent":20,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":547,"s":[55.772,20.001,0],"to":[0,-3.333,0],"ti":[0,3.333,0]},{"t":577,"s":[55.772,0.001,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[80,80,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".blue100","cl":"blue100","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[55.772,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.823529422283,0.890196084976,0.988235294819,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.823529411765,0.890196078431,0.988235294118,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":".yellow400","cl":"yellow400","parent":20,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":177,"s":[0]},{"t":187,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":947,"s":[27.135,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":957,"s":[27.135,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":967,"s":[27.135,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.988235294819,0.78823530674,0.203921571374,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":177,"op":11577,"st":177,"bm":0},{"ddd":0,"ind":15,"ty":4,"nm":".yellow400","cl":"yellow400","parent":20,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":167,"s":[100]},{"t":177,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[27.135,0.001,0],"to":[0.728,-9.542,0],"ti":[-45.478,19.292,0]},{"t":187,"s":[78,-76.749,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.988235294819,0.78823530674,0.203921571374,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":16,"ty":4,"nm":".grey400","cl":"grey400","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":944,"s":[27.135,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":954,"s":[27.135,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":964,"s":[27.135,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.741176470588,0.756862745098,0.776470588235,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":137,"op":252,"st":0,"bm":0},{"ddd":0,"ind":17,"ty":4,"nm":".green400","cl":"green400","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":941,"s":[-1.498,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":951,"s":[-1.498,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":961,"s":[-1.498,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.35686275363,0.72549021244,0.454901963472,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":18,"ty":4,"nm":".red400","cl":"red400","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":938,"s":[-30.134,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":948,"s":[-30.134,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":958,"s":[-30.134,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333337307,0.403921574354,0.360784322023,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":19,"ty":4,"nm":".blue400","cl":"blue400","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":935,"s":[-58.771,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":945,"s":[-58.771,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":955,"s":[-58.771,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":20,"ty":4,"nm":".grey800","cl":"grey800","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[204.5,227.001,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0.001,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":[[-8.284,0],[0,0],[0,-8.284],[0,0],[8.284,0],[0,0],[0,8.284],[0,0]],"o":[[0,0],[8.284,0],[0,0],[0,8.284],[0,0],[-8.284,0],[0,0],[0,-8.284]],"v":[[-86.5,-15],[86.5,-15],[101.5,0],[101.5,0],[86.5,15],[-86.5,15],[-101.5,0],[-101.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.235294118524,0.250980407,0.262745112181,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null_MasterScale","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[120,120,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"screen_matte","parent":1,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[81,0,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,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[320,204.01],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":13,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":10878,"st":-522,"bm":0},{"ddd":0,"ind":3,"ty":0,"nm":"Pre-comp_TaskBar_Transient","parent":1,"tt":1,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.1,"y":1},"o":{"x":0.7,"y":0},"t":-472,"s":[-1.5,94.501,0],"to":[0,-2.5,0],"ti":[0,2.5,0]},{"i":{"x":0.1,"y":0.1},"o":{"x":0.167,"y":0.167},"t":-428,"s":[-1.5,79.501,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.1,"y":0.1},"o":{"x":0.3,"y":0.3},"t":-166,"s":[-1.5,79.501,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":0.3},"o":{"x":0.3,"y":0.3},"t":-136,"s":[-1.5,79.501,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.8,"y":0},"t":-128,"s":[-1.5,79.501,0],"to":[0,1.333,0],"ti":[42.083,12.917,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.3,"y":0},"t":-86,"s":[-1.5,87.501,0],"to":[-42.083,-12.917,0],"ti":[42.083,14.25,0]},{"t":-48,"s":[-254,2.001,0]}],"ix":2,"l":2},"a":{"a":0,"k":[206,227.625,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.7,0.7,0.167],"y":[0,0,0]},"t":-472,"s":[70,70,100]},{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":-428,"s":[100,100,100]},{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":-128,"s":[100,100,100]},{"t":-48,"s":[370,370,100]}],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":10878,"st":-522,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".blue100","cl":"blue100","parent":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":-273,"s":[0]},{"t":-253,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[81,0,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,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.3,0.3],"y":[0,0]},"t":-166,"s":[320,204.01]},{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0,0]},"t":-136,"s":[320,173.01]},{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.3,0.3],"y":[0,0]},"t":-86,"s":[320,173.01]},{"t":-56,"s":[320,204.01]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":12,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.823529411765,0.890196078431,0.988235294118,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":-166,"s":[-81,0],"to":[0,-2.542],"ti":[0,2.542]},{"i":{"x":0.1,"y":0.1},"o":{"x":0.167,"y":0.167},"t":-136,"s":[-81,-15.25],"to":[0,0],"ti":[0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":-86,"s":[-81,-15.25],"to":[0,2.542],"ti":[0,-2.542]},{"t":-56,"s":[-81,0]}],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11113,"st":-287,"bm":0}],"markers":[{"tm":94,"cm":"","dr":0},{"tm":249,"cm":"","dr":0},{"tm":474,"cm":"","dr":0}]}
\ No newline at end of file
diff --git a/quickstep/res/raw/taskbar_edu_suggestions_persistent.json b/quickstep/res/raw/taskbar_edu_suggestions_persistent.json
new file mode 100644
index 0000000..86ad8cf
--- /dev/null
+++ b/quickstep/res/raw/taskbar_edu_suggestions_persistent.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":186,"op":360,"w":412,"h":300,"nm":"Taskbar_Persistent_LT_Step_2","ddd":0,"assets":[{"id":"comp_0","nm":"Pre-comp_Taskbar_Persistent_Layers_LT","fr":60,"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 2","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,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":11306,"st":-94,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"screen_matte","parent":1,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[81,0,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,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[320,204.01],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":13,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11306,"st":-94,"bm":0},{"ddd":0,"ind":3,"ty":0,"nm":"Pre-comp_TaskBar_Persistent_LT","parent":1,"tt":1,"refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-1.5,87.892,0],"ix":2,"l":2},"a":{"a":0,"k":[206,227.625,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":11306,"st":-94,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"screen 3","parent":1,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[81,0,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,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[320,204.01],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":13,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":175,"op":11306,"st":-94,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".blue100","cl":"blue100","parent":1,"tt":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":155,"s":[0]},{"t":175,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[81,0.5,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,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[320,174.01],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":13,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.549019607843,0.713725490196,0.964705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,-15.25],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":175,"op":11541,"st":141,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".blue100","cl":"blue100","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-73.438,0,0],"ix":2,"l":2},"a":{"a":0,"k":[-154.438,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":43,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":93,"s":[95,95,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":155,"s":[95,95,100]},{"t":175,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":43,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[13.125,-89.005],[13.125,59.005],[0.125,72.005],[-147,72.005],[-160,59.005],[-160,-89.005],[-147,-102.005],[0.125,-102.005]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.8,"y":0},"t":93,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[0,-89.005],[0,62.689],[-13,75.689],[-147,75.689],[-160,62.689],[-160,-89.005],[-147,-102.005],[-13,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.167,"y":0},"t":155,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[0,-89.005],[0,62.689],[-13,75.689],[-147,75.689],[-160,62.689],[-160,-89.005],[-147,-102.005],[-13,-102.005]],"c":true}]},{"t":175,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[13.125,-89.005],[13.125,59.005],[0.125,72.005],[-147,72.005],[-160,59.005],[-160,-89.005],[-147,-102.005],[0.125,-102.005]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.549019607843,0.713725490196,0.964705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":175,"st":-94,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".blue100","cl":"blue100","parent":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.8],"y":[0]},"t":43,"s":[100]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.8],"y":[0]},"t":93,"s":[0]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":155,"s":[0]},{"t":175,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[73.438,0,0],"ix":2,"l":2},"a":{"a":0,"k":[-7.562,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":43,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":93,"s":[95,95,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":155,"s":[95,95,100]},{"t":175,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":43,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,59.005],[147,72.005],[-0.125,72.005],[-13.125,59.005],[-13.125,-89.005],[-0.125,-102.005],[147,-102.005]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.8,"y":0},"t":93,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,62.689],[147,75.689],[13,75.689],[0,62.689],[0,-89.005],[13,-102.005],[147,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.167,"y":0},"t":155,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,62.689],[147,75.689],[13,75.689],[0,62.689],[0,-89.005],[13,-102.005],[147,-102.005]],"c":true}]},{"t":175,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,59.005],[147,72.005],[-0.125,72.005],[-13.125,59.005],[-13.125,-89.005],[-0.125,-102.005],[147,-102.005]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.549019607843,0.713725490196,0.964705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":175,"st":-94,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".yellow100","cl":"yellow100","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[73.438,0,0],"ix":2,"l":2},"a":{"a":0,"k":[-7.562,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":43,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":93,"s":[95,95,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":155,"s":[95,95,100]},{"t":175,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":43,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,59.005],[147,72.005],[-0.125,72.005],[-13.125,59.005],[-13.125,-89.005],[-0.125,-102.005],[147,-102.005]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.8,"y":0},"t":93,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,62.689],[147,75.689],[13,75.689],[0,62.689],[0,-89.005],[13,-102.005],[147,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.167,"y":0},"t":155,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,62.689],[147,75.689],[13,75.689],[0,62.689],[0,-89.005],[13,-102.005],[147,-102.005]],"c":true}]},{"t":175,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,59.005],[147,72.005],[-0.125,72.005],[-13.125,59.005],[-13.125,-89.005],[-0.125,-102.005],[147,-102.005]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.98431372549,0.78431372549,0.274509803922,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":175,"st":-94,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".grey800","cl":"grey800","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[81,0,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,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[320,204.01],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":13,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.909803921569,0.917647058824,0.929411764706,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11306,"st":-94,"bm":0}]},{"id":"comp_1","nm":"Pre-comp_TaskBar_Persistent_LT","fr":60,"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 1","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[237.75,183,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[50,50,100],"ix":6,"l":2}},"ao":0,"ip":102,"op":255,"st":90,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"swipe up 2","parent":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":100,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":110,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":167,"s":[100]},{"t":177,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":100,"s":[-45.3,33.917,0],"to":[5.5,9,0],"ti":[-12,-36,0]},{"i":{"x":0.3,"y":0.3},"o":{"x":0.333,"y":0.333},"t":127,"s":[-12.3,87.917,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[-12.3,87.917,0],"to":[0,0,0],"ti":[-99.967,45.186,0]},{"t":187,"s":[89.5,-76.7,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.1,0.1,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":127,"s":[100,100,100]},{"i":{"x":[0.1,0.1,0.667],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.333],"y":[0,0,0]},"t":137,"s":[80,80,100]},{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.333],"y":[0,0,0]},"t":167,"s":[80,80,100]},{"t":179,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"ef":[{"ty":25,"nm":"Drop Shadow","np":8,"mn":"ADBE Drop Shadow","ix":1,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":63.75,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":8,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":30,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]},{"ty":25,"nm":"Drop Shadow 2","np":8,"mn":"ADBE Drop Shadow","ix":2,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":40,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]}],"shapes":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.5,0.5],"y":[1,1]},"o":{"x":[0.5,0.5],"y":[0,0]},"t":240,"s":[56,56]},{"i":{"x":[0.5,0.5],"y":[1,1]},"o":{"x":[0.5,0.5],"y":[0,0]},"t":262,"s":[56,98]},{"t":288,"s":[56,56]}],"ix":2},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[0,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.8,"y":0},"t":187,"s":[0,14],"to":[0,0],"ti":[0,0]},{"t":189,"s":[0,0]}],"ix":3},"r":{"a":0,"k":67,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,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":[1,1,1,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":102,"op":234,"st":77,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".grey300","cl":"grey300","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-83.044,0.454,0],"ix":2,"l":2},"a":{"a":0,"k":[121.456,227.454,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.454901960784,0.470588235294,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[125.547,231.545],"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":"Layer 18","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.454901960784,0.470588235294,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[125.547,227.455],"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":"Layer 17","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.454901960784,0.470588235294,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[125.547,223.364],"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":"Layer 16","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.454901960784,0.470588235294,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[121.453,231.545],"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":"Layer 15","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.454901960784,0.470588235294,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[121.453,227.455],"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":"Layer 14","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.454901960784,0.470588235294,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[121.453,223.364],"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":"Layer 13","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.454901960784,0.470588235294,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[117.364,231.545],"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":"Layer 12","np":1,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.454901960784,0.470588235294,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[117.364,227.455],"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":"Layer 11","np":1,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.454901960784,0.470588235294,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[117.364,223.364],"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":"Layer 10","np":1,"cix":2,"bm":0,"ix":9,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"green circle matte","parent":20,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84.409,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.807843148708,0.917647063732,0.839215695858,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.807843137255,0.917647058824,0.839215686275,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".green400","cl":"green400","parent":20,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":362,"s":[84.522,0.001,0],"to":[0,-3.333,0],"ti":[0,3.333,0]},{"t":392,"s":[84.522,-19.999,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[80,80,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.35686275363,0.72549021244,0.454901963472,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"green circle matte 2","parent":20,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84.409,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.807843148708,0.917647063732,0.839215695858,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.807843137255,0.917647058824,0.839215686275,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".green400","cl":"green400","parent":20,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":365,"s":[84.522,20.001,0],"to":[0,-3.333,0],"ti":[0,3.333,0]},{"t":395,"s":[84.522,0.001,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[80,80,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.35686275363,0.72549021244,0.454901963472,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".green100","cl":"green100","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84.409,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"blue circle matte 2","parent":20,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[55.772,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.541175991881,0.705881993911,0.972549019608,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".blue400","cl":"blue400","parent":20,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":356,"s":[55.772,0.001,0],"to":[0,-3.333,0],"ti":[0,3.333,0]},{"t":386,"s":[55.772,-19.999,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[80,80,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"blue circle matte","parent":20,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[55.772,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.541175991881,0.705881993911,0.972549019608,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".blue400","cl":"blue400","parent":20,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":359,"s":[55.772,20.001,0],"to":[0,-3.333,0],"ti":[0,3.333,0]},{"t":389,"s":[55.772,0.001,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[80,80,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".blue100","cl":"blue100","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[55.772,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":".yellow400","cl":"yellow400","parent":20,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":177,"s":[0]},{"t":187,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":947,"s":[27.135,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":957,"s":[27.135,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":967,"s":[27.135,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.941176470588,0.596078431373,0.145098039216,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":177,"op":11577,"st":177,"bm":0},{"ddd":0,"ind":15,"ty":4,"nm":".yelllow400","cl":"yelllow400","parent":20,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":167,"s":[100]},{"t":177,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[27.135,0.001,0],"to":[0.728,-9.542,0],"ti":[-45.478,19.292,0]},{"t":187,"s":[78,-76.749,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.941176470588,0.596078431373,0.145098039216,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":16,"ty":4,"nm":".grey400","cl":"grey400","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":944,"s":[27.135,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":954,"s":[27.135,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":964,"s":[27.135,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.741176470588,0.756862745098,0.776470588235,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":137,"op":252,"st":0,"bm":0},{"ddd":0,"ind":17,"ty":4,"nm":".green400","cl":"green400","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":941,"s":[-1.498,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":951,"s":[-1.498,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":961,"s":[-1.498,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.35686275363,0.72549021244,0.454901963472,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":18,"ty":4,"nm":".red400","cl":"red400","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":938,"s":[-30.134,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":948,"s":[-30.134,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":958,"s":[-30.134,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333337307,0.403921574354,0.360784322023,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":19,"ty":4,"nm":".blue400","cl":"blue400","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":935,"s":[-58.771,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":945,"s":[-58.771,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":955,"s":[-58.771,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.61568627451,0.964705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":20,"ty":4,"nm":".grey800","cl":"grey800","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[204.5,227.001,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0.001,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":[[-8.284,0],[0,0],[0,-8.284],[0,0],[8.284,0],[0,0],[0,8.284],[0,0]],"o":[[0,0],[8.284,0],[0,0],[0,8.284],[0,0],[-8.284,0],[0,0],[0,-8.284]],"v":[[-86.5,-15],[86.5,-15],[101.5,0],[101.5,0],[86.5,15],[-86.5,15],[-101.5,0],[-101.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.909803921569,0.917647058824,0.929411764706,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"screen","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[287,150,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,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[320,204.01],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":13,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11306,"st":-94,"bm":0},{"ddd":0,"ind":2,"ty":0,"nm":"Pre-comp_Taskbar_Persistent_Layers_LT","tt":1,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":205,"s":[206,150,0],"to":[-41.417,-45.917,0],"ti":[41.417,45.917,0]},{"t":285,"s":[-42.5,-125.5,0]}],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":205,"s":[100,100,100]},{"t":285,"s":[370,370,100]}],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".white","cl":"white","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[15.3,0],[0,0],[0,15.7],[0,0],[-15.3,0],[0,0],[0,-15.7],[0,0]],"o":[[0,0],[-15.3,0],[0,0],[0,-15.7],[0,0],[15.2,0],[0,0],[0,15.5]],"v":[[178.2,150],[-178.2,150],[-206,121.5],[-206,-121.5],[-178.2,-150],[178.3,-150],[206,-121.5],[206,121.7]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11306,"st":-94,"bm":0}],"markers":[{"tm":154,"cm":"","dr":0},{"tm":360,"cm":"","dr":0}]}
\ No newline at end of file
diff --git a/quickstep/res/raw/taskbar_edu_suggestions_transient.json b/quickstep/res/raw/taskbar_edu_suggestions_transient.json
new file mode 100644
index 0000000..b2624b0
--- /dev/null
+++ b/quickstep/res/raw/taskbar_edu_suggestions_transient.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":548,"op":628,"w":412,"h":300,"nm":"Taskbar_Transient_Step_4_LT","ddd":0,"assets":[{"id":"comp_0","nm":"Taskbar_Transient_LT","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"press 2","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":381,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":391,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":416,"s":[100]},{"t":426,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[259.25,134,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":406,"s":[50,50,100]},{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":416,"s":[40,40,100]},{"t":426,"s":[50,50,100]}],"ix":6,"l":2}},"ao":0,"ef":[{"ty":25,"nm":"Drop Shadow","np":8,"mn":"ADBE Drop Shadow","ix":1,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":63.75,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":8,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":30,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]},{"ty":25,"nm":"Drop Shadow 2","np":8,"mn":"ADBE Drop Shadow","ix":2,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":40,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]}],"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[56,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":67,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,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":[1,1,1,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.167,"y":0.186},"t":381,"s":[-37,67],"to":[15,-7],"ti":[7,20]},{"t":406,"s":[0,0],"h":1}],"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":"Touch","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":381,"op":437,"st":176,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"press","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":301,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":311,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":336,"s":[100]},{"t":346,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[248,134,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":326,"s":[50,50,100]},{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":336,"s":[40,40,100]},{"t":346,"s":[50,50,100]}],"ix":6,"l":2}},"ao":0,"ef":[{"ty":25,"nm":"Drop Shadow","np":8,"mn":"ADBE Drop Shadow","ix":1,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":63.75,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":8,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":30,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]},{"ty":25,"nm":"Drop Shadow 2","np":8,"mn":"ADBE Drop Shadow","ix":2,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":40,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]}],"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[56,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":67,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,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":[1,1,1,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.167,"y":0.186},"t":301,"s":[-37,67],"to":[15,-7],"ti":[7,20]},{"t":326,"s":[0,0],"h":1}],"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":"Touch","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":301,"op":357,"st":96,"bm":0},{"ddd":0,"ind":3,"ty":3,"nm":"Null 1","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,194,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[50,50,100],"ix":6,"l":2}},"ao":0,"ip":15,"op":73,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"swipe up","parent":3,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":15,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":25,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":60,"s":[100]},{"t":70,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":15,"s":[-48.3,37.417,0],"to":[5.5,9,0],"ti":[-12,-36,0]},{"i":{"x":0.667,"y":0.667},"o":{"x":0.333,"y":0.333},"t":40,"s":[-15.3,91.417,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.45,"y":0},"t":50,"s":[-15.3,91.417,0],"to":[0,0,0],"ti":[0,0,0]},{"t":70,"s":[-15.3,-62.583,0],"h":1}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":40,"s":[100,100,100]},{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":50,"s":[80,80,100]},{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":60,"s":[80,80,100]},{"t":70,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"ef":[{"ty":25,"nm":"Drop Shadow","np":8,"mn":"ADBE Drop Shadow","ix":1,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":63.75,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":8,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":30,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]},{"ty":25,"nm":"Drop Shadow 2","np":8,"mn":"ADBE Drop Shadow","ix":2,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":40,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]}],"shapes":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.5,0.5],"y":[1,1]},"o":{"x":[0.5,0.5],"y":[0,0]},"t":52,"s":[56,56]},{"i":{"x":[0.5,0.5],"y":[1,1]},"o":{"x":[0.5,0.5],"y":[0,0]},"t":62,"s":[56,98]},{"t":72,"s":[56,56]}],"ix":2},"p":{"a":1,"k":[{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":52,"s":[0,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":62,"s":[0,14],"to":[0,0],"ti":[0,0]},{"t":72,"s":[0,0]}],"ix":3},"r":{"a":0,"k":67,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,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":[1,1,1,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":15,"op":73,"st":-10,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"screen_matte","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[287,150,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,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[320,204.01],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":13,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":0,"nm":"Pre-comp_TaskBar_Transient_LT","tt":1,"refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.1,"y":1},"o":{"x":0.7,"y":0},"t":50,"s":[204.5,244.501,0],"to":[0,-2.5,0],"ti":[0,2.5,0]},{"i":{"x":0.1,"y":0.1},"o":{"x":0.167,"y":0.167},"t":94,"s":[204.5,229.501,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":356,"s":[204.5,229.501,0],"to":[0,1.333,0],"ti":[0,-1.333,0]},{"i":{"x":0.1,"y":0.1},"o":{"x":0.167,"y":0.167},"t":386,"s":[204.5,237.501,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":436,"s":[204.5,237.501,0],"to":[0,-1.333,0],"ti":[0,1.333,0]},{"i":{"x":0.3,"y":0.3},"o":{"x":0.3,"y":0.3},"t":466,"s":[204.5,229.501,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":474,"s":[204.5,229.501,0],"to":[-42.083,-12.917,0],"ti":[42.083,12.917,0]},{"t":554,"s":[-48,152.001,0]}],"ix":2,"l":2},"a":{"a":0,"k":[206,227.625,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.7,0.7,0.167],"y":[0,0,0]},"t":50,"s":[70,70,100]},{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":94,"s":[100,100,100]},{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":474,"s":[100,100,100]},{"t":554,"s":[370,370,100]}],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".grey800","cl":"grey800","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.9,"y":0},"t":46,"s":[205.5,241.5,0],"to":[0,-1.667,0],"ti":[0,1.667,0]},{"t":76,"s":[205.5,231.5,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.381,0],[0,0],[0,-1.381],[0,0],[1.381,0],[0,0],[0,1.381],[0,0]],"o":[[0,0],[1.381,0],[0,0],[0,1.381],[0,0],[-1.381,0],[0,0],[0,-1.381]],"v":[[-42,-2.5],[42,-2.5],[44.5,0],[44.5,0],[42,2.5],[-42,2.5],[-44.5,0],[-44.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.909803921569,0.917647058824,0.929411764706,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":65,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":0,"nm":"Pre-comp_Toggle_LT","refId":"comp_2","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":256,"s":[0]},{"i":{"x":[0.316],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":286,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":474,"s":[100]},{"t":554,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":235,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".blue100","cl":"blue100","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":249,"s":[0]},{"t":269,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[287,150,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,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.3,0.3],"y":[0,0]},"t":356,"s":[320,204.01]},{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0,0]},"t":386,"s":[320,173.01]},{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.3,0.3],"y":[0,0]},"t":436,"s":[320,173.01]},{"t":466,"s":[320,204.01]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":12,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.549019607843,0.713725490196,0.964705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":356,"s":[-81,0],"to":[0,-2.542],"ti":[0,2.542]},{"i":{"x":0.1,"y":0.1},"o":{"x":0.167,"y":0.167},"t":386,"s":[-81,-15.25],"to":[0,0],"ti":[0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":436,"s":[-81,-15.25],"to":[0,2.542],"ti":[0,-2.542]},{"t":466,"s":[-81,0]}],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":286,"op":11635,"st":235,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".blue100","cl":"blue100","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[132.562,150,0],"ix":2,"l":2},"a":{"a":0,"k":[-154.438,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":137,"s":[100,100,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":187,"s":[95,95,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.167],"y":[0,0,0]},"t":249,"s":[95,95,100]},{"t":269,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[13.125,-89.005],[13.125,89.005],[0.125,102.005],[-147,102.005],[-160,89.005],[-160,-89.005],[-147,-102.005],[0.125,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.8,"y":0},"t":187,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[0,-89.005],[0,89.005],[-13,102.005],[-147,102.005],[-160,89.005],[-160,-89.005],[-147,-102.005],[-13,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":249,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[0,-89.005],[0,89.005],[-13,102.005],[-147,102.005],[-160,89.005],[-160,-89.005],[-147,-102.005],[-13,-102.005]],"c":true}]},{"t":269,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[13.125,-89.005],[13.125,89.005],[0.125,102.005],[-147,102.005],[-160,89.005],[-160,-89.005],[-147,-102.005],[0.125,-102.005]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.549019607843,0.713725490196,0.964705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":286,"st":0,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":".blue100","cl":"blue100","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.8],"y":[0]},"t":137,"s":[100]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.8],"y":[0]},"t":187,"s":[0]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.3],"y":[0]},"t":249,"s":[0]},{"t":259,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[279.438,150,0],"ix":2,"l":2},"a":{"a":0,"k":[-7.562,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":137,"s":[100,100,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":187,"s":[95,95,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.167],"y":[0,0,0]},"t":249,"s":[95,95,100]},{"t":269,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[-0.125,102.005],[-13.125,89.005],[-13.125,-89.005],[-0.125,-102.005],[147,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.8,"y":0},"t":187,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[13,102.005],[0,89.005],[0,-89.005],[13,-102.005],[147,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":249,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[13,102.005],[0,89.005],[0,-89.005],[13,-102.005],[147,-102.005]],"c":true}]},{"t":269,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[-0.125,102.005],[-13.125,89.005],[-13.125,-89.005],[-0.125,-102.005],[147,-102.005]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.549019607843,0.713725490196,0.964705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":286,"st":0,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".yellow100","cl":"yellow100","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[279.438,150,0],"ix":2,"l":2},"a":{"a":0,"k":[-7.562,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.3,0.3,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":137,"s":[100,100,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.167],"y":[0,0,0]},"t":187,"s":[95,95,100]},{"i":{"x":[0.1,0.1,0.833],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.167],"y":[0,0,0]},"t":249,"s":[95,95,100]},{"t":269,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[-0.125,102.005],[-13.125,89.005],[-13.125,-89.005],[-0.125,-102.005],[147,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.8,"y":0},"t":187,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[13,102.005],[0,89.005],[0,-89.005],[13,-102.005],[147,-102.005]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":249,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[13,102.005],[0,89.005],[0,-89.005],[13,-102.005],[147,-102.005]],"c":true}]},{"t":269,"s":[{"i":[[0,-7.18],[0,0],[7.18,0],[0,0],[0,7.18],[0,0],[-7.18,0],[0,0]],"o":[[0,0],[0,7.18],[0,0],[-7.18,0],[0,0],[0,-7.18],[0,0],[7.18,0]],"v":[[160,-89.005],[160,89.005],[147,102.005],[-0.125,102.005],[-13.125,89.005],[-13.125,-89.005],[-0.125,-102.005],[147,-102.005]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.98431372549,0.78431372549,0.274509803922,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":286,"st":0,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".grey800","cl":"grey800","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[287,150,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,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[320,204.01],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":13,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.909803921569,0.917647058824,0.929411764706,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-81,0],"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":100,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":".black","cl":"black","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[15.3,0],[0,0],[0,15.7],[0,0],[-15.3,0],[0,0],[0,-15.7],[0,0]],"o":[[0,0],[-15.3,0],[0,0],[0,-15.7],[0,0],[15.2,0],[0,0],[0,15.5]],"v":[[178.2,150],[-178.2,150],[-206,121.5],[-206,-121.5],[-178.2,-150],[178.3,-150],[206,-121.5],[206,121.7]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11400,"st":0,"bm":0}]},{"id":"comp_1","nm":"Pre-comp_TaskBar_Transient_LT","fr":60,"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 1","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[237.75,183,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[50,50,100],"ix":6,"l":2}},"ao":0,"ip":102,"op":255,"st":90,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"swipe up 2","parent":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":100,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":110,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":167,"s":[100]},{"t":177,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":100,"s":[-45.3,33.917,0],"to":[5.5,9,0],"ti":[-12,-36,0]},{"i":{"x":0.3,"y":0.3},"o":{"x":0.333,"y":0.333},"t":127,"s":[-12.3,87.917,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[-12.3,87.917,0],"to":[0,0,0],"ti":[-99.967,45.186,0]},{"t":187,"s":[89.5,-76.7,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.1,0.1,0.667],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.333],"y":[0,0,0]},"t":127,"s":[100,100,100]},{"i":{"x":[0.1,0.1,0.667],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.333],"y":[0,0,0]},"t":137,"s":[80,80,100]},{"i":{"x":[0.5,0.5,0.667],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.333],"y":[0,0,0]},"t":167,"s":[80,80,100]},{"t":179,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"ef":[{"ty":25,"nm":"Drop Shadow","np":8,"mn":"ADBE Drop Shadow","ix":1,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":63.75,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":8,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":30,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]},{"ty":25,"nm":"Drop Shadow 2","np":8,"mn":"ADBE Drop Shadow","ix":2,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":40,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]}],"shapes":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.5,0.5],"y":[1,1]},"o":{"x":[0.5,0.5],"y":[0,0]},"t":240,"s":[56,56]},{"i":{"x":[0.5,0.5],"y":[1,1]},"o":{"x":[0.5,0.5],"y":[0,0]},"t":262,"s":[56,98]},{"t":288,"s":[56,56]}],"ix":2},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[0,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.8,"y":0},"t":187,"s":[0,14],"to":[0,0],"ti":[0,0]},{"t":189,"s":[0,0]}],"ix":3},"r":{"a":0,"k":67,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,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":[1,1,1,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":102,"op":234,"st":77,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".grey300","cl":"grey300","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-83.044,0.454,0],"ix":2,"l":2},"a":{"a":0,"k":[121.456,227.454,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.454901960784,0.470588235294,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[125.547,231.545],"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":"Layer 18","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.454901960784,0.470588235294,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[125.547,227.455],"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":"Layer 17","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.454901960784,0.470588235294,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[125.547,223.364],"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":"Layer 16","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.454901960784,0.470588235294,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[121.453,231.545],"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":"Layer 15","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.454901960784,0.470588235294,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[121.453,227.455],"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":"Layer 14","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.454901960784,0.470588235294,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[121.453,223.364],"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":"Layer 13","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.454901960784,0.470588235294,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[117.364,231.545],"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":"Layer 12","np":1,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.454901960784,0.470588235294,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[117.364,227.455],"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":"Layer 11","np":1,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.753,0],[0,-0.753],[0.753,0],[0,0.753]],"o":[[0.753,0],[0,0.753],[-0.753,0],[0,-0.753]],"v":[[0,-1.364],[1.364,0],[0,1.364],[-1.364,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.454901960784,0.470588235294,0.486274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[117.364,223.364],"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":"Layer 10","np":1,"cix":2,"bm":0,"ix":9,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"green circle matte","parent":20,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84.409,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.807843148708,0.917647063732,0.839215695858,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.807843137255,0.917647058824,0.839215686275,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".green400","cl":"green400","parent":20,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":550,"s":[84.522,0.001,0],"to":[0,-3.333,0],"ti":[0,3.333,0]},{"t":580,"s":[84.522,-19.999,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[80,80,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.35686275363,0.72549021244,0.454901963472,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"green circle matte 2","parent":20,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84.409,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.807843148708,0.917647063732,0.839215695858,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.807843137255,0.917647058824,0.839215686275,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".green400","cl":"green400","parent":20,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":553,"s":[84.522,20.001,0],"to":[0,-3.333,0],"ti":[0,3.333,0]},{"t":583,"s":[84.522,0.001,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[80,80,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.35686275363,0.72549021244,0.454901963472,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".green100","cl":"green100","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84.409,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"blue circle matte 2","parent":20,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[55.772,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.541175991881,0.705881993911,0.972549019608,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".blue400","cl":"blue400","parent":20,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":544,"s":[55.772,0.001,0],"to":[0,-3.333,0],"ti":[0,3.333,0]},{"t":574,"s":[55.772,-19.999,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[80,80,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"blue circle matte","parent":20,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[55.772,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.541175991881,0.705881993911,0.972549019608,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".blue400","cl":"blue400","parent":20,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":547,"s":[55.772,20.001,0],"to":[0,-3.333,0],"ti":[0,3.333,0]},{"t":577,"s":[55.772,0.001,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[80,80,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".blue100","cl":"blue100","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[55.772,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.443,0],[0,-4.443],[4.443,0],[0,4.443]],"o":[[4.443,0],[0,4.443],[-4.443,0],[0,-4.443]],"v":[[0,-8.045],[8.045,0],[0,8.045],[-8.045,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"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":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":".yellow400","cl":"yellow400","parent":20,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":177,"s":[0]},{"t":187,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":947,"s":[27.135,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":957,"s":[27.135,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":967,"s":[27.135,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.941176470588,0.596078431373,0.145098039216,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":177,"op":11577,"st":177,"bm":0},{"ddd":0,"ind":15,"ty":4,"nm":".yellow400","cl":"yellow400","parent":20,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":167,"s":[100]},{"t":177,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":137,"s":[27.135,0.001,0],"to":[0.728,-9.542,0],"ti":[-45.478,19.292,0]},{"t":187,"s":[78,-76.749,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.941176470588,0.596078431373,0.145098039216,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":16,"ty":4,"nm":".grey400","cl":"grey400","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":944,"s":[27.135,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":954,"s":[27.135,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":964,"s":[27.135,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.741176470588,0.756862745098,0.776470588235,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":137,"op":252,"st":0,"bm":0},{"ddd":0,"ind":17,"ty":4,"nm":".green400","cl":"green400","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":941,"s":[-1.498,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":951,"s":[-1.498,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":961,"s":[-1.498,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.35686275363,0.72549021244,0.454901963472,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":18,"ty":4,"nm":".red400","cl":"red400","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":938,"s":[-30.134,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":948,"s":[-30.134,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":958,"s":[-30.134,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333337307,0.403921574354,0.360784322023,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":19,"ty":4,"nm":".blue400","cl":"blue400","parent":20,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":935,"s":[-58.771,0.001,0],"to":[0,-0.625,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":945,"s":[-58.771,-3.749,0],"to":[0,0,0],"ti":[0,-0.625,0]},{"t":955,"s":[-58.771,0.001,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.272,0],[0,-5.272],[5.272,0],[0,5.272]],"o":[[5.272,0],[0,5.272],[-5.272,0],[0,-5.272]],"v":[[0,-9.545],[9.545,0],[0,9.545],[-9.545,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":20,"ty":4,"nm":".grey800","cl":"grey800","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[204.5,227.001,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0.001,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":[[-8.284,0],[0,0],[0,-8.284],[0,0],[8.284,0],[0,0],[0,8.284],[0,0]],"o":[[0,0],[8.284,0],[0,0],[0,8.284],[0,0],[-8.284,0],[0,0],[0,-8.284]],"v":[[-86.5,-15],[86.5,-15],[101.5,0],[101.5,0],[86.5,15],[-86.5,15],[-101.5,0],[-101.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.909803921569,0.917647058824,0.929411764706,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":65,"op":11400,"st":0,"bm":0}]},{"id":"comp_2","nm":"Pre-comp_Toggle_LT","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".blue900","cl":"blue900","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":336,"s":[-12.5,0,0],"to":[3.75,0,0],"ti":[-3.75,0,0]},{"i":{"x":0.2,"y":0.2},"o":{"x":0.7,"y":0.7},"t":356,"s":[10,0,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":416,"s":[10,0,0],"to":[-3.75,0,0],"ti":[3.75,0,0]},{"t":436,"s":[-12.5,0,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.8,"y":0},"t":336,"s":[{"i":[[-6.627,0],[0,0],[0,-6.627],[0,0],[6.627,0],[0,0],[0,6.627],[0,0]],"o":[[0,0],[6.627,0],[0,0],[0,6.627],[0,0],[-6.627,0],[0,0],[0,-6.627]],"v":[[0,-12],[0,-12],[12,0],[12,0],[0,12],[0,12],[-12,0],[-12,0]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.8,"y":0},"t":356,"s":[{"i":[[-7.732,0],[0,0],[0,-7.732],[0,0],[7.732,0],[0,0],[0,7.732],[0,0]],"o":[[0,0],[7.732,0],[0,0],[0,7.732],[0,0],[-7.732,0],[0,0],[0,-7.732]],"v":[[0,-14],[0,-14],[14,0],[14,0],[0,14],[0,14],[-14,0],[-14,0]],"c":true}]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0},"t":416,"s":[{"i":[[-7.732,0],[0,0],[0,-7.732],[0,0],[7.732,0],[0,0],[0,7.732],[0,0]],"o":[[0,0],[7.732,0],[0,0],[0,7.732],[0,0],[-7.732,0],[0,0],[0,-7.732]],"v":[[0,-14],[0,-14],[14,0],[14,0],[0,14],[0,14],[-14,0],[-14,0]],"c":true}]},{"t":436,"s":[{"i":[[-6.627,0],[0,0],[0,-6.627],[0,0],[6.627,0],[0,0],[0,6.627],[0,0]],"o":[[0,0],[6.627,0],[0,0],[0,6.627],[0,0],[-6.627,0],[0,0],[0,-6.627]],"v":[[0,-12],[0,-12],[12,0],[12,0],[0,12],[0,12],[-12,0],[-12,0]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.090196078431,0.305882352941,0.650980392157,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".blue200","cl":"blue200","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[254.5,133.75,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[50,50,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-8.837,0],[0,0],[0,-8.837],[0,0],[8.837,0],[0,0],[0,8.837],[0,0]],"o":[[0,0],[8.837,0],[0,0],[0,8.837],[0,0],[-8.837,0],[0,0],[0,-8.837]],"v":[[-10,-16],[10,-16],[26,0],[26,0],[10,16],[-10,16],[-26,0],[-26,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.682352941176,0.796078431373,0.980392156863,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".blue100_T","cl":"blue100_T","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,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,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.784,0],[0,1.783],[1.783,0],[0,-1.783]],"o":[[1.783,0],[0,-1.783],[-1.784,0],[0,1.783]],"v":[[-43.755,-12.577],[-40.526,-15.806],[-43.755,-19.035],[-46.984,-15.806]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.823529411765,0.890196078431,0.988235294118,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0.053]],"o":[[0,0.053],[0,0]],"v":[[-44.937,-23.822],[-44.937,-23.822]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[0,0.054]],"o":[[0,0.054],[0,0]],"v":[[-49.458,-21.078],[-49.458,-21.078]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[-0.431,0.269],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0.215],[0,0.215],[0,0],[0,0],[0,0],[0,0],[0,0],[0.431,0.161],[0,0],[0,0],[0,0],[0,0],[0,0],[0.431,-0.269],[0,0],[0,0],[0,0],[0,0],[0,0],[0,-0.216],[-0.054,-0.215],[0,0],[0,0],[0,0],[0,0],[0,0],[-0.431,-0.162],[0,0]],"o":[[0,0],[0,0],[0,0],[0.431,-0.162],[0,0],[0,0],[0,0],[0,0],[0,0],[0.054,-0.215],[0,-0.269],[0,0],[0,0],[0,0],[0,0],[0,0],[-0.377,-0.269],[0,0],[0,0],[0,0],[0,0],[0,0],[-0.431,0.161],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0.215],[0,0.215],[0,0],[0,0],[0,0],[0,0],[0,0],[0.377,0.323],[0,0],[0,0]],"v":[[-45.045,-8.215],[-42.515,-8.215],[-42.138,-10.529],[-41.654,-10.744],[-40.416,-11.444],[-39.986,-11.767],[-37.779,-10.906],[-36.487,-13.112],[-38.371,-14.565],[-38.317,-15.104],[-38.263,-15.803],[-38.317,-16.503],[-38.371,-17.041],[-36.487,-18.494],[-37.779,-20.701],[-39.986,-19.84],[-40.416,-20.163],[-41.654,-20.862],[-42.138,-21.078],[-42.461,-23.392],[-44.991,-23.392],[-45.314,-21.078],[-45.798,-20.862],[-47.036,-20.163],[-47.467,-19.84],[-49.673,-20.701],[-50.965,-18.494],[-49.081,-17.041],[-49.135,-16.503],[-49.189,-15.803],[-49.135,-15.104],[-49.081,-14.565],[-50.965,-13.112],[-49.673,-10.906],[-47.467,-11.767],[-47.036,-11.444],[-45.798,-10.744],[-45.368,-10.529]],"c":true},"ix":2},"nm":"Path 3","mn":"ADBE Vector Shape - Group","hd":false},{"ind":3,"ty":"sh","ix":4,"ks":{"a":0,"k":{"i":[[0,0],[0.054,0]],"o":[[0.054,0],[0,0]],"v":[[-44.991,-7.784],[-44.991,-7.784]],"c":true},"ix":2},"nm":"Path 4","mn":"ADBE Vector Shape - Group","hd":false},{"ind":4,"ty":"sh","ix":5,"ks":{"a":0,"k":{"i":[[0.7,0],[0,0],[0.108,0.7],[0,0],[0.215,0.162],[0,0],[0.323,0.592],[0,0],[-0.484,0.376],[0,0],[0,0.161],[0,0.162],[0,0],[-0.376,0.593],[0,0],[-0.592,-0.215],[0,0],[-0.215,0.162],[0,0],[-0.7,0],[0,0],[-0.107,-0.7],[0,0],[-0.269,-0.162],[0,0],[-0.323,-0.592],[0,0],[0.484,-0.377],[0,0],[0,-0.162],[0,-0.161],[0,0],[0.323,-0.592],[0,0],[0.646,0.215],[0,0],[0.216,-0.162],[0,0]],"o":[[0,0],[-0.7,0],[0,0],[-0.269,-0.108],[0,0],[-0.646,0.215],[0,0],[-0.323,-0.592],[0,0],[0,-0.161],[0,-0.162],[0,0],[-0.538,-0.431],[0,0],[0.323,-0.592],[0,0],[0.215,-0.162],[0,0],[0.053,-0.646],[0,0],[0.699,0],[0,0],[0.269,0.108],[0,0],[0.646,-0.215],[0,0],[0.323,0.592],[0,0],[0,0.161],[0,0.161],[0,0],[0.538,0.431],[0,0],[-0.323,0.592],[0,0],[-0.215,0.161],[0,0],[0,0.538]],"v":[[-42.085,-6.385],[-45.475,-6.385],[-46.821,-7.569],[-47.09,-9.291],[-47.789,-9.722],[-49.458,-9.076],[-51.126,-9.668],[-52.795,-12.574],[-52.472,-14.296],[-51.072,-15.373],[-51.072,-15.803],[-51.072,-16.234],[-52.472,-17.31],[-52.795,-19.033],[-51.126,-21.939],[-49.512,-22.531],[-47.843,-21.831],[-47.144,-22.262],[-46.874,-24.038],[-45.529,-25.168],[-42.138,-25.168],[-40.793,-23.984],[-40.524,-22.262],[-39.77,-21.831],[-38.102,-22.477],[-36.433,-21.885],[-34.765,-18.979],[-35.088,-17.256],[-36.487,-16.18],[-36.487,-15.749],[-36.487,-15.319],[-35.088,-14.243],[-34.765,-12.52],[-36.487,-9.56],[-38.156,-8.968],[-39.824,-9.614],[-40.524,-9.183],[-40.793,-7.407]],"c":true},"ix":2},"nm":"Path 5","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.823529411765,0.890196078431,0.988235294118,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 2","np":6,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[-43.792,-15.776],"ix":2},"a":{"a":0,"k":[-43.792,-15.776],"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":"Group 6","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11400,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,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,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-8.284,0],[0,0],[0,-8.284],[0,0],[8.284,0],[0,0],[0,8.284],[0,0]],"o":[[0,0],[8.284,0],[0,0],[0,8.284],[0,0],[-8.284,0],[0,0],[0,-8.284]],"v":[[-54,-33],[60,-33],[75,-18],[75,-13],[60,2],[-54,2],[-69,-13],[-69,-18]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.137254901961,0.462745098039,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"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":"Group 5","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11400,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"Taskbar_Transient_LT","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":11400,"st":0,"bm":0}],"markers":[{"tm":94,"cm":"","dr":0},{"tm":249,"cm":"","dr":0},{"tm":474,"cm":"","dr":0}]}
\ No newline at end of file
diff --git a/quickstep/res/values-af/strings.xml b/quickstep/res/values-af/strings.xml
index e834157..a090e66 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,25 +77,31 @@
<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>
- <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_button_hint" msgid="2395219947744706291">"Tik op die tuisknoppie om na jou tuisskerm toe te gaan"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"Jy is gereed om jou <xliff:g id="DEVICE">%1$s</xliff:g> te begin gebruik"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"toestel"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Stelselnavigasie-instellings"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Deel"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Kies nog ’n app as jy verdeelde skerm wil gebruik"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Jou organisasie laat nie hierdie program toe nie"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Kanselleer"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Slaan oor"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Draai skerm"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Taakbalkopvoeding"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Taakbalkopvoeding het verskyn"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Taakbalkopvoeding is toegemaak"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Gebruik die taakbalk om tussen programme te wissel"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Sleep na die kant om twee programme gelyktydig te gebruik"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Raak en hou om die taakbalk te versteek"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Kortswiep op om die taakbalk te wys"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Volgende"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Terug"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Maak toe"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Onlangs"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Kennisgewings"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Kitsinstellings"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Taakbalk"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigasiebalk"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Skuif na links bo"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Skuif na regs onder"</string>
</resources>
diff --git a/quickstep/res/values-am/strings.xml b/quickstep/res/values-am/strings.xml
index 342bbe2..6b63deb 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,25 +77,31 @@
<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_description" msgid="6350320429953234580">"ስልክዎን መጠቀም ለመጀመር ዝግጁ ነዎት"</string>
- <string name="allset_description_tablet" msgid="7332070270570039247">"ጡባዊዎን መጠቀም ለመጀመር ዝግጁ ነዎት"</string>
+ <string name="allset_button_hint" msgid="2395219947744706291">"ወደ መነሻ ማያ ገጽዎ ለመሄድ የመነሻ አዝራሩን መታ ያድርጉ"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"የእርስዎን <xliff:g id="DEVICE">%1$s</xliff:g> መጠቀም ለመጀመር ዝግጁ ነዎት"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"መሣሪያ"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"የስርዓት አሰሳ ቅንብሮች"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"አጋራ"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"የተከፈለ ማያ ገጽን ለመቀበል ሌላ መተግበሪያ ይምረጡ"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"ይቅር"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"ዝለል"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"ማያ ገጹን አዙር"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"የተግባር አሞሌ ትምህርት"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"የተግባር አሞሌ ትምህርት ይታያል"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"የተግባር አሞሌ ትምህርት ተዘግቷል"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"መተግበሪያዎችን ለመቀየር የተግባር አሞሌውን ይጠቀሙ"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"በአንድ ጊዜ ሁለት መተግበሪያዎችን ለመጠቀም ወደ ጎን ይጎትቱ"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"የተግባር አሞሌውን ለመደበቅ ነክተው ይያዙት"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"የተግባር አሞሌውን ለማሳየት አጭር ወደ ላይ ያንሸራትቱ"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"ቀጣይ"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"ተመለስ"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"ዝጋ"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"የቅርብ ጊዜዎቹ"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"ማሳወቂያዎች"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"ፈጣን ቅንብሮች"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"የተግባር አሞሌ"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"የአሰሳ አሞሌ"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ወደ ላይ/ግራ ይውሰዱ"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ወደ ታች/ቀኝ ይውሰዱ"</string>
</resources>
diff --git a/quickstep/res/values-ar/strings.xml b/quickstep/res/values-ar/strings.xml
index f15fcd4..ed98f69 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,25 +77,31 @@
<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_description" msgid="6350320429953234580">"يمكنك الآن بدء استخدام هاتفك."</string>
- <string name="allset_description_tablet" msgid="7332070270570039247">"يمكنك الآن بدء استخدام جهازك اللوحي."</string>
+ <string name="allset_button_hint" msgid="2395219947744706291">"انقر على زر الشاشة الرئيسية للانتقال إلى الشاشة الرئيسية."</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"يمكنك الآن بدء استخدام \"<xliff:g id="DEVICE">%1$s</xliff:g>\"."</string>
+ <string name="default_device_name" msgid="6660656727127422487">"الجهاز"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"إعدادات التنقّل داخل النظام"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"مشاركة"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"اختَر تطبيقًا آخر لاستخدام وضع تقسيم الشاشة."</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"إلغاء"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"التخطي"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"تدوير الشاشة"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"التعريف بشريط التطبيقات"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"ظهرت لوحة تعليم استخدام شريط المهام."</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"تم إغلاق لوحة تعليم استخدام شريط المهام."</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"يمكنك استخدام شريط المهام للتبديل بين التطبيقات."</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"اسحبه إلى جانب الشاشة لاستخدام تطبيقين في آنٍ واحد."</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"انقر مع الاستمرار لإخفاء شريط المهام."</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"مرِّر سريعًا للأعلى تمريرة قصيرة لإظهار شريط التطبيقات."</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"الشاشة التالية"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"رجوع"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"إغلاق"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"الأحدث"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"الإشعارات"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"إعدادات سريعة"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"شريط التطبيقات"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"شريط التنقل"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"الانتقال إلى يمين الشاشة أو أعلاها"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"الانتقال إلى يسار الشاشة أو أسفلها"</string>
</resources>
diff --git a/quickstep/res/values-as/strings.xml b/quickstep/res/values-as/strings.xml
index b33abed..a9be2c3 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,25 +77,31 @@
<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_description" msgid="6350320429953234580">"আপুনি আপোনাৰ ফ’নটো ব্যৱহাৰ কৰিবলৈ সাজু"</string>
- <string name="allset_description_tablet" msgid="7332070270570039247">"আপুনি আপোনাৰ টেবলেটটো ব্যৱহাৰ কৰিবলৈ সাজু"</string>
+ <string name="allset_button_hint" msgid="2395219947744706291">"আপোনাৰ গৃহ স্ক্ৰীনলৈ যাবলৈ গৃহপৃষ্ঠা বুটামটো টিপক"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"আপুনি আপোনাৰ <xliff:g id="DEVICE">%1$s</xliff:g>টো ব্যৱহাৰ কৰিবলৈ সাজু"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"ডিভাইচ"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"ছিষ্টেম নেভিগেশ্বনৰ ছেটিং"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"শ্বেয়াৰ কৰক"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"বিভাজিত স্ক্ৰীন ব্যৱহাৰ কৰিবলৈ অন্য এটা এপ্ বাছক"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"বাতিল কৰক"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"এৰি যাওক"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"স্ক্ৰীনখন ঘূৰাওক"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"টাস্কবাৰৰ শিক্ষা"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"টাস্কবাৰৰ শিক্ষাৰ পেনেলটো প্ৰদর্শিত হৈছে"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"টাস্কবাৰৰ শিক্ষাৰ পেনেলটো বন্ধ হৈছে"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"এপ্ সলনি কৰিবলৈ টাস্কবাৰডাল ব্যৱহাৰ কৰক"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"এবাৰতে দুটা এপ্ ব্যৱহাৰ কৰিবলৈ কাষলৈ টানি আনি এৰক"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"টাস্কবাৰডাল লুকুৱাবলৈ স্পৰ্শ কৰি ধৰি ৰাখক"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"টাস্কবাৰ দেখুৱাবলৈ সামান্য পৰিমাণে ওপৰলৈ ছোৱাইপ কৰক"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"পৰৱৰ্তী"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"উভতি যাওক"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"বন্ধ কৰক"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"শেহতীয়া"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"জাননী"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"ক্ষিপ্ৰ ছেটিং"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"টাস্কবাৰ"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"নেভিগেশ্বনৰ দণ্ড"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ওপৰৰ বাঁওফাললৈ নিয়ক"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"তলৰ সোঁফাললৈ নিয়ক"</string>
</resources>
diff --git a/quickstep/res/values-az/strings.xml b/quickstep/res/values-az/strings.xml
index 07e29bb..a755d28 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,25 +77,31 @@
<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>
- <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_button_hint" msgid="2395219947744706291">"Əsas ekranınıza keçmək üçün əsas düyməyə toxunun"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"<xliff:g id="DEVICE">%1$s</xliff:g> cihazından istifadəyə başlamağa hazırsınız"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"cihaz"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Sistem naviqasiya ayarları"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Paylaşın"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Bölünmüş ekrandan istifadə üçün başqa tətbiq seçin"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Bu əməliyyata tətbiq və ya təşkilatınız tərəfindən icazə verilmir"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Ləğv edin"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Ötürün"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Ekranı fırladın"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Tapşırıq panelində təhsil"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Tapşırıq panelindəki təlim bölməsi görünür"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Tapşırıq panelindəki təlim bölməsi bağlanıb"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Tətbiqləri keçirmək üçün tapşırıq panelindən istifadə edin"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Eyni anda iki tətbiqi istifadə etmək üçün yan tərəfə çəkin"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Tapşırıq panelini toxunub saxlamaqla gizlədin"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Tapşırıq panelini göstərmək üçün azca yuxarı sürüşdürün"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Sonra"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Geri"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Bağlayın"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Sonuncular"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Bildirişlər"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Sürətli Ayarlar"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Tapşırıq paneli"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Naviqasiya paneli"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Yuxarı/sola köçürün"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Aşağı/sağa köçürün"</string>
</resources>
diff --git a/quickstep/res/values-b+sr+Latn/strings.xml b/quickstep/res/values-b+sr+Latn/strings.xml
index 42023d3..8022979 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,25 +77,31 @@
<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>
- <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_button_hint" msgid="2395219947744706291">"Dodirnite dugme Početak da bisti išli na početni ekran"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"Spremni ste da počnete da koristite <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"uređaj"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Podešavanja kretanja kroz sistem"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Deli"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Odaberite drugu aplikaciju za podeljeni ekran"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Aplikacija ili organizacija ne dozvoljavaju ovu radnju"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Otkaži"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Preskoči"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Rotirajte ekran"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Uputstva na traci zadataka"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Edukativno okno iz trake zadataka se pojavilo"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Edukativno okno iz trake zadataka je zatvoreno"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Koristite traku zadataka da biste menjali aplikacije"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Prevucite na stranu da koristite dve aplikacije odjednom"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Dodirnite i zadržite za skrivanje trake zadataka"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Nakratko prevucite nagore da biste prikazali traku zadataka"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Dalje"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Nazad"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Zatvori"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Nedavno"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Obaveštenja"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Brza podešavanja"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Traka zadataka"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Traka za navigaciju"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Premesti gore levo"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Premesti dole desno"</string>
</resources>
diff --git a/quickstep/res/values-be/strings.xml b/quickstep/res/values-be/strings.xml
index dc4dc65..b951c7a 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,25 +77,31 @@
<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_description" msgid="6350320429953234580">"Вы можаце пачаць карыстанне тэлефонам"</string>
- <string name="allset_description_tablet" msgid="7332070270570039247">"Вы можаце пачаць карыстанне планшэтам"</string>
+ <string name="allset_button_hint" msgid="2395219947744706291">"Каб перайсці на галоўны экран, націсніце кнопку галоўнага экрана"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"Вы можаце пачаць карыстанне прыладай \"<xliff:g id="DEVICE">%1$s</xliff:g>\""</string>
+ <string name="default_device_name" msgid="6660656727127422487">"прылада"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Налады навігацыі ў сістэме"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Абагуліць"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Каб падзяліць экран, выберыце іншую праграму"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Скасаваць"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Прапусціць"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Павярнуць экран"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Інфармацыя пра панэль задач"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"З\'явілася панэль навучання на панэлі задач"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Панэль навучання на панэлі задач закрыта"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Выкарыстоўвайце панэль задач для пераключэння праграм"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Перацягніце ўбок, каб адначасова скарыстаць дзве праграмы"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Націсніце і ўтрымлівайце, каб схаваць панэль задач"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Хутка правядзіце пальцам уверх, каб убачыць панэль задач"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Далей"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Назад"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Закрыць"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Нядаўнія"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Апавяшчэнні"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Хуткія налады"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Панэль задач"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Панэль навігацыі"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Перамясціць уверх/улева"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Перамясціць уніз/управа"</string>
</resources>
diff --git a/quickstep/res/values-bg/strings.xml b/quickstep/res/values-bg/strings.xml
index 560a03b..4900627 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,25 +77,31 @@
<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_description" msgid="6350320429953234580">"Можете да започнете да използвате телефона си"</string>
- <string name="allset_description_tablet" msgid="7332070270570039247">"Можете да започнете да използвате таблета си"</string>
+ <string name="allset_button_hint" msgid="2395219947744706291">"Докоснете бутона „Начало“, за да преминете към началния екран"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"Можете да започнете да използвате <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"устройството"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Настройки за навигиране в системата"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Споделяне"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"За разделен екран изберете още едно приложение"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Отказ"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Пропускане"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Завъртане на екрана"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Урок за лентата на задачите"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Показва се урокът за лентата на задачите"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Урокът за лентата на задачите бе затворен"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Използвайте лентата на задачите за превключване между прил."</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Плъзнете встрани, за да използвате едновременно 2 приложения"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Докоснете и задръжте, за да скриете лентата на задачите"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Прекарайте пръст нагоре, за да видите лентата на задачите"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Напред"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Назад"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Затваряне"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Скорошни"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Известия"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Бързи настройки"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Лента на задачите"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Лента за навигация"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Преместване горе/вляво"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Преместване долу/вдясно"</string>
</resources>
diff --git a/quickstep/res/values-bn/strings.xml b/quickstep/res/values-bn/strings.xml
index d4774a7..992cce1 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,25 +77,31 @@
<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_description" msgid="6350320429953234580">"এবারে আপনি ফোন ব্যবহার করতে পারবেন"</string>
- <string name="allset_description_tablet" msgid="7332070270570039247">"এবারে আপনি ট্যাবলেট ব্যবহার করতে পারবেন"</string>
+ <string name="allset_button_hint" msgid="2395219947744706291">"আপনার হোম স্ক্রিনে যাওয়ার জন্য হোম বোতামে ট্যাপ করুন"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"<xliff:g id="DEVICE">%1$s</xliff:g> ব্যবহার শুরু করার জন্য আপনি রেডি"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"ডিভাইস"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"সিস্টেম নেভিগেশন সেটিংস"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"শেয়ার করুন"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"স্প্লিট স্ক্রিন ব্যবহার করতে অন্য অ্যাপ বেছে নিন"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"বাতিল করুন"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"এড়িয়ে যান"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"স্ক্রিন ঘোরান"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"টাস্কবার এডুকেশন"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"টাস্কবার এডুকেশন দেখানো হয়েছে"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"টাস্কবার এডুকেশন বন্ধ করা আছে"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"অ্যাপ পাল্টানোর জন্য টাস্কবার ব্যবহার করুন"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"একসাথে দুটি অ্যাপ ব্যবহার করতে পাশে টেনে আনুন"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"টাস্কবার লুকানোর জন্য টাচ করে ধরে থাকুন"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"টাস্কবার দেখানোর জন্য শর্ট সোয়াইপ করুন"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"পরবর্তী"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"ফিরুন"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"বন্ধ করুন"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"সম্প্রতি"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"বিজ্ঞপ্তি"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"দ্রুত সেটিংস"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"টাস্কবার"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"নেভিগেশন বার"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"উপরে/বাঁদিকে সরান"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"নিচে/ডানদিকে সরান"</string>
</resources>
diff --git a/quickstep/res/values-bs/strings.xml b/quickstep/res/values-bs/strings.xml
index 3743e24..477e5fa 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,25 +77,31 @@
<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>
- <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_button_hint" msgid="2395219947744706291">"Dodirnite dugme za početni ekran da odete napočetni ekran"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"Sve je spremno da počnete koristiti uređaj <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"uređaj"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Postavke navigiranja sistemom"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Dijeli"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Odaberite drugu apl. da koristite podijeljeni ekran"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Ovu radnju ne dozvoljava aplikacija ili vaša organizacija"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Otkaži"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Preskoči"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Rotiranje ekrana"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Edukacija o traci zadataka"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Edukacija o programskoj traci je prikazana"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Edukacija o programskoj traci je zatvorena"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Koristite programsku traku da promijenite aplikacije"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Prevucite u stranu da istovremeno koristite dvije aplikacije"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Dodirnite i držite da sakrijete programsku traku"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Nakratko prevucite nagore da prikažete traku zadataka"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Naprijed"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Nazad"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Zatvori"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Nedavno"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Obavještenja"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Brze postavke"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Traka zadataka"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigaciona traka"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Premjesti gore lijevo"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Premjesti dolje desno"</string>
</resources>
diff --git a/quickstep/res/values-ca/strings.xml b/quickstep/res/values-ca/strings.xml
index 9400fba..0eb529b 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,25 +77,31 @@
<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>
- <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_button_hint" msgid="2395219947744706291">"Toca el botó d\'inici per anar a la pantalla d\'inici"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"Ja pots començar a utilitzar <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"dispositiu"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Configuració de navegació del sistema"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Comparteix"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Tria una altra app per utilitzar pantalla dividida"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"L\'aplicació o la teva organització no permeten aquesta acció"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Cancel·la"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Omet"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Gira la pantalla"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Informació sobre Barra de tasques"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Ha aparegut el tauler educatiu de la barra de tasques"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"S\'ha tancat el tauler educatiu de la barra de tasques"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Utilitza la barra de tasques per canviar d\'aplicació"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Arrossega al costat per utilitzar dues aplicacions alhora"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Mantén premut per amagar la barra de tasques"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Llisca una mica cap amunt per mostrar la barra de tasques"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Següent"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Enrere"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Tanca"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Recents"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Notificacions"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Config. ràpida"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Barra de tasques"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Barra de navegació"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mou a la part superior o a l\'esquerra"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mou a la part inferior o a la dreta"</string>
</resources>
diff --git a/quickstep/res/values-cs/strings.xml b/quickstep/res/values-cs/strings.xml
index cb49a35..23a98cb 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,25 +77,31 @@
<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>
- <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_button_hint" msgid="2395219947744706291">"Klepnutím na tlačítko plochy se vrátíte na plochu"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"Jste připraveni začít používat <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"zařízení"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Nastavení navigace v systému"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Sdílet"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Vyberte podporovanou aplikaci"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Aplikace nebo organizace zakazuje tuto akci"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Zrušit"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Přeskočit"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Otočit obrazovku"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Informace o panelu aplikací"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Zobrazila se výuka k hlavnímu panelu"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Výuka k hlavnímu panelu byla zavřena"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Aplikace lze přepínat pomocí hlavního panelu"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Po přetažení na stranu lze používat dvě aplikace současně"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Hlavní panel můžete skrýt podržením"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Krátkým přejetím nahoru zobrazíte panel aplikací"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Další"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Zpět"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Zavřít"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Poslední"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Oznámení"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Rychlé nastavení"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Panel aplikací"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigační panel"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Přesunout doleva nahoru"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Přesunout doprava dolů"</string>
</resources>
diff --git a/quickstep/res/values-da/strings.xml b/quickstep/res/values-da/strings.xml
index 621006b..ce1bdf8 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,25 +77,31 @@
<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>
- <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_button_hint" msgid="2395219947744706291">"Tryk på knappen Hjem for at gå til din startskærm"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"Du er klar til at bruge din <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"enhed"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Indstillinger for systemnavigation"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Del"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Vælg en anden app for at bruge opdelt skærm"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Appen eller din organisation tillader ikke denne handling"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Annuller"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Spring over"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Roter skærm"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Oplysninger om proceslinjen"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Vejledningen om proceslinjen blev åbnet"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Vejledningen om proceslinjen blev lukket"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Skift mellem apps ved hjælp af proceslinjen"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Træk til siden for at bruge to apps samtidig"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Du kan skjule proceslinjen ved at holde fingeren nede"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Stryg hurtigt opad for at se proceslinjen"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Næste"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Tilbage"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Luk"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Seneste"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Notifikationer"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Kvikmenu"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Proceslinje"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigationslinje"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Flyt til toppen eller venstre side"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Flyt til bunden eller højre side"</string>
</resources>
diff --git a/quickstep/res/values-de/strings.xml b/quickstep/res/values-de/strings.xml
index d19c23b..78faeb6 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,25 +77,31 @@
<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>
- <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_button_hint" msgid="2395219947744706291">"Startbildschirmtaste drücken, um zum Startbildschirm zu gehen"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"Du kannst dein <xliff:g id="DEVICE">%1$s</xliff:g> jetzt verwenden"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"Gerät"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Einstellungen der Systemsteuerung"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Teilen"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Für geteilten Bildschirm andere App auswählen"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Abbrechen"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Überspringen"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Bildschirm drehen"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Informationen zur Taskleiste"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Anleitung für Taskleiste eingeblendet"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Anleitung für Taskleiste geschlossen"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Über die Taskleiste zwischen Apps wechseln"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Zur Seite ziehen, um zwei Apps gleichzeitig zu verwenden"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Gedrückt halten, um die Taskleiste auszublenden"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Kurz nach oben wischen, um die Taskleiste anzuzeigen"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Weiter"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Zurück"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Schließen"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Letzte Apps"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Benachrichtigungen"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Schnelleinstellungen"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Taskleiste"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigationsleiste"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Nach oben / Nach links verschieben"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Nach unten / Nach rechts verschieben"</string>
</resources>
diff --git a/quickstep/res/values-el/strings.xml b/quickstep/res/values-el/strings.xml
index 2c6702b..d064401 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,25 +77,31 @@
<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_description" msgid="6350320429953234580">"Είστε έτοιμοι να ξεκινήσετε να χρησιμοποιείτε το τηλέφωνό σας"</string>
- <string name="allset_description_tablet" msgid="7332070270570039247">"Είστε έτοιμοι να ξεκινήσετε να χρησιμοποιείτε το tablet."</string>
+ <string name="allset_button_hint" msgid="2395219947744706291">"Πατήστε το κουμπί αρχικής οθόνης για να μεταβείτε στην αρχική οθόνη"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"Είστε έτοιμοι να ξεκινήσετε να χρησιμοποιείτε το/τη <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"συσκευή"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Ρυθμίσεις πλοήγησης συστήματος"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Κοινοποίηση"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Επιλέξτε άλλη εφαρμογή για διαχωρισμό οθόνης"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Ακύρωση"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Παράβλεψη"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Περιστροφή οθόνης"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Πληροφορίες χρήσης της Γραμμής εργαλείων"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Η εκπαίδευση για τη γραμμή εργασιών εμφανίστηκε"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Η εκπαίδευση για τη γραμμή εργασιών έκλεισε"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Χρήση της γραμμής εργασιών για εναλλαγή εφαρμογών"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Σύρετε στο πλάι για ταυτόχρονη χρήση δύο εφαρμογών"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Αγγίξτε παρατεταμένα για απόκρυψη της γραμμής εργασιών."</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Σύρετε σύντομα προς τα πάνω για εμφάνιση γραμμής εργαλείων"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Επόμενο"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Πίσω"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Κλείσιμο"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Πρόσφατα"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Ειδοποιήσεις"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Γρήγορες ρυθμ."</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Γραμμή εργαλείων"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Γραμμή πλοήγησης"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Μετακίνηση επάνω/αριστερά"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Μετακίνηση κάτω/δεξιά"</string>
</resources>
diff --git a/quickstep/res/values-en-rAU/strings.xml b/quickstep/res/values-en-rAU/strings.xml
index 7b297af..a42c819 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,25 +77,31 @@
<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_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_button_hint" msgid="2395219947744706291">"Tap the home button to go to your home screen"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"You’re ready to start using your <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"device"</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_app_unsupported" msgid="2360229567007828914">"Choose another app to use split screen"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"This action isn\'t allowed by the app or your organisation"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Cancel"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Skip"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Rotate screen"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Taskbar education"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Taskbar education appeared"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Taskbar education closed"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Use the taskbar to switch apps"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Drag to the side to use two apps at once"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Touch & hold to hide the taskbar"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Short swipe up to show the taskbar"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Next"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Back"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Close"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Recents"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Notifications"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Quick Settings"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Taskbar"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigation bar"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Move to top/left"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Move to bottom/right"</string>
</resources>
diff --git a/quickstep/res/values-en-rCA/strings.xml b/quickstep/res/values-en-rCA/strings.xml
index 4013a71..220cfd1 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,58 +44,64 @@
<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_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_title" msgid="5021126669778966707">"All set!"</string>
+ <string name="allset_hint" msgid="2384632994739392447">"Swipe up to go Home"</string>
+ <string name="allset_button_hint" msgid="2395219947744706291">"Tap the home button to go to your home screen"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"You’re ready to start using your <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"device"</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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Choose another app to use split screen"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"This action isn\'t allowed by the app or your organization"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Cancel"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Skip"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Rotate screen"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Taskbar education"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Taskbar education appeared"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Taskbar education closed"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Use the taskbar to switch apps"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Drag to the side to use two apps at once"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Touch & hold to hide the taskbar"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Short swipe up to show the taskbar"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Next"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Back"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Close"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Recents"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Notifications"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Quick Settings"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Taskbar"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigation bar"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Move to top/left"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Move to bottom/right"</string>
</resources>
diff --git a/quickstep/res/values-en-rGB/strings.xml b/quickstep/res/values-en-rGB/strings.xml
index 7b297af..a42c819 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,25 +77,31 @@
<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_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_button_hint" msgid="2395219947744706291">"Tap the home button to go to your home screen"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"You’re ready to start using your <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"device"</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_app_unsupported" msgid="2360229567007828914">"Choose another app to use split screen"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"This action isn\'t allowed by the app or your organisation"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Cancel"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Skip"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Rotate screen"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Taskbar education"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Taskbar education appeared"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Taskbar education closed"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Use the taskbar to switch apps"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Drag to the side to use two apps at once"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Touch & hold to hide the taskbar"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Short swipe up to show the taskbar"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Next"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Back"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Close"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Recents"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Notifications"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Quick Settings"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Taskbar"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigation bar"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Move to top/left"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Move to bottom/right"</string>
</resources>
diff --git a/quickstep/res/values-en-rIN/strings.xml b/quickstep/res/values-en-rIN/strings.xml
index 7b297af..a42c819 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,25 +77,31 @@
<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_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_button_hint" msgid="2395219947744706291">"Tap the home button to go to your home screen"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"You’re ready to start using your <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"device"</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_app_unsupported" msgid="2360229567007828914">"Choose another app to use split screen"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"This action isn\'t allowed by the app or your organisation"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Cancel"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Skip"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Rotate screen"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Taskbar education"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Taskbar education appeared"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Taskbar education closed"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Use the taskbar to switch apps"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Drag to the side to use two apps at once"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Touch & hold to hide the taskbar"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Short swipe up to show the taskbar"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Next"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Back"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Close"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Recents"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Notifications"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Quick Settings"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Taskbar"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigation bar"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Move to top/left"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Move to bottom/right"</string>
</resources>
diff --git a/quickstep/res/values-en-rXC/strings.xml b/quickstep/res/values-en-rXC/strings.xml
index bd2a024..881e0ab 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,25 +77,31 @@
<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>
- <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_button_hint" msgid="2395219947744706291">"Tap the home button to go to your home screen"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"You’re ready to start using your <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"device"</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 splitscreen"</string>
- <string name="toast_split_app_unsupported" msgid="3271526028981899666">"App does not support split-screen."</string>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Choose another app to use split screen"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"This action isn\'t allowed by the app or your organization"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Cancel"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Skip"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Rotate screen"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Taskbar education"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Taskbar education appeared"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Taskbar education closed"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Use the taskbar to switch apps"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Drag to the side to use two apps at once"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Touch & hold to hide the taskbar"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Short swipe up to show the taskbar"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Next"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Back"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Close"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Recents"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Notifications"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Quick Settings"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Taskbar"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigation bar"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Move to top/left"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Move to bottom/right"</string>
</resources>
diff --git a/quickstep/res/values-es-rUS/strings.xml b/quickstep/res/values-es-rUS/strings.xml
index 06ee5bb..743e7f2 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,25 +77,31 @@
<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>
- <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_button_hint" msgid="2395219947744706291">"Presiona el botón de inicio para ir a la pantalla principal"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"Ya puedes comenzar a usar <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"dispositivo"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Configuración de navegación del sistema"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Compartir"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Elige otra app para usar la pantalla dividida"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"La app o tu organización no permiten realizar esta acción"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Cancelar"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Omitir"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Girar pantalla"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Información sobre la barra de tareas"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Se abrió la barra de herramientas Educación"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Se cerró la barra de herramientas Educación"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Usa la barra de tareas para cambiar de app"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Arrastra a un lado para usar dos apps a la vez"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Mantén presionado para ocultar la barra de tareas"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Desliza un poco hacia arriba para mostrar la barra de tareas"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Siguiente"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Atrás"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Cerrar"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Recientes"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Notificaciones"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Config. rápida"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Barra de tareas"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Barra de navegación"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mover a la parte superior o izquierda"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mover a la parte inferior o derecha"</string>
</resources>
diff --git a/quickstep/res/values-es/strings.xml b/quickstep/res/values-es/strings.xml
index 80c8b0b..84c078c 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,25 +77,31 @@
<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>
- <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_button_hint" msgid="2395219947744706291">"Toca el botón de inicio para ir a la pantalla de inicio"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"Ya puedes empezar a usar tu <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"dispositivo"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Ajustes de navegación del sistema"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Compartir"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Elige otra app para usar la pantalla dividida"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"No puedes hacerlo porque la aplicación o tu organización no lo permiten"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Cancelar"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Saltar"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Girar la pantalla"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Información sobre la barra de tareas"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Ha aparecido una nota sobre la barra de tareas"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Nota sobre la barra de tareas cerrada"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Usa la barra de tareas para cambiar de aplicación"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Arrastra hacia un lado para usar dos aplicaciones a la vez"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Mantén pulsada la barra de tareas para ocultarla"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Desliza un poco hacia arriba para ver la barra de tareas"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Siguiente"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Atrás"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Cerrar"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Recientes"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Notificaciones"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Ajustes rápidos"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Barra de tareas"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Barra de navegación"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mover arriba/a la izquierda"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mover abajo/a la derecha"</string>
</resources>
diff --git a/quickstep/res/values-et/strings.xml b/quickstep/res/values-et/strings.xml
index 7f37a4b..5badec9 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,25 +77,31 @@
<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>
- <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_button_hint" msgid="2395219947744706291">"Avakuvale liikumiseks puudutage avakuva nuppu"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"Olete valmis oma seadet <xliff:g id="DEVICE">%1$s</xliff:g> kasutama"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"seade"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Süsteemi navigeerimisseaded"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Jaga"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Valige jagatud ekraanikuva jaoks muu rakendus"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Rakendus või teie organisatsioon on selle toimingu keelanud"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Tühista"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Jäta vahele"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Pöörake ekraani"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Tegumiriba tutvustus"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Tegumiriba juhised kuvati"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Tegumiriba juhised on suletud"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Kasutage rakenduste vahetamiseks tegumiriba"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Kahe rakenduse korraga kasutamiseks lohistage külje poole"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Tegumiriba peitmiseks puudutage pikalt"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Tegumiriba kuvamiseks pühkige korraks"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Järgmine"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Tagasi"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Sule"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Hiljutised"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Märguanded"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Kiirseaded"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Tegumiriba"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigeerimisriba"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Teisalda üles/vasakule"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Teisalda alla/paremale"</string>
</resources>
diff --git a/quickstep/res/values-eu/strings.xml b/quickstep/res/values-eu/strings.xml
index 4169838..e2912ef 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,25 +77,31 @@
<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>
- <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_button_hint" msgid="2395219947744706291">"Hasierako pantailara joateko, sakatu Hasiera botoia"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"Prest zaude <xliff:g id="DEVICE">%1$s</xliff:g> erabiltzen hasteko"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"gailua"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Sisteman nabigatzeko ezarpenak"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Partekatu"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Pantaila zatitua ikusteko, aukeratu beste aplikazio bat"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Utzi"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Saltatu"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Biratu pantaila"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Zereginen barra erabiltzeko argibideak"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Agertu egin da zereginen barraren tutoriala"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Itxi egin da zereginen barraren tutoriala"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Erabili zereginen barra aplikazioz aldatzeko"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Bi aplikazio batera erabiltzeko, arrastatu hatza albo batera"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Zereginen barra ezkutatzeko, eduki ezazu sakatuta"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Ataza-barra ikusteko, pasatu hatza bizkor gora"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Hurrengoa"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Atzera"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Itxi"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Azkenak"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Jakinarazpenak"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Ezarpen bizkorrak"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Zereginen barra"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Nabigazio-barra"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Eraman gora, ezkerretara"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Eraman behera, eskuinetara"</string>
</resources>
diff --git a/quickstep/res/values-fa/strings.xml b/quickstep/res/values-fa/strings.xml
index de6d7e5..0e3b538 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,25 +77,31 @@
<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_description" msgid="6350320429953234580">"آمادهاید از تلفنتان استفاده کنید"</string>
- <string name="allset_description_tablet" msgid="7332070270570039247">"آمادهاید از رایانه لوحیتان استفاده کنید"</string>
+ <string name="allset_button_hint" msgid="2395219947744706291">"برای رفتن به صفحه اصلی، روی دکمه صفحه اصلی ضربه بزنید"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"آمادهاید از <xliff:g id="DEVICE">%1$s</xliff:g> خود استفاده کنید"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"دستگاه"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"تنظیمات پیمایش سیستم"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"همرسانی"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"انتخاب برنامهای دیگر برای استفاده از صفحه دونیمه"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"لغو"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"رد شدن"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"چرخاندن صفحه"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"آموزش نوار وظیفه"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"پانل آموزشی نوار وظیفه نمایان شد"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"پانل آموزشی نوار وظیفه بسته شد"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"برای جابهجایی بین برنامهها، از نوار وظیفه استفاده کنید"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"برای استفاده همزمان از دو برنامه، آن را به کنار بکشید"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"برای پنهان کردن نوار وظیفه، لمس کنید و نگه دارید"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"برای نمایش نوار وظیفه، کمی به بالا بکشید"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"بعدی"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"برگشت"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"بستن"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"موارد اخیر"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"اعلانها"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"تنظیمات فوری"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"نوار وظیفه"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"نوار پیمایش"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"انتقال به بالا/ چپ"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"انتقال به پایین/ راست"</string>
</resources>
diff --git a/quickstep/res/values-fi/strings.xml b/quickstep/res/values-fi/strings.xml
index 8c50f70..c3cd69e 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,25 +77,31 @@
<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>
- <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_button_hint" msgid="2395219947744706291">"Siirry aloitusnäytölle napauttamalla aloitusnäyttöpainiketta"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"<xliff:g id="DEVICE">%1$s</xliff:g> on nyt valmis käytettäväksi"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"Laite"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Järjestelmän navigointiasetukset"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Jaa"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Käytä jaettua näyttöä valitsemalla toinen sovellus"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Sovellus tai organisaatio ei salli tätä toimintoa"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Peru"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Ohita"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Käännä näyttö"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Tehtäväpalkin ohje"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Tehtäväpalkin ohje näkyvissä"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Tehtäväpalkin ohje suljettu"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Vaihda sovellusta tehtäväpalkin kautta"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Vetämällä sivuun voit käyttää kahta sovellusta samaan aikaan"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Piilota tehtäväpalkki koskettamalla pitkään"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Näytä tehtäväpalkki pyyhkäisemällä ylös lyhyesti"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Seuraava"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Takaisin"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Sulje"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Viimeaikaiset"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Ilmoitukset"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Pika-asetukset"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Tehtäväpalkki"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigointipalkki"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Siirrä ylös tai vasemmalle"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Siirrä alas tai oikealle"</string>
</resources>
diff --git a/quickstep/res/values-fr-rCA/strings.xml b/quickstep/res/values-fr-rCA/strings.xml
index f92d55c..5293ed6 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,25 +77,31 @@
<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>
- <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_button_hint" msgid="2395219947744706291">"Toucher le bouton d\'accueil pour passer sur votre écran d\'accueil"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"Vous êtes maintenant prêt à utiliser votre <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"appareil"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Paramètres de navigation du système"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Partager"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Choisir une autre application pour utiliser l\'écran partagé"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"L\'application ou votre organisation n\'autorise pas cette action"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Annuler"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Ignorer"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Faire pivoter l\'écran"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Informations sur la barre des tâches"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"La barre des tâches éducatives s\'est affichée"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"La barre des tâches éducatives est fermée"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Utilisez la barre des tâches pour changer les applications"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Glissez sur le côté pour utiliser 2 applications à la fois"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Maintenez le doigt sur la barre des tâches pour la masquer"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Faites glisser vers le haut pour afficher la barre de tâches"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Suivant"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Retour"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Fermer"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Récents"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Notifications"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Paramètres rapides"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Barre des tâches"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Barre de navigation"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Déplacer vers le coin supérieur gauche de l\'écran"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Déplacer vers le coin inférieur droit de l\'écran"</string>
</resources>
diff --git a/quickstep/res/values-fr/strings.xml b/quickstep/res/values-fr/strings.xml
index 9ffa0cc..cff4659 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,25 +77,31 @@
<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>
- <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_button_hint" msgid="2395219947744706291">"Appuyez sur le bouton d\'accueil pour accéder à votre écran d\'accueil"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"Vous pouvez maintenant utiliser votre <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"appareil"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Paramètres de navigation système"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Partager"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Sélect. autre appli pour utiliser l\'écran partagé"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Cette action n\'est pas autorisée par l\'application ou par votre organisation"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Annuler"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Passer"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Faire pivoter l\'écran"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Fonctionnement de la barre des tâches"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Infos sur la barre des tâches affichées"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Infos sur la barre des tâches fermées"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Utilisez la barre des tâches pour changer d\'application"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Faites glisser sur côté pour utiliser deux applis à la fois"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Appuyez de manière prolongée pour masquer barre des tâches"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Balayez rapidement vers haut pour afficher barre des tâches"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Suivant"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Retour"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Fermer"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Récents"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Notifications"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Réglages rapides"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Barre des tâches"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Barre de navigation"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Déplacer en haut ou à gauche"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Déplacer en bas ou à droite"</string>
</resources>
diff --git a/quickstep/res/values-gl/strings.xml b/quickstep/res/values-gl/strings.xml
index bea23b7..5988433 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,25 +77,31 @@
<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>
- <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_button_hint" msgid="2395219947744706291">"Toca o botón de inicio para ir á pantalla de inicio"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"Xa podes comezar a utilizar o teu dispositivo (<xliff:g id="DEVICE">%1$s</xliff:g>)"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"dispositivo"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Configuración da navegación do sistema"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Compartir"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Escolle outra app para usar a pantalla dividida"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"A aplicación ou a túa organización non permite realizar esta acción"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Cancelar"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Omitir"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Xira a pantalla"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Información sobre a función Barra de tarefas"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Panel de información de barra de tarefas aberto"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Panel de información de barra de tarefas pechado"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Usa a barra de ferramentas para cambiar de aplicación"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Para usar dúas aplicacións á vez, arrastra cara ao lado"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Mantén premida a barra de tarefas para ocultala"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Pasa o dedo un pouco cara arriba para ver a barra de tarefas"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Seguinte"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Atrás"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Pechar"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Recentes"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Notificacións"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Configuración rápida"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Barra de tarefas"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Barra de navegación"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mover á parte superior ou á esquerda"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mover á parte inferior ou á dereita"</string>
</resources>
diff --git a/quickstep/res/values-gu/strings.xml b/quickstep/res/values-gu/strings.xml
index a146b24..518d051 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,25 +77,31 @@
<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_description" msgid="6350320429953234580">"તમે તમારા ફોનનો ઉપયોગ કરવા માટે તૈયાર છો"</string>
- <string name="allset_description_tablet" msgid="7332070270570039247">"તમે તમારા ટૅબ્લેટનો ઉપયોગ કરવા માટે તૈયાર છો"</string>
+ <string name="allset_button_hint" msgid="2395219947744706291">"તમારી હોમ સ્ક્રીન પર જવા માટે હોમ બટન ટૅપ કરો"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"તમે તમારા <xliff:g id="DEVICE">%1$s</xliff:g>નો ઉપયોગ કરવાનું શરૂ કરવા માટે તૈયાર છો"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"ડિવાઇસ"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"સિસ્ટમના નૅવિગેશન સેટિંગ"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"શેર કરો"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"સ્ક્રીન વિભાજનનો ઉપયોગ કરવા કોઈ અન્ય ઍપ પસંદ કરો"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"રદ કરો"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"છોડો"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"સ્ક્રીન ફેરવો"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"ટાસ્કબાર વિશે શિક્ષણ"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"ટાસ્કબારનું શિક્ષણ આપતી પૅનલ દેખાય છે"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"ટાસ્કબારનું શિક્ષણ આપતી પૅનલ બંધ થઈ છે"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"ઍપ સ્વિચ કરવા માટે, ટાસ્કબારનો ઉપયોગ કરો"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"એક જ સમયે બે ઍપનો ઉપયોગ કરવા માટે, ખેંચીને બાજુ પર લઈ જાઓ"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"ટાસ્કબાર છુપાવવા, તેને ટચ કરીને થોડીવાર દબાવી રાખો"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"ટાસ્કબાર બતાવવા માટે ઉપર થોડું સ્વાઇપ કરો"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"આગળ"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"પાછળ"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"બંધ કરો"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"તાજેતરના"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"નોટિફિકેશન"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"ઝડપી સેટિંગ"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"ટાસ્કબાર"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"નૅવિગેશન બાર"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"સૌથી ઉપર ડાબી બાજુએ ખસેડો"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"સૌથી નીચે જમણી બાજુએ ખસેડો"</string>
</resources>
diff --git a/quickstep/res/values-hi/strings.xml b/quickstep/res/values-hi/strings.xml
index c938e6b..540530d 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,25 +77,31 @@
<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_description" msgid="6350320429953234580">"अब आपका फ़ोन, इस्तेमाल के लिए तैयार है"</string>
- <string name="allset_description_tablet" msgid="7332070270570039247">"आप टैबलेट को इस्तेमाल करने के लिए तैयार हैं"</string>
+ <string name="allset_button_hint" msgid="2395219947744706291">"होम स्क्रीन पर जाने के लिए, होम बटन पर टैप करें"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"आप <xliff:g id="DEVICE">%1$s</xliff:g> को इस्तेमाल करने के लिए तैयार हैं"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"डिवाइस"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"सिस्टम नेविगेशन सेटिंग"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"शेयर करें"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"स्प्लिट स्क्रीन के लिए, दूसरा ऐप्लिकेशन चुनें"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"रद्द करें"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"छोड़ें"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"स्क्रीन घुमाएं"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"टास्कबार का ट्यूटोरियल"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"टास्कबार ट्यूटोरियल दिखाया गया"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"टास्कबार ट्यूटोरियल बंद किया गया"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"ऐप्लिकेशन स्विच करने के लिए, टास्कबार का इस्तेमाल करें"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"एक साथ दो ऐप्लिकेशन इस्तेमाल करने के लिए, उन्हें किनारे की ओर खींचें और छोड़ें"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"टास्कबार को छिपाने के लिए, उसे दबाकर रखें"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"टास्कबार दिखाने के लिए, ऊपर की ओर थोड़ा स्वाइप करें"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"आगे बढ़ें"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"वापस जाएं"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"बंद करें"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"हाल ही के"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"सूचनाएं"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"फटाफट सेटिंग"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"टास्कबार"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"नेविगेशन बार"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ऊपर/बाईं तरफ़ ले जाएं"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"नीचे/दाईं तरफ़ ले जाएं"</string>
</resources>
diff --git a/quickstep/res/values-hr/strings.xml b/quickstep/res/values-hr/strings.xml
index 640d7cc..2152f22 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,25 +77,31 @@
<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>
- <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_button_hint" msgid="2395219947744706291">"Dodirnite gumb početnog zaslona da biste prešli na početni zaslon"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"Spremni ste za početak upotrebe uređaja <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"uređaj"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Postavke navigacije sustavom"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Podijeli"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Odaberite drugu aplikaciju za upotrebu podijeljenog zaslona"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Aplikacija ili vaša organizacija ne dopuštaju ovu radnju"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Odustani"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Preskoči"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Zakretanje zaslona"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Upute za traku sa zadacima"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Upute za programsku traku su se pojavile"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Upute za programsku traku su zatvorene"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Upotrijebite program. traku da biste promijenili aplikaciju"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Povucite u stranu da biste istovremeno koristili dvije aplikacije"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Dodirnite i zadržite da biste sakrili programsku traku"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Kratki pokret prema gore za prikaz trake sa zadacima"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Dalje"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Natrag"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Zatvori"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Najnovije"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Obavijesti"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Brze postavke"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Traka sa zadacima"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigacijska traka"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Premjesti gore/lijevo"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Premjesti dolje/desno"</string>
</resources>
diff --git a/quickstep/res/values-hu/strings.xml b/quickstep/res/values-hu/strings.xml
index 6369293..9646f3e 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,25 +77,31 @@
<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>
- <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_button_hint" msgid="2395219947744706291">"A kezdőképernyőre való lépéshez koppintson a kezdőképernyő gombra"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"Készen áll a(z) <xliff:g id="DEVICE">%1$s</xliff:g> használatára"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"eszköz"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Rendszer-navigációs beállítások"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Megosztás"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Válasszon másik appot a képernyő felosztásához"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Az alkalmazás vagy az Ön szervezete nem engedélyezi ezt a műveletet"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Mégse"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Kihagyás"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Képernyő elforgatása"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Tálca használatának ismertetése"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Az eszköztár használatát ismertető panel megjelent"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Az eszköztár használatát ismertető panel bezárult"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Az eszköztárral válthat az alkalmazások között"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Húzza oldalra, ha egyszerre két appot szeretne használni"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Nyomva tartással elrejthető az eszköztár"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Röviden csúsztasson fel a Feladatsáv megjelenítéséhez"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Tovább"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Vissza"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Bezárás"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Legutóbbiak"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Értesítések"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Gyorsbeállítások"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Tálca"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigációs sáv"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mozgatás felülre vagy a bal oldalra"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mozgatás alulra vagy a jobb oldalra"</string>
</resources>
diff --git a/quickstep/res/values-hy/strings.xml b/quickstep/res/values-hy/strings.xml
index 9eeaa98..fa32a31 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,25 +77,31 @@
<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_description" msgid="6350320429953234580">"Դուք արդեն կարող եք օգտագործել ձեր հեռախոսը"</string>
- <string name="allset_description_tablet" msgid="7332070270570039247">"Դուք արդեն կարող եք օգտագործել ձեր պլանշետը"</string>
+ <string name="allset_button_hint" msgid="2395219947744706291">"Հիմնական էկրան վերադառնալու համար սեղմեք գլխավոր էկրանի կոճակը"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"Դուք արդեն կարող եք օգտագործել ձեր <xliff:g id="DEVICE">%1$s</xliff:g> սարքը"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"սարք"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Նավիգացիայի համակարգային կարգավորումներ"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Կիսվել"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Ընտրեք այլ հավելված՝ կիսված էկրանից օգտվելու համար"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Չեղարկել"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Բաց թողնել"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Պտտել էկրանը"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Խնդրագոտու «Կրթություն» պատուհան"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Խնդրագոտու «Կրթություն» վահանակը բացվեց"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Խնդրագոտու «Կրթություն» վահանակը փակվեց"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Օգտագործեք խնդրագոտին՝ մի հավելվածից մյուսին անցնելու համար"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Քաշեք մի կողմ՝ միաժամանակ երկու հավելված օգտագործելու համար"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Հպեք և պահեք՝ խնդրագոտին թաքցնելու համար"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Հավելվածների վահանակը բացելու համար մատն արագ սահեցրեք վերև"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Առաջ"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Հետ"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Փակել"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Վերջինները"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Ծանուցումներ"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Արագ կարգավորումներ"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Խնդրագոտի"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Նավիգացիայի գոտի"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Տեղափոխել վերևի ձախ անկյուն"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Տեղափոխել ներքևի աջ անկյուն"</string>
</resources>
diff --git a/quickstep/res/values-in/strings.xml b/quickstep/res/values-in/strings.xml
index 14d8c3e..4e4a3ea 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,25 +77,31 @@
<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>
- <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_button_hint" msgid="2395219947744706291">"Ketuk tombol layar utama untuk membuka layar utama"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"Anda sudah siap untuk mulai menggunakan <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"perangkat"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Setelan navigasi sistem"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Bagikan"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Pilih aplikasi lain untuk memakai layar terpisah"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Tindakan ini tidak diizinkan oleh aplikasi atau organisasi Anda"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Batal"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Lewati"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Putar layar"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Pengantar Taskbar"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Edukasi taskbar ditampilkan"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Edukasi taskbar ditutup"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Gunakan taskbar untuk beralih aplikasi"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Tarik ke samping untuk menggunakan dua aplikasi sekaligus"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Sentuh lama untuk menyembunyikan taskbar"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Geser sedikit ke atas untuk menampilkan taskbar"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Berikutnya"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Kembali"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Tutup"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Terbaru"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Notifikasi"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Setelan Cepat"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Taskbar"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Menu navigasi"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Pindahkan ke atas/kiri"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Pindahkan ke bawah/kanan"</string>
</resources>
diff --git a/quickstep/res/values-is/strings.xml b/quickstep/res/values-is/strings.xml
index 1761203..957f3ba 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,25 +77,31 @@
<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>
- <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_button_hint" msgid="2395219947744706291">"Ýttu á heimahnappinn til að fara á heimaskjáinn"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"Þú getur byrjað að nota <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"tækið"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Stillingar kerfisstjórnunar"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Deila"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Veldu annað forrit til að nota skjáskiptingu"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Forritið eða fyrirtækið leyfir ekki þessa aðgerð"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Hætta við"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Sleppa"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Snúa skjánum"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Leiðsögn verkefnastiku"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Leiðsögn verkefnastiku sýnileg"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Leiðsögn verkefnastiku lokað"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Notaðu verkefnastikuna til að skipta á milli forrita"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Dragðu til hliðar til að nota tvö forrit í einu"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Haltu inni til að fela verkefnastikuna"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Stutt stroka upp til að sýna forritastiku"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Áfram"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Til baka"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Loka"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Nýlegt"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Tilkynningar"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Flýtistillingar"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Verkstika"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Yfirlitsstika"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Færa efst/til vinstri"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Færa neðst/til hægri"</string>
</resources>
diff --git a/quickstep/res/values-it/strings.xml b/quickstep/res/values-it/strings.xml
index 874d072..c01ca3d 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,25 +77,31 @@
<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>
- <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_button_hint" msgid="2395219947744706291">"Tocca il pulsante Home per andare alla schermata Home"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"Puoi iniziare a usare il tuo <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"dispositivo"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Impostazioni Navigazione del sistema"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Condividi"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Scegli un\'altra app per usare lo schermo diviso"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Questa azione non è consentita dall\'app o dall\'organizzazione"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Annulla"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Salta"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Ruota lo schermo"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Informazioni sulla barra delle applicazioni"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Riquadro Formazione barra delle applicazioni visualizzato"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Riquadro Formazione barra delle applicazioni chiuso"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Usa la barra delle applicazioni per cambiare app"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Trascina di lato per usare due app contemporaneamente"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Tocca e tieni premuto per nascondere barra applicazioni"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Scorri verso l\'alto per mostrare la barra delle applicazioni"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Avanti"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Indietro"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Chiudi"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Recenti"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Notifiche"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Impostazioni rapide"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Barra delle applicazioni"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Barra di navigazione"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Sposta in alto/a sinistra"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Sposta in basso/a destra"</string>
</resources>
diff --git a/quickstep/res/values-iw/strings.xml b/quickstep/res/values-iw/strings.xml
index 09d76f8..cef57e00 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,25 +77,31 @@
<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_description" msgid="6350320429953234580">"הכול מוכן ואפשר להתחיל להשתמש בטלפון"</string>
- <string name="allset_description_tablet" msgid="7332070270570039247">"הכול מוכן ואפשר להתחיל להשתמש בטאבלט"</string>
+ <string name="allset_button_hint" msgid="2395219947744706291">"כדי לעבור אל מסך הבית יש להקיש על הלחצן הראשי"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"הכול מוכן ואפשר להתחיל להשתמש ב<xliff:g id="DEVICE">%1$s</xliff:g>"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"מכשיר"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"הגדרות הניווט של המערכת"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"שיתוף"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"כדי להשתמש במסך מפוצל צריך לבחור אפליקציה אחרת"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"ביטול"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"דילוג"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"סיבוב המסך"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"הסבר על סרגל האפליקציות"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"חלונית ההסברים על שורת המשימות מופיעה"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"חלונית ההסברים על שורת המשימות נסגרה"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"כדי לעבור בין אפליקציות, משתמשים בשורת המשימות"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"כדי להשתמש בשתי אפליקציות בו-זמנית, צריך לגרור לצד"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"כדי להסתיר את שורת המשימות, לוחצים לחיצה ארוכה"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"צריך להחליק מעט כדי להציג את סרגל האפליקציות"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"הבא"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"חזרה"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"סגירה"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"לאחרונה"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"התראות"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"הגדרות מהירות"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"סרגל האפליקציות"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"סרגל הניווט"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"העברה לפינה השמאלית/העליונה"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"העברה לפינה הימנית/התחתונה"</string>
</resources>
diff --git a/quickstep/res/values-ja/strings.xml b/quickstep/res/values-ja/strings.xml
index 50c031e..46b7f38 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,25 +77,31 @@
<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_description" msgid="6350320429953234580">"スマートフォンを使用する準備ができました"</string>
- <string name="allset_description_tablet" msgid="7332070270570039247">"これでタブレットが使えるようになりました"</string>
+ <string name="allset_button_hint" msgid="2395219947744706291">"ホームボタンをタップすると、ホーム画面に移動します"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"<xliff:g id="DEVICE">%1$s</xliff:g> を使用する準備ができました"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"デバイス"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"システム ナビゲーションの設定"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"共有"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"分割画面にするには、別のアプリを選択してください"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"キャンセル"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"スキップ"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"画面を回転"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"タスクバーの説明"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"タスクバーの説明を開きました"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"タスクバーの説明を閉じました"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"アプリを切り替えるには、タスクバーを使用します"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"2 個のアプリを同時に使用するには、横にドラッグします"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"タスクバーを長押しすると非表示になります"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"タスクバーを表示するには、上に短くスワイプします"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"次へ"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"戻る"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"閉じる"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"最近"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"通知"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"クイック設定"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"タスクバー"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"ナビゲーション バー"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"上 / 左に移動"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"下 / 右に移動"</string>
</resources>
diff --git a/quickstep/res/values-ka/strings.xml b/quickstep/res/values-ka/strings.xml
index 2bc0108..86d369b 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,25 +77,31 @@
<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_description" msgid="6350320429953234580">"მზად ხართ ტელეფონის გამოსაყენებლად"</string>
- <string name="allset_description_tablet" msgid="7332070270570039247">"მზად ხართ ტაბლეტის გამოსაყენებლად"</string>
+ <string name="allset_button_hint" msgid="2395219947744706291">"შეეხეთ მთავარი ეკრანის ღილაკს მთავარ ეკრანზე გადასასვლელად"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"მზად ხართ, გამოიყენოთ <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"მოწყობილობა"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"სისტემის ნავიგაციის პარამეტრები"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"გაზიარება"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"აირჩიეთ სხვა აპი ეკრანის გასაყოფად"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"გაუქმება"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"გამოტოვება"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"ეკრანის შეტრიალება"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"ამოცანათა ზოლი: განათლება"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"ამოცანების ზოლის სასწავლო არე გამოჩნდა"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"ამოცანების ზოლის სასწავლო არე დაიხურა"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"აპების გადასართავად გამოიყენეთ ამოცანათა ზოლი"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"გადაათრიეთ კიდეზე ორი აპის ერთდოულად გამოსაყენებლად"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"ხანგრძლივად შეეხეთ ამოცანების ზოლის დასამალად"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"მოკლედ გადაფურცლეთ ზემოთ, რომ ამოცანათა ზოლი გამოაჩინოთ"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"შემდეგი"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"უკან"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"დახურვა"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"ბოლოდროინდელი"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"შეტყობინებები"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"სწრაფი პარამეტრები"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"ამოცანათა ზოლი"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"ნავიგაციის ზოლი"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ზემოთ/მარცხნივ გადატანა"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ქვემოთ/მარჯვნივ გადატანა"</string>
</resources>
diff --git a/quickstep/res/values-kk/strings.xml b/quickstep/res/values-kk/strings.xml
index 7758125..f4f33ca 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,25 +77,31 @@
<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_description" msgid="6350320429953234580">"Телефоныңыз пайдалануға дайын."</string>
- <string name="allset_description_tablet" msgid="7332070270570039247">"Планшетіңіз пайдалануға дайын."</string>
+ <string name="allset_button_hint" msgid="2395219947744706291">"Негізгі экранға өту үшін негізгі экран түймесін түртіңіз."</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"<xliff:g id="DEVICE">%1$s</xliff:g> пайдалануға дайын."</string>
+ <string name="default_device_name" msgid="6660656727127422487">"құрылғы"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Навигацияның жүйелік параметрлері"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Бөлісу"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Экранды бөлу үшін басқа қолданбаны таңдаңыз."</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Бас тарту"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Өткізіп жіберу"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Экранды бұру"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Тапсырмалар жолағы: үйрену"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Тапсырмалар тақтасы бойынша нұсқаулық ашылды."</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Тапсырмалар тақтасы бойынша нұсқаулық жабылды."</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Қолданбаларды ауыстыру үшін тапсырма тақтасын пайдаланыңыз."</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Екі қолданбаны бір уақытта пайдалану үшін шетке сүйреңіз."</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Тапсырмалар тақтасын жасыру үшін басып тұрыңыз."</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Тапсырмалар жолағын көру үшін жоғары қарай тез сырғытыңыз."</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Келесі"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Артқа"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Жабу"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Соңғылары"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Хабарландырулар"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Жылдам параметрлер"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Тапсырмалар жолағы"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Навигация жолағы"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Жоғары/солға жылжыту"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Төмен/оңға жылжыту"</string>
</resources>
diff --git a/quickstep/res/values-km/strings.xml b/quickstep/res/values-km/strings.xml
index 93b2361..82fbe4f 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,25 +77,31 @@
<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_description" msgid="6350320429953234580">"អ្នកអាចចាប់ផ្ដើមប្រើទូរសព្ទរបស់អ្នកបានហើយ"</string>
- <string name="allset_description_tablet" msgid="7332070270570039247">"អ្នកអាចចាប់ផ្ដើមប្រើថេប្លេតរបស់អ្នកបានហើយ"</string>
+ <string name="allset_button_hint" msgid="2395219947744706291">"ចុចប៊ូតុងដើម ដើម្បីចូលទៅកាន់អេក្រង់ដើមរបស់អ្នក"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"អ្នកអាចចាប់ផ្ដើមប្រើ <xliff:g id="DEVICE">%1$s</xliff:g> របស់អ្នកបានហើយ"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"ឧបករណ៍"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"ការកំណត់ការរុករកប្រព័ន្ធ"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"ចែករំលែក"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"ជ្រើសរើសកម្មវិធីផ្សេងទៀត ដើម្បីប្រើមុខងារបំបែកអេក្រង់"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"បោះបង់"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"រំលង"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"បង្វិលអេក្រង់"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"ការអប់រំលើរបារកិច្ចការ"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"ការបង្រៀនអំពីរបារកិច្ចការបានបង្ហាញ"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"ការបង្រៀនអំពីរបារកិច្ចការត្រូវបានបិទ"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"ប្រើរបារកិច្ចការ ដើម្បីប្ដូរកម្មវិធី"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"អូសទៅចំហៀង ដើម្បីប្រើកម្មវិធីពីរក្នុងពេលតែមួយ"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"ចុចឱ្យជាប់ ដើម្បីលាក់របារកិច្ចការ"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"អូសឡើងលើបន្តិច ដើម្បីបង្ហាញរបារកិច្ចការ"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"បន្ទាប់"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"ថយក្រោយ"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"បិទ"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"ថ្មីៗ"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"ការជូនដំណឹង"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"ការកំណត់រហ័ស"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"របារកិច្ចការ"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"របាររុករក"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ផ្លាស់ទីទៅខាងលើ/ឆ្វេង"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ផ្លាស់ទីទៅខាងក្រោម/ស្ដាំ"</string>
</resources>
diff --git a/quickstep/res/values-kn/strings.xml b/quickstep/res/values-kn/strings.xml
index 2c1f699..4c23238 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,25 +77,31 @@
<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_description" msgid="6350320429953234580">"ನಿಮ್ಮ ಫೋನ್ ಬಳಸುವುದನ್ನು ಪ್ರಾರಂಭಿಸಲು ನೀವು ಸಿದ್ದರಾಗಿರುವಿರಿ"</string>
- <string name="allset_description_tablet" msgid="7332070270570039247">"ನಿಮ್ಮ ಟ್ಯಾಬ್ಲೆಟ್ ಬಳಸುವುದನ್ನು ಪ್ರಾರಂಭಿಸಲು ನೀವು ಸಿದ್ದರಾಗಿರುವಿರಿ"</string>
+ <string name="allset_button_hint" msgid="2395219947744706291">"ನಿಮ್ಮ ಮುಖಪುಟದ ಪರದೆಗೆ ಹೋಗಲು ಮುಖಪುಟ ಬಟನ್ ಅನ್ನು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"ನಿಮ್ಮ <xliff:g id="DEVICE">%1$s</xliff:g> ಬಳಸುವುದನ್ನು ಪ್ರಾರಂಭಿಸಲು ನೀವು ಸಿದ್ಧರಾಗಿರುವಿರಿ"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"ಸಾಧನ"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"ಸಿಸ್ಟಂ ನ್ಯಾವಿಗೇಶನ್ ಸೆಟ್ಟಿಂಗ್ಗಳು"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"ಹಂಚಿಕೊಳ್ಳಿ"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"\"ಪರದೆ ಬೇರ್ಪಡಿಸಿ\" ಬಳಸಲು ಬೇರೆ ಆ್ಯಪ್ ಅನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"ರದ್ದುಮಾಡಿ"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"ಸ್ಕಿಪ್ ಮಾಡಿ"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"ಸ್ಕ್ರೀನ್ ತಿರುಗಿಸಿ"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"ಟಾಸ್ಕ್ಬಾರ್ ಶಿಕ್ಷಣ"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"ಟಾಸ್ಕ್ಬಾರ್ ಶಿಕ್ಷಣ ಕಾಣಿಸಿಕೊಂಡಿದೆ"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"ಟಾಸ್ಕ್ಬಾರ್ ಶಿಕ್ಷಣ ಮುಚ್ಚಿದೆ"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"ಆ್ಯಪ್ಗಳನ್ನು ಬದಲಾಯಿಸಲು ಟಾಸ್ಕ್ ಬಾರ್ ಬಳಸಿ"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"ಏಕಕಾಲದಲ್ಲಿ ಎರಡು ಆ್ಯಪ್ಗಳನ್ನು ಬಳಸಲು, ಬದಿಗೆ ಡ್ರ್ಯಾಗ್ ಮಾಡಿ"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"ಟಾಸ್ಕ್ಬಾರ್ ಅನ್ನು ಮರೆಮಾಡಲು ಸ್ಪರ್ಶಿಸಿ ಮತ್ತು ಹೋಲ್ಡ್ ಮಾಡಿ"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"ಟಾಸ್ಕ್ಬಾರ್ ಅನ್ನು ತೋರಿಸಲು ಚಿಕ್ಕದಾಗಿ ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"ಮುಂದೆ"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"ಹಿಂದೆ"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"ಮುಚ್ಚಿರಿ"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"ಇತ್ತೀಚಿನವು"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"ಅಧಿಸೂಚನೆಗಳು"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"ತ್ವರಿತ ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"ಟಾಸ್ಕ್ಬಾರ್"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"ನ್ಯಾವಿಗೇಷನ್ ಬಾರ್"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ಮೇಲಿನ/ಎಡಭಾಗಕ್ಕೆ ಸರಿಸಿ"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ಕೆಳಗಿನ/ಬಲಭಾಗಕ್ಕೆ ಸರಿಸಿ"</string>
</resources>
diff --git a/quickstep/res/values-ko/strings.xml b/quickstep/res/values-ko/strings.xml
index 6626bc1..bcf6a4e 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,25 +77,31 @@
<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_description" msgid="6350320429953234580">"휴대전화를 사용할 준비가 되었습니다."</string>
- <string name="allset_description_tablet" msgid="7332070270570039247">"태블릿을 사용할 준비가 되었습니다."</string>
+ <string name="allset_button_hint" msgid="2395219947744706291">"홈 화면으로 이동하려면 홈 버튼을 탭하세요."</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"<xliff:g id="DEVICE">%1$s</xliff:g> 기기를 사용할 준비가 되었습니다."</string>
+ <string name="default_device_name" msgid="6660656727127422487">"기기"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"시스템 탐색 설정"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"공유"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"화면 분할을 사용하려면 다른 앱을 선택하세요."</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"취소"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"건너뛰기"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"화면 회전"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"태스크 바 정보"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"작업 표시줄 튜토리얼 패널 표시됨"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"작업 표시줄 튜토리얼 패널 닫힘"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"작업 표시줄을 사용하여 앱 전환"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"옆으로 드래그하여 한 번에 앱 두 개 사용"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"작업 표시줄을 숨기려면 길게 터치하세요."</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"위로 짧게 스와이프하여 태스크 바를 표시합니다."</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"다음"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"뒤로"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"닫기"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"최근 항목"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"알림"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"빠른 설정"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"태스크 바"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"탐색 메뉴"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"상단/왼쪽으로 이동"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"하단/오른쪽으로 이동"</string>
</resources>
diff --git a/quickstep/res/values-ky/strings.xml b/quickstep/res/values-ky/strings.xml
index dac4d1d..ad17616 100644
--- a/quickstep/res/values-ky/strings.xml
+++ b/quickstep/res/values-ky/strings.xml
@@ -22,7 +22,7 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Кадап коюу"</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="accessibility_app_usage_settings" msgid="6312864233673544149">"Колдонмону пайдалануу параметрлери"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Баарын тазалоо"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Акыркы колдонмолор"</string>
<string name="task_view_closed" msgid="9170038230110856166">"Тапшырма жабылды"</string>
@@ -35,10 +35,9 @@
<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>
+ <string name="hotseat_prediction_settings" msgid="6246554993566070818">"Параметрлер"</string>
<string name="hotseat_auto_enrolled" msgid="522100018967146807">"Көп иштетилген колдонмолор ушул жерде көрүнүп, тартиптин негизинде өзгөрөт"</string>
<string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Сунуштарды алып туруу үчүн ылдый жактагы тилкедеги колдонмолорду сүйрөп келиңиз"</string>
<string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"Сунушталган колдонмолор бош жерге кошулат"</string>
@@ -72,31 +71,37 @@
<string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Бир колдонмодон экинчисине өтүү үчүн экранды 2 манжа менен ылдыйдан өйдө сүрүп, коё бербей туруңуз."</string>
<string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Дапдаяр!"</string>
<string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Бүттү"</string>
- <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Жөндөөлөр"</string>
+ <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Параметрлер"</string>
<string name="gesture_tutorial_try_again" msgid="65962545858556697">"Кайталап көрүңүз"</string>
<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_description" msgid="6350320429953234580">"Телефонуңузду колдоно берсеңиз болот"</string>
- <string name="allset_description_tablet" msgid="7332070270570039247">"Планшетиңизди колдоно берсеңиз болот"</string>
+ <string name="allset_button_hint" msgid="2395219947744706291">"Башкы экранга өтүү үчүн башкы бет баскычын таптап коюңуз"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"<xliff:g id="DEVICE">%1$s</xliff:g> түзмөгүн колдоно берсеңиз болот"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"түзмөк"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Өтүү аракетинин системалык параметрлери"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Бөлүшүү"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Экранды бөлүү үчүн башка колдонмону тандаңыз"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Жокко чыгаруу"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Өткрп жиберүү"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Экранды буруу"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Тапшырмалар панели жөнүндө маалымат"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Тапшырмалар тактасынын окутуу панели көрсөтүлдү"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Тапшырмалар тактасынын окутуу панели жабылды"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Тапшырмалар тактасы аркылуу башка колдонмого которула аласыз"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Эки колдонмону бир убакта пайдалануу үчүн капталга сүрүңүз"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Тапшырмалар тактасын жашыруу үчүн коё бербей басып туруңуз"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Тапшырмалар тактасын көрүү үчүн экранды өйдө серпиңиз"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Кийинки"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Артка"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Жабуу"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Акыркылар"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Билдирмелер"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Ыкчам жөндөөлөр"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Тапшырмалар панели"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Чабыттоо тилкеси"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Жогорку/сол бурчка жылдыруу"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Төмөнкү/оң бурчка жылдыруу"</string>
</resources>
diff --git a/quickstep/res/values-land/dimens.xml b/quickstep/res/values-land/dimens.xml
index 8368069..30983c4 100644
--- a/quickstep/res/values-land/dimens.xml
+++ b/quickstep/res/values-land/dimens.xml
@@ -76,8 +76,10 @@
<dimen name="gesture_tutorial_taskbar_padding_start_end">218dp</dimen>
<!-- Taskbar 3 button spacing -->
- <dimen name="taskbar_button_margin_5_5">94.5dp</dimen>
- <dimen name="taskbar_button_margin_6_5">94.5dp</dimen>
- <dimen name="taskbar_button_margin_4_5">84dp</dimen>
- <dimen name="taskbar_button_margin_4_4">79dp</dimen>
+ <dimen name="taskbar_button_margin_split">88dp</dimen>
+ <dimen name="taskbar_button_margin_6_5">219.6dp</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..963f497 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,25 +77,31 @@
<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_description" msgid="6350320429953234580">"ທ່ານພ້ອມເລີ່ມຕົ້ນໃຊ້ໂທລະສັບຂອງທ່ານແລ້ວ"</string>
- <string name="allset_description_tablet" msgid="7332070270570039247">"ທ່ານພ້ອມເລີ່ມຕົ້ນໃຊ້ແທັບເລັດຂອງທ່ານແລ້ວ"</string>
+ <string name="allset_button_hint" msgid="2395219947744706291">"ແຕະປຸ່ມໜ້າທຳອິດເພື່ອໄປຫາໂຮມສະກຣີນຂອງທ່ານ"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"ທ່ານເລີ່ມໃຊ້ແທັບເລັດ <xliff:g id="DEVICE">%1$s</xliff:g> ຂອງທ່ານໄດ້ແລ້ວ"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"ອຸປະກອນ"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"ການຕັ້ງຄ່າການນຳທາງລະບົບ"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"ແບ່ງປັນ"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"ເລືອກແອັບອື່ນເພື່ອໃຊ້ການແບ່ງໜ້າຈໍ"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"ຍົກເລີກ"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"ຂ້າມ"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"ໝຸນໜ້າຈໍ"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"ແຖບໜ້າວຽກ Education"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"ສະແດງການສຶກສາແຖບໜ້າວຽກແລ້ວ"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"ປິດການສຶກສາແຖບໜ້າວຽກແລ້ວ"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"ໃຊ້ແຖບໜ້າວຽກເພື່ອສະຫຼັບແອັບ"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"ລາກໄປທາງຂ້າງເພື່ອໃຊ້ສອງແອັບພ້ອມກັນ"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"ແຕະຄ້າງໄວ້ເພື່ອເຊື່ອງແຖບໜ້າວຽກ"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"ປັດຂຶ້ນສັ້ນໆເພື່ອສະແດງແຖບໜ້າວຽກ"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"ຕໍ່ໄປ"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"ກັບຄືນ"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"ປິດ"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"ຫຼ້າສຸດ"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"ການແຈ້ງເຕືອນ"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"ການຕັ້ງຄ່າດ່ວນ"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"ແຖບໜ້າວຽກ"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"ແຖບການນຳທາງ"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ຍ້າຍໄປຊ້າຍ/ເທິງ"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ຍ້າຍໄປຂວາ/ລຸ່ມ"</string>
</resources>
diff --git a/quickstep/res/values-lt/strings.xml b/quickstep/res/values-lt/strings.xml
index 25a84a7..e31fbf0 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,25 +77,31 @@
<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>
- <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_button_hint" msgid="2395219947744706291">"Norėdami eiti į pagrindinį ekraną, palieskite pagrindinio ekrano mygtuką"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"Esate pasirengę pradėti naudoti <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"įrenginys"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Sistemos naršymo nustatymai"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Bendrinti"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Išskaidyto ekrano režimą naudokite kita programa"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Jūsų organizacijoje arba naudojant šią programą neleidžiama atlikti šio veiksmo"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Atšaukti"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Praleisti"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Pasukti ekraną"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Užduočių juostos mokomoji informacija"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Užduočių juostos patarimai rodomi"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Užduočių juostos patarimai uždaryti"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Naudokite užduočių juostą, kad gal. perjungti programas"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Nuvilkite į šoną, kad gal. vienu metu naudoti dvi programas"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Palieskite ir palaikykite, kad paslėptumėte užduočių juostą"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Trumpai perbraukite, kad būtų rodoma užduočių juosta"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Kitas"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Atgal"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Uždaryti"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Naujausi"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Pranešimai"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Spartieji nustatymai"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Užduočių juosta"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Naršymo juosta"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Perkelti aukštyn, kairėn"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Perkelti žemyn, dešinėn"</string>
</resources>
diff --git a/quickstep/res/values-lv/strings.xml b/quickstep/res/values-lv/strings.xml
index ee25cb5..d167c05 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,25 +77,31 @@
<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>
- <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_button_hint" msgid="2395219947744706291">"Pieskarieties pogai Sākums, lai dotos uz sākuma ekrānu"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"Varat sākt izmantot savu ierīci (<xliff:g id="DEVICE">%1$s</xliff:g>)"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"ierīce"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Sistēmas navigācijas iestatījumi"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Kopīgot"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Izvēlieties citu lietotni, lai sadalītu ekrānu"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Lietotne vai jūsu organizācija neatļauj veikt šo darbību."</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Atcelt"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Izlaist"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Pagriezt ekrānu"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Informācija par uzdevumu joslu"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Tika atvērta uzdevumjoslas apmācība"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Tika aizvērta uzdevumjoslas apmācība"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Izmantojiet uzdevumjoslu, lai pārslēgtu lietotnes."</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Velciet uz malu, lai izmantotu divas lietotnes vienlaikus."</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Pieskarieties un turiet, lai paslēptu uzdevumjoslu."</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Īsi velciet augšup, lai skatītu uzdevumu joslu"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Tālāk"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Atpakaļ"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Aizvērt"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Nesenie"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Paziņojumi"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Ātrie iestatīj."</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Uzdevumu josla"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigācijas josla"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Pārvietot uz augšējo/kreiso stūri"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Pārvietot uz apakšējo/labo stūri"</string>
</resources>
diff --git a/quickstep/res/values-mk/strings.xml b/quickstep/res/values-mk/strings.xml
index 75f0933..07af6dd 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,25 +77,31 @@
<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_description" msgid="6350320429953234580">"Спремни сте да почнете да го користите телефонот"</string>
- <string name="allset_description_tablet" msgid="7332070270570039247">"Спремни сте да почнете да го користите таблетот"</string>
+ <string name="allset_button_hint" msgid="2395219947744706291">"Допрете го копчето за почетен екран за да одите на почетниот екран"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"Подготвени сте да почнете да го користите вашиот <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"уред"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Поставки за системска навигација"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Сподели"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Изберете друга апликација за да користите поделен екран"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Откажи"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Прескокни"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Ротирајте го екранот"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Обука за лентата со задачи"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Се појави лентата за задачи за образование"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Затворена е лентата за задачи за образование"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Префрлувајте се меѓу апликации преку лентата за задачи"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Повлечете кон страната за да користите две апликации одеднаш"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Допрете и задржете за да се сокрие лентата за задачи"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Кратко повлечете нагоре за да се прикаже лентата со задачи"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Следно"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Назад"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Затвори"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Неодамнешни"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Известувања"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Брзи поставки"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Лента со задачи"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Лента за навигација"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Премести горе лево"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Премести долу десно"</string>
</resources>
diff --git a/quickstep/res/values-ml/strings.xml b/quickstep/res/values-ml/strings.xml
index 9f00e91..5415710 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,25 +77,31 @@
<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_description" msgid="6350320429953234580">"ഫോൺ ഉപയോഗിച്ച് തുടങ്ങാൻ നിങ്ങൾ തയ്യാറാണ്"</string>
- <string name="allset_description_tablet" msgid="7332070270570039247">"ടാബ്ലെറ്റ് ഉപയോഗിച്ച് തുടങ്ങാൻ നിങ്ങൾ തയ്യാറാണ്"</string>
+ <string name="allset_button_hint" msgid="2395219947744706291">"നിങ്ങളുടെ ഹോം സ്ക്രീനിലേക്ക് പോകാൻ ഹോം ബട്ടൺ ടാപ്പ് ചെയ്യുക"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"നിങ്ങൾക്ക് <xliff:g id="DEVICE">%1$s</xliff:g> ഉപയോഗിച്ചു തുടങ്ങാം"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"ഉപകരണം"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"സിസ്റ്റം നാവിഗേഷൻ ക്രമീകരണം"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"പങ്കിടുക"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"സ്ക്രീൻ വിഭജന മോഡിന് മറ്റൊരു ആപ്പ് തിരഞ്ഞെടുക്കൂ"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"റദ്ദാക്കുക"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"ഒഴിവാക്കുക"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"സ്ക്രീൻ റൊട്ടേറ്റ് ചെയ്യുക"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"ടാസ്ക്ബാർ മാർഗ്ഗനിർദ്ദേശ വിൻഡോ"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"ടാസ്ക്ക്ബാർ വിവര പാനൽ ദൃശ്യമായി"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"ടാസ്ക്ക്ബാർ വിവര പാനൽ അടച്ചു"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"ആപ്പുകൾ മാറാൻ ടാസ്ക്ക്ബാർ ഉപയോഗിക്കുക"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"രണ്ട് ആപ്പുകൾ ഒരുമിച്ച് ഉപയോഗിക്കാൻ അരികിലേക്ക് വലിച്ചിടുക"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"ടാസ്ക്ക്ബാർ മറയ്ക്കാൻ സ്പർശിച്ച് പിടിക്കുക"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"ടാസ്ക്ബാർ ദൃശ്യമാക്കാൻ മുകളിലേക്ക് ചെറുതായി സ്വൈപ്പ് ചെയ്യൂ"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"അടുത്തത്"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"മടങ്ങുക"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"അടയ്ക്കുക"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"അടുത്തിടെയുള്ളവ"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"അറിയിപ്പുകൾ"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"ദ്രുത ക്രമീകരണം"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"ടാസ്ക്ബാർ"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"നാവിഗേഷൻ ബാർ"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"മുകളിലേക്കോ ഇടത്തേക്കോ നീക്കുക"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"താഴേക്കോ വലത്തേക്കോ നീക്കുക"</string>
</resources>
diff --git a/quickstep/res/values-mn/strings.xml b/quickstep/res/values-mn/strings.xml
index 59728a6..f9fd5fb 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,25 +77,31 @@
<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_description" msgid="6350320429953234580">"Та утсаа ашиглаж эхлэхэд бэлэн боллоо"</string>
- <string name="allset_description_tablet" msgid="7332070270570039247">"Та таблетаа ашиглаж эхлэхэд бэлэн боллоо"</string>
+ <string name="allset_button_hint" msgid="2395219947744706291">"Үндсэн нүүр лүүгээ очихын тулд нүүр хуудасны товчлуурыг товшино уу"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"Та <xliff:g id="DEVICE">%1$s</xliff:g>-г ашиглаж эхлэхэд бэлэн боллоо"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"төхөөрөмж"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Системийн навигацын тохиргоо"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Хуваалцах"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Дэлгэцийг хуваах горим ашиглах өөр апп сонгоно уу"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Цуцлах"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Алгасах"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Дэлгэцийг эргүүлэх"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Ажлын хэсгийн боловсрол"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Боловсролын ажлын талбар гарч ирсэн"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Боловсролын ажлын талбарыг хаасан"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Аппуудыг сэлгэхийн тулд талбарыг ашиглана уу"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Хоёр аппыг зэрэг ашиглахын тулд хажуу тал руу чирнэ үү"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Ажлын талбарыг нуухын тулд хүрээд удаан дарна уу"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Ажлын хэсгийг харуулахын тулд бага зэрэг дээш шударна уу"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Дараах"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Буцах"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Хаах"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Саяхны"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Мэдэгдэл"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Шуурхай тохиргоо"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Ажлын хэсэг"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Навигацын самбар"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Зүүн дээд хэсэг рүү зөөх"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Баруун доод хэсэг рүү зөөх"</string>
</resources>
diff --git a/quickstep/res/values-mr/strings.xml b/quickstep/res/values-mr/strings.xml
index 5f2fb0c..60e5a64 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,25 +77,31 @@
<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_description" msgid="6350320429953234580">"तुम्ही तुमचा फोन वापरण्यास सुरुवात करू शकता"</string>
- <string name="allset_description_tablet" msgid="7332070270570039247">"तुम्ही तुमचा टॅबलेट वापरण्यास सुरुवात करू शकता"</string>
+ <string name="allset_button_hint" msgid="2395219947744706291">"तुमच्या होम स्क्रीनवर जाण्यासाठी होम बटणावर टॅप करा"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"तुम्ही तुमचे <xliff:g id="DEVICE">%1$s</xliff:g> वापरण्यास सुरुवात करू शकता"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"डिव्हाइस"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"सिस्टीम नेव्हिगेशन सेटिंग्ज"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"शेअर करा"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"स्प्लिट स्क्रीन वापरण्यासाठी दुसरे ॲप निवडा"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"रद्द करा"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"वगळा"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"स्क्रीन फिरवा"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"टास्कबारशी संबंधित माहिती"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"टास्कबारशी संबंधित माहिती देणारे पॅनल उघडले आहे"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"टास्कबारशी संबंधित माहिती देणारे पॅनल बंद केले आहे"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"ॲप्स स्विच करण्यासाठी टास्कबार वापरा"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"दोन ॲप्स एकत्र वापरण्यासाठी, त्यांना बाजूला नेऊन ड्रॅग करा"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"टास्कबार लपवण्यासाठी स्पर्श करा आणि धरून ठेवा"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"टास्कबार दाखवण्यासाठी थोडे वरती स्वाइप करा"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"पुढे"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"मागे जा"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"बंद करा"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"अलीकडील"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"सूचना"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"क्विक सेटिंग्ज"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"टास्कबार"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"नेव्हिगेशन बार"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"सर्वात वरती/डावीकडे हलवा"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"तळाशी/उजवीकडे हलवा"</string>
</resources>
diff --git a/quickstep/res/values-ms/strings.xml b/quickstep/res/values-ms/strings.xml
index 27dcc41..b153a0a 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,25 +77,31 @@
<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>
- <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_button_hint" msgid="2395219947744706291">"Ketik butang skrin utama untuk pergi ke skrin utama anda"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"Anda sudah sedia untuk mula menggunakan <xliff:g id="DEVICE">%1$s</xliff:g> anda"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"peranti"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Tetapan navigasi sistem"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Kongsi"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Pilih apl lain untuk menggunakan skrin pisah"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Tindakan ini tidak dibenarkan oleh apl atau organisasi anda"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Batal"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Langkau"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Putar skrin"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Pendidikan bar tugas"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Pendidikan bar tugas muncul"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Pendidikan bar tugas ditutup"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Gunakan bar tugas untuk menukar apl"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Seret ke tepi untuk menggunakan dua apl serentak"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Sentuh & tahan untuk menyembunyikan bar tugas"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Leret pendek ke atas untuk menunjukkan bar tugas"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Seterusnya"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Kembali"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Tutup"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Terbaharu"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Pemberitahuan"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Tetapan Pantas"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Bar Tugas"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Bar navigasi"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Alihkan ke atas/kiri"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Alihkan ke bawah/kanan"</string>
</resources>
diff --git a/quickstep/res/values-my/strings.xml b/quickstep/res/values-my/strings.xml
index 825b6f2..b459c60 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,25 +77,31 @@
<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_description" msgid="6350320429953234580">"သင့်ဖုန်း စသုံးရန် အသင့်ဖြစ်ပါပြီ"</string>
- <string name="allset_description_tablet" msgid="7332070270570039247">"သင့်တက်ဘလက်ကို စသုံးရန် အသင့်ဖြစ်ပါပြီ"</string>
+ <string name="allset_button_hint" msgid="2395219947744706291">"ပင်မစာမျက်နှာသို့ သွားရန် ပင်မခလုတ်ကို တို့ပါ"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"<xliff:g id="DEVICE">%1$s</xliff:g> ကို စသုံးရန် အသင့်ဖြစ်ပါပြီ"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"စက်"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"စနစ် လမ်းညွှန် ဆက်တင်များ"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"မျှဝေရန်"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"မျက်နှာပြင်ခွဲ၍ပြသခြင်းသုံးရန် နောက်အက်ပ်တစ်ခုရွေးပါ"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"မလုပ်တော့"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"ကျော်ရန်"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"ဖန်သားပြင်လှည့်ရန်"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"လုပ်ဆောင်စရာဘား ပညာပေး"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"ပညာရေး လုပ်ဆောင်စရာဘား ပြထားသည်"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"ပညာရေး လုပ်ဆောင်စရာဘား ပိတ်ထားသည်"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"အက်ပ်များပြောင်းရန် လုပ်ဆောင်စရာဘားကို သုံးပါ"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"အက်ပ်နှစ်ခု တစ်ပြိုင်တည်းသုံးရန် တစ်ဖက်သို့ ဖိဆွဲပါ"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"လုပ်ဆောင်စရာဘားကို ဖျောက်ရန် ထိပြီးဖိထားနိုင်သည်"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"လုပ်ဆောင်စရာဘားပြရန် အမြန် အပေါ်ပွတ်ဆွဲပါ"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"ရှေ့သို့"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"နောက်သို့"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"ပိတ်ရန်"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"လတ်တလောများ"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"အကြောင်းကြားချက်"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"အမြန်ဆက်တင်များ"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"လုပ်ဆောင်စရာဘား"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"လမ်းညွှန်ဘား"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"အပေါ်/ဘယ်ဘက်သို့ ရွှေ့ရန်"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"အောက်ခြေ/ညာဘက်သို့ ရွှေ့ရန်"</string>
</resources>
diff --git a/quickstep/res/values-nb/strings.xml b/quickstep/res/values-nb/strings.xml
index 250724e..6b6094f 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,25 +77,31 @@
<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>
- <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_button_hint" msgid="2395219947744706291">"Trykk på hjemknappen for å gå til startskjermen"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"Nå kan du bruke <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"enheten"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Innstillinger for systemnavigasjon"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Del"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Velg en annen app for å bruke delt skjerm"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Appen eller organisasjonen din tillater ikke denne handlingen"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Avbryt"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Hopp over"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Rotér skjermen"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Veiledning for oppgavelinjen"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Opplæringen for oppgavelinjen vises"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Opplæringen for oppgavelinjen er lukket"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Bruk oppgavelinjen for å bytte app"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Dra til siden for å bruke to apper samtidig"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Trykk og hold for å skjule oppgavelinjen"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Kort sveip opp for å vise oppgavelinjen"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Neste"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Tilbake"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Lukk"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Nylige"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Varsler"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Hurtiginnst."</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Oppgavelinje"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigasjonsrad"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Flytt til øverst/venstre"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Flytt til nederst/høyre"</string>
</resources>
diff --git a/quickstep/res/values-ne/strings.xml b/quickstep/res/values-ne/strings.xml
index f7214b0..2af016e 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,25 +77,31 @@
<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_description" msgid="6350320429953234580">"तपाईं आफ्नो फोन चलाउन थाल्न सक्नुहुन्छ"</string>
- <string name="allset_description_tablet" msgid="7332070270570039247">"तपाईं अब आफ्नो ट्याब्लेट चलाउन थाल्न सक्नुहुन्छ"</string>
+ <string name="allset_button_hint" msgid="2395219947744706291">"आफ्नो होम स्क्रिनमा जान होम बटनमा ट्याप गर्नुहोस्"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"तपाईं अब आफ्नो <xliff:g id="DEVICE">%1$s</xliff:g> चलाउन थाल्न सक्नुहुन्छ"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"डिभाइस"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"सिस्टम नेभिगेसनसम्बन्धी सेटिङ"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"सेयर गर्नुहोस्"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"स्प्लिट स्क्रिन प्रयोग गर्न अर्को एप रोज्नुहोस्"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"रद्द गर्नुहोस्"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"स्किप गर्नु…"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"स्क्रिन घुमाउनुहोस्"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"टास्कबार एजुकेसन"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"टास्कबार एजुकेसन देखिएको छ"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"टास्कबार एजुकेसन बन्द गरिएको छ"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"एउटा एपबाट अर्को एपमा जान टास्कबार प्रयोग गर्नुहोस्"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"टास्कबार छेउतिर ड्र्याग गरेर एकै पटक दुई वटा एप चलाउनुहोस्"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"टास्कबार लुकाउन टास्कबार थिचिराख्नुहोस्"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"टास्कबार देखाउन माथितिर थोरै स्वाइप गर्नुहोस्"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"अर्को"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"पछाडि"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"बन्द गर्नुहोस्"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"हालसालैका बटनहरू"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"सूचनाहरू"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"द्रुत सेटिङ"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"टास्कबार"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"नेभिगेसन बार"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"सिरान/बायाँतिर सार्नुहोस्"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"फेद/दायाँतिर सार्नुहोस्"</string>
</resources>
diff --git a/quickstep/res/values-nl/strings.xml b/quickstep/res/values-nl/strings.xml
index ff5e45e..7bc235b 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,25 +77,31 @@
<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>
- <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_button_hint" msgid="2395219947744706291">"Tik op de startknop om naar je startscherm te gaan"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"Je bent klaar om je <xliff:g id="DEVICE">%1$s</xliff:g> te gebruiken"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"apparaat"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Navigatie-instellingen van systeem"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Delen"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Kies andere app om gesplitst scherm te gebruiken"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Deze actie wordt niet toegestaan door de app of je organisatie"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Annuleren"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Overslaan"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Scherm draaien"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Taakbalk Onderwijs"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Uitleg van taakbalk geopend"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Uitleg van taakbalk gesloten"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Gebruik de taakbalk om van app te wisselen"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Sleep naar de zijkant om 2 apps tegelijk te gebruiken"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Tik en houd vast om de taakbalk te verbergen"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Swipe kort omhoog om de taakbalk te bekijken"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Volgende"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Terug"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Sluiten"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Recent"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Meldingen"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Snelle instellingen"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Taakbalk"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigatiebalk"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Naar boven/links verplaatsen"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Naar beneden/rechts verplaatsen"</string>
</resources>
diff --git a/quickstep/res/values-or/strings.xml b/quickstep/res/values-or/strings.xml
index 6ab7c5e..b2f2484 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,37 +76,45 @@
<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_description" msgid="6350320429953234580">"ଆପଣ ଆପଣଙ୍କ ଫୋନ୍ ବ୍ୟବହାର କରିବା ପାଇଁ ପ୍ରସ୍ତୁତ ଅଛନ୍ତି"</string>
- <string name="allset_description_tablet" msgid="7332070270570039247">"ଆପଣ ଆପଣଙ୍କ ଟାବଲେଟ ବ୍ୟବହାର କରିବା ଆରମ୍ଭ କରିବାକୁ ପ୍ରସ୍ତୁତ ଅଛନ୍ତି"</string>
+ <string name="allset_hint" msgid="2384632994739392447">"ହୋମକୁ ଯିବା ପାଇଁ ଉପରକୁ ସ୍ୱାଇପ କରନ୍ତୁ"</string>
+ <string name="allset_button_hint" msgid="2395219947744706291">"ଆପଣଙ୍କ ହୋମ ସ୍କ୍ରିନକୁ ଯିବା ପାଇଁ ହୋମ ବଟନରେ ଟାପ କରନ୍ତୁ"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"ଆପଣ ଆପଣଙ୍କ <xliff:g id="DEVICE">%1$s</xliff:g> ବ୍ୟବହାର କରିବା ଆରମ୍ଭ କରିବାକୁ ପ୍ରସ୍ତୁତ ଅଛନ୍ତି"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"ଡିଭାଇସ"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"ସିଷ୍ଟମ ନାଭିଗେସନ ସେଟିଂସ"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"ସେୟାର୍ କରନ୍ତୁ"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନ ବ୍ୟବହାର କରିବାକୁ ଅନ୍ୟ ଏକ ଆପ ବାଛନ୍ତୁ"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"ବାତିଲ କରନ୍ତୁ"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"ବାଦ୍ ଦିଅନ୍ତୁ"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"ସ୍କ୍ରିନ ଘୂରାନ୍ତୁ"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"ଟାସ୍କବାର ଶିକ୍ଷା"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"ଟାସ୍କବାର୍ ଶିକ୍ଷା ଦେଖାଯାଇଛି"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"ଟାସ୍କବାର୍ ଶିକ୍ଷା ବନ୍ଦ ହୋଇଯାଇଛି"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"ଆପଗୁଡ଼ିକୁ ସ୍ୱିଚ କରିବା ପାଇଁ ଟାସ୍କବାର ବ୍ୟବହାର କରନ୍ତୁ"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"ଥରକେ ଦୁଇଟି ଆପ ବ୍ୟବହାର କରିବା ପାଇଁ ପାର୍ଶ୍ୱକୁ ଡ୍ରାଗ କରନ୍ତୁ"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"ଟାସ୍କବାରକୁ ଲୁଚାଇବା ପାଇଁ ସ୍ପର୍ଶ କରି ଧରି ରଖନ୍ତୁ"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"ଟାସ୍କବାର ଦେଖାଇବା ପାଇଁ ଉପରକୁ ଅଳ୍ପ ସମୟ ସ୍ୱାଇପ କରନ୍ତୁ"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"ପରବର୍ତ୍ତୀ"</string>
<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>
<string name="taskbar_button_recents" msgid="7273376136216613134">"ବର୍ତ୍ତମାନର"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"କ୍ୱିକ ସେଟିଂସ"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"ଟାସ୍କବାର"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"ନାଭିଗେସନ ବାର"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ଶୀର୍ଷ/ବାମକୁ ମୁଭ କରନ୍ତୁ"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ନିମ୍ନ/ଡାହାଣକୁ ମୁଭ କରନ୍ତୁ"</string>
</resources>
diff --git a/quickstep/res/values-pa/strings.xml b/quickstep/res/values-pa/strings.xml
index 83def85..963486d 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,25 +77,31 @@
<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_description" msgid="6350320429953234580">"ਤੁਸੀਂ ਆਪਣਾ ਫ਼ੋਨ ਵਰਤਣ ਲਈ ਤਿਆਰ ਹੋ"</string>
- <string name="allset_description_tablet" msgid="7332070270570039247">"ਤੁਸੀਂ ਆਪਣਾ ਟੈਬਲੈੱਟ ਵਰਤਣ ਲਈ ਤਿਆਰ ਹੋ"</string>
+ <string name="allset_button_hint" msgid="2395219947744706291">"ਆਪਣੀ ਹੋਮ ਸਕ੍ਰੀਨ \'ਤੇ ਜਾਣ ਲਈ ਹੋਮ ਬਟਨ \'ਤੇ ਟੈਪ ਕਰੋ"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"ਤੁਸੀਂ ਆਪਣਾ <xliff:g id="DEVICE">%1$s</xliff:g> ਵਰਤਣ ਲਈ ਤਿਆਰ ਹੋ"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"ਡੀਵਾਈਸ"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"ਸਿਸਟਮ ਨੈਵੀਗੇਸ਼ਨ ਸੈਟਿੰਗਾਂ"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"ਸਾਂਝਾ ਕਰੋ"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਵਰਤਣ ਲਈ ਕਿਸੇ ਹੋਰ ਐਪ ਨੂੰ ਚੁਣੋ"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"ਰੱਦ ਕਰੋ"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"ਛੱਡੋ"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"ਸਕ੍ਰੀਨ ਘੁਮਾਓ"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"ਟਾਸਕਬਾਰ ਸਿੱਖਿਆ"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"ਟਾਸਕਵਾਰ ਸਿੱਖਿਆ ਪੈਨਲ ਦਿਖਾਇਆ ਗਿਆ"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"ਟਾਸਕਵਾਰ ਸਿੱਖਿਆ ਪੈਨਲ ਬੰਦ ਕੀਤਾ ਗਿਆ"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"ਐਪਾਂ ਸਵਿੱਚ ਕਰਨ ਲਈ ਟਾਸਕਬਾਰ ਵਰਤੋ"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"ਇੱਕ ਵਾਰ ਵਿੱਚ ਦੋ ਐਪਾਂ ਵਰਤਣ ਲਈ ਉਨ੍ਹਾਂ ਨੂੰ ਸਾਈਡ ਵੱਲ ਘਸੀਟੋ"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"ਟਾਸਕਬਾਰ ਨੂੰ ਲੁਕਾਉਣ ਲਈ ਸਪਰਸ਼ ਕਰਕੇ ਰੱਖੋ"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"ਟਾਸਕਬਾਰ ਦਿਖਾਉਣ ਲਈ ਥੋੜ੍ਹਾ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰੋ"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"ਅੱਗੇ"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"ਪਿੱਛੇ"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"ਬੰਦ ਕਰੋ"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"ਹਾਲੀਆ"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"ਸੂਚਨਾਵਾਂ"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"ਤਤਕਾਲ ਸੈਟਿੰਗਾਂ"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"ਟਾਸਕਬਾਰ"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"ਨੈਵੀਗੇਸ਼ਨ ਵਾਲੀ ਪੱਟੀ"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ਸਿਖਰਲੇ/ਖੱਬੇ ਪਾਸੇ ਲੈ ਕੇ ਜਾਓ"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ਹੇਠਾਂ/ਸੱਜੇ ਪਾਸੇ ਲੈ ਕੇ ਜਾਓ"</string>
</resources>
diff --git a/quickstep/res/values-pl/strings.xml b/quickstep/res/values-pl/strings.xml
index 077982a..f9d0853 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,25 +77,31 @@
<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>
- <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_button_hint" msgid="2395219947744706291">"Kliknij przycisk ekranu głównego, aby otworzyć ekran główny"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"Teraz możesz zacząć używać urządzenia <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"urządzenie"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Ustawienia nawigacji w systemie"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Udostępnij"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Wybierz drugą aplikację, aby podzielić ekran"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Nie możesz wykonać tego działania, bo nie zezwala na to aplikacja lub Twoja organizacja"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Anuluj"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Pomiń"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Obróć ekran"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Informacje o pasku aplikacji"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Wskazówki na temat paska zadań zostały wyświetlone"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Wskazówki na temat paska zadań zostały zamknięte"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Używaj paska zadań, aby przełączać aplikacje"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Przeciągnij w bok, aby używać 2 aplikacji jednocześnie"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Naciśnij i przytrzymaj, aby ukryć pasek zadań"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Przesuń palcem krótko w górę, aby wyświetlić pasek aplikacji"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Dalej"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Wstecz"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Zamknij"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Ostatnie"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Powiadomienia"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Szybkie ustawienia"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Pasek aplikacji"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Pasek nawigacyjny"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Przesuń w górny lewy róg"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Przesuń w dolny prawy róg"</string>
</resources>
diff --git a/quickstep/res/values-pt-rPT/strings.xml b/quickstep/res/values-pt-rPT/strings.xml
index 124b33b..075c5be 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,25 +77,31 @@
<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>
- <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_button_hint" msgid="2395219947744706291">"Toque no botão página inicial para aceder ao ecrã principal"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"Já pode começar a usar o seu <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"dispositivo"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Definições de navegação do sistema"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Partilhar"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Escolher outra app para usar o ecrã dividido"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Esta ação não é permitida pela app ou a sua entidade."</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Cancelar"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Ignorar"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Rodar ecrã"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Educação da Barra de tarefas"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Informação da barra de tarefas apresentada"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Informação da barra de tarefas fechada"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Utilize a barra de ferramentas para alternar entre apps"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Arraste para o lado para utilizar duas apps em simultâneo"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Toque sem soltar para ocultar a barra de tarefas"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Deslize rápido curto para cima para ver a barra de tarefas"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Seguinte"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Anterior"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Fechar"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Recentes"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Notificações"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Definiç. rápidas"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Barra de tarefas"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Barra de navegação"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mover para a parte superior esquerda"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mover para a part superior direita"</string>
</resources>
diff --git a/quickstep/res/values-pt/strings.xml b/quickstep/res/values-pt/strings.xml
index 199c2f4..3445826 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,25 +77,31 @@
<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>
- <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_button_hint" msgid="2395219947744706291">"Toque no botão home para ir para a tela inicial"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"Você já pode começar a usar seu <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"dispositivo"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Configurações de navegação do sistema"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Compartilhar"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Escolha outro app para usar na tela dividida"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Essa ação não é permitida pelo app ou pela organização"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Cancelar"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Pular"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Girar a tela"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Informações sobre a barra de tarefas"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"As dicas sobre a barra de tarefas foram abertas"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"As dicas sobre a barra de tarefas foram fechadas"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Use a barra de tarefas para alternar entre apps"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Arraste para o lado e use dois apps ao mesmo tempo"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Mantenha a barra de tarefas pressionada para ocultá-la"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Deslize para cima para mostrar a barra de tarefas"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Próxima"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Voltar"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Fechar"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Recentes"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Notificações"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Config. rápidas"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Barra de tarefas"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Barra de navegação"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mover para cima/para a esquerda"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mover para baixo/para a direita"</string>
</resources>
diff --git a/quickstep/res/values-ro/strings.xml b/quickstep/res/values-ro/strings.xml
index ba4badf..031a11d 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,25 +77,31 @@
<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>
- <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_button_hint" msgid="2395219947744706291">"Atinge butonul ecran de pornire ca să accesezi ecranul de pornire"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"Ești gata să folosești <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"dispozitivul"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Setările de navigare ale sistemului"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Distribuie"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Alege altă aplicație pentru ecranul împărțit"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Această acțiune nu este permisă de aplicație sau de organizația ta"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Anulează"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Omite"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Rotește ecranul"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Informații despre bara de activități"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Panoul cu informații despre bara de activități s-a afișat"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Panoul cu informații despre bara de activități s-a închis"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Folosește bara de activități ca să comuți între aplicații"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Trage în lateral ca să folosești două aplicații deodată"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Atinge lung oricând pentru a ascunde bara de activități"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Glisează scurt în sus pentru a afișa bara de activități"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Înainte"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Înapoi"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Închide"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Recente"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Notificări"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Setări rapide"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Bară de activități"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Bară de navigare"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mută în stânga sus"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mută în dreapta jos"</string>
</resources>
diff --git a/quickstep/res/values-ru/strings.xml b/quickstep/res/values-ru/strings.xml
index 44b72cf..3924dee 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,25 +77,31 @@
<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_description" msgid="6350320429953234580">"Теперь вы можете использовать телефон."</string>
- <string name="allset_description_tablet" msgid="7332070270570039247">"Теперь вы можете использовать планшет."</string>
+ <string name="allset_button_hint" msgid="2395219947744706291">"Нажмите кнопку главного экрана, чтобы открыть его."</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"Теперь вы можете использовать <xliff:g id="DEVICE">%1$s</xliff:g>."</string>
+ <string name="default_device_name" msgid="6660656727127422487">"устройство"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Системные настройки навигации"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Поделиться"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Выберите другое приложение для разделения экрана."</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Отмена"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Пропустить"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Повернуть экран"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Обучение по работе с панелью задач"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Обучение по работе с панелью задач показано"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Обучение по работе с панелью задач скрыто"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Используйте панель задач, чтобы переключать приложения."</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Перетащите в сторону, чтобы использовать два приложения сразу."</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Чтобы скрыть панель задач, коснитесь ее и удерживайте."</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Чтобы открыть панель задач, быстро проведите снизу вверх."</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Далее"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Назад"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Закрыть"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Недавние"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Уведомления"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Быстрые настройки"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Панель задач"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Панель навигации"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Переместить вверх или влево"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Переместить вниз или вправо"</string>
</resources>
diff --git a/quickstep/res/values-si/strings.xml b/quickstep/res/values-si/strings.xml
index 31749cc..6f9734a 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,25 +77,31 @@
<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_description" msgid="6350320429953234580">"ඔබ ඔබගේ දුරකථනය භාවිත කිරීම පටන් ගැනීමට සූදානම්"</string>
- <string name="allset_description_tablet" msgid="7332070270570039247">"ඔබ ඔබගේ ටැබ්ලටය භාවිත කිරීම පටන් ගැනීමට සූදානම්"</string>
+ <string name="allset_button_hint" msgid="2395219947744706291">"ඔබේ මුල් තිරය වෙත යාමට මුල් පිටුව බොත්තම තට්ටු කරන්න"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"ඔබ ඔබේ <xliff:g id="DEVICE">%1$s</xliff:g> භාවිත කිරීම පටන් ගැනීමට සූදානම්"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"උපාංගය"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"පද්ධති සංචාලන සැකසීම්"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"බෙදා ගන්න"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"බෙදීම් තිරය භාවිතා කිරීමට වෙනත් යෙදුමක් තෝරා ගන්න"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"අවලංගු කරන්න"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"මඟ හරින්න"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"තිරය කරකවන්න"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"කාර්ය තීරු අධ්යාපනය"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"කාර්ය තීරු අධ්යාපනය දිස් විය"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"කාර්ය තීරු අධ්යාපනය වසා ඇත"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"යෙදුම් මාරු කිරීමට කාර්ය තීරුව භාවිත කරන්න"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"එකවර යෙදුම් දෙකක් භාවිතා කිරීමට පැත්තට අදින්න"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"කාර්ය තීරුව සැඟවීමට ස්පර්ශ කර අල්ලා ගෙන සිටින්න"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"කාර්ය තීරුව පෙන්වීමට ඉහළට කෙටි ස්වයිප් කරන්න"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"ඊළඟ"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"ආපසු"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"වසන්න"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"මෑත"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"දැනුම්දීම්"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"ඉක්මන් සැකසීම්"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"කාර්ය තීරුව"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"සංචලන තීරුව"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ඉහළ/වම වෙත ගෙන යන්න"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"පහළ/දකුණ වෙත ගෙන යන්න"</string>
</resources>
diff --git a/quickstep/res/values-sk/strings.xml b/quickstep/res/values-sk/strings.xml
index 5d51d45..672e730 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,25 +77,31 @@
<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>
- <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_button_hint" msgid="2395219947744706291">"Na plochu prejdete klepnutím na tlačidlo plochy"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"<xliff:g id="DEVICE">%1$s</xliff:g> môžete začať používať"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"zariadenie"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Nastavenia navigácie systémom"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Zdieľať"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Na použitie rozd. obrazovky vyberte inú aplikáciu"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Aplikácia alebo vaša organizácia túto akciu nepovoľuje"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Zrušiť"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Preskočiť"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Otočiť obrazovku"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Panel vzdelávacích aplikácií"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Zobrazila sa výuka k hlavnému panelu"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Výuka k hlavnému panelu bola zatvorená"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Aplikácie je možné prepínať pomocou panela úloh"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Po presunutí na stranu je možné používať dve aplikácie naraz"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Panel úloh skryjete pridržaním"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Krátkym potiahnutím nahor zobrazíte panel aplikácií"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Ďalej"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Späť"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Zavrieť"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Nedávne"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Upozornenia"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Rýchle nastavenia"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Panel aplikácií"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigačný panel"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Presunúť hore alebo doľava"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Presunúť dole alebo doprava"</string>
</resources>
diff --git a/quickstep/res/values-sl/strings.xml b/quickstep/res/values-sl/strings.xml
index e196e5d..3753acf 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,25 +77,31 @@
<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>
- <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_button_hint" msgid="2395219947744706291">"Za pomik na začetni zaslon se dotaknite gumba za začetni zaslon."</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"Pripravljeni ste, da začnete uporabljati <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"napravo"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Nastavitve krmarjenja po sistemu"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Deli"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Izberite drugo aplikacijo za uporabo razdeljenega zaslona."</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Aplikacija ali vaša organizacija ne dovoljuje tega dejanja"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Prekliči"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Preskoči"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Sukanje zaslona"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Poučni nasveti o opravilni vrstici"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Poučni nasveti o opravilni vrstici so prikazani."</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Poučni nasveti o opravilni vrstici so zaprti."</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Za preklop aplikacij uporabite opravilno vrstico."</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Povlecite vstran za uporabo dveh aplikacij hkrati."</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Pridržite, če želite opravilno vrstico skriti."</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Na kratko povlecite navzgor za prikaz opravilne vrstice."</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Naprej"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Nazaj"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Zapri"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Nedavno"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Obvestila"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Hitre nastavitve"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Opravilna vrstica"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Vrstica za krmarjenje"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Premakni na vrh/levo"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Premakni na dno/desno"</string>
</resources>
diff --git a/quickstep/res/values-sq/strings.xml b/quickstep/res/values-sq/strings.xml
index 39771cb..240eac7 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,25 +77,31 @@
<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>
- <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_button_hint" msgid="2395219947744706291">"Trokit te butoni \"kreu\" për të shkuar tek ekrani bazë"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"Je gati që të fillosh të përdorësh <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"pajisje"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Cilësimet e navigimit të sistemit"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Ndaj"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Zgjidh një aplikacion tjetër për të përdorur ekranin e ndarë"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Ky veprim nuk lejohet nga aplikacioni ose organizata jote"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Anulo"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Kapërce"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Rrotullo ekranin"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Edukimi për shiritin e detyrave"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Edukimi i shiritit të detyrave u shfaq"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Edukimi nga shiriti i detyrave u mbyll"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Përdor shiritin e detyrave për të ndryshuar aplikacionet"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Zvarrit anash për të përdorur të dyja aplikacionet njëherësh"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Preke dhe mbaje prekur për ta fshehur shiritin e detyrave"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Kryej një rrëshqitje të shkurtër lart për të shfaqur shiritin e detyrave"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Para"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Pas"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Mbyll"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Të fundit"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Njoftimet"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Cilësimet shpejt"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Shiriti i detyrave"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Shiriti i navigimit"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Lëviz në krye/majtas"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Lëviz në fund/djathtas"</string>
</resources>
diff --git a/quickstep/res/values-sr/strings.xml b/quickstep/res/values-sr/strings.xml
index 067a7fe..8323a0f 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,25 +77,31 @@
<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_description" msgid="6350320429953234580">"Спремни сте да почнете да користите телефон"</string>
- <string name="allset_description_tablet" msgid="7332070270570039247">"Спремни сте да почнете да користите таблет"</string>
+ <string name="allset_button_hint" msgid="2395219947744706291">"Додирните дугме Почетак да бисти ишли на почетни екран"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"Спремни сте да почнете да користите <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"уређај"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Подешавања кретања кроз систем"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Дели"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Одаберите другу апликацију за подељени екран"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Откажи"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Прескочи"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Ротирајте екран"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Упутства на траци задатака"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Едукативно окно из траке задатака се појавило"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Едукативно окно из траке задатака је затворено"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Користите траку задатака да бисте мењали апликације"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Превуците на страну да користите две апликације одједном"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Додирните и задржите за скривање траке задатака"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Накратко превуците нагоре да бисте приказали траку задатака"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Даље"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Назад"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Затвори"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Недавно"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Обавештења"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Брза подешавања"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Трака задатака"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Трака за навигацију"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Премести горе лево"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Премести доле десно"</string>
</resources>
diff --git a/quickstep/res/values-sv/strings.xml b/quickstep/res/values-sv/strings.xml
index 3bb47c3..387eb69 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,25 +77,31 @@
<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>
- <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_button_hint" msgid="2395219947744706291">"Tryck på hemknappen för att öppna startskärmen"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"Nu kan du börja använda din <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"enhet"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Systemnavigeringsinställningar"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Dela"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Välj en annan app för att använda delad skärm"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Appen eller organisationen tillåter inte den här åtgärden"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Avbryt"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Hoppa över"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Rotera skärmen"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Aktivitetsfältsutbildning"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Information om aktivitetsfältet visades"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Information om aktivitetsfältet stängdes"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Använd aktivitetsfältet för att byta mellan appar"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Dra till sidan om du vill använda två appar samtidigt"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Tryck länge för att dölja aktivitetsfältet"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Svep en kort bit uppåt för att visa aktivitetsfältet"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Nästa"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Tillbaka"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Stäng"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Senaste"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Aviseringar"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Snabbinställn."</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Aktivitetsfält"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigeringsfält"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Flytta högst upp/till vänster"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Flytta längst ned/till höger"</string>
</resources>
diff --git a/quickstep/res/values-sw/strings.xml b/quickstep/res/values-sw/strings.xml
index 16891a8..a0360f1 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,25 +77,31 @@
<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>
- <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_button_hint" msgid="2395219947744706291">"Gusa kitufe cha ukurasa wa mwanzo ili uende kwenye skrini ya kwanza"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"Uko tayari kuanza kutumia <xliff:g id="DEVICE">%1$s</xliff:g> yako"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"kifaa"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Mipangilio ya usogezaji kwenye mfumo"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Shiriki"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Chagua programu nyingine ili utumie hali ya kugawa skrini"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Kitendo hiki hakiruhusiwi na programu au shirika lako"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Ghairi"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Ruka"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Zungusha skrini"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Elimu ya Upauzana"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Paneli ya elimu kwenye upau wa shughuli inaonyeshwa"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Paneli ya elimu kwenye upau wa shughuli imefungwa"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Tumia upau wa shughuli kubadilisha programu"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Buruta pembeni ili utumie programu mbili kwa wakati mmoja"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Gusa na ushikilie ili ufiche upau wa shughuli"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Telezesha kidole juu ili uonyeshe upauzana"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Endelea"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Nyuma"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Funga"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Vilivyotumika majuzi"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Arifa"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Mipangilio ya Haraka"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Upauzana"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Sehemu ya viungo muhimu"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Sogeza juu/kushoto"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Sogeza chini/kulia"</string>
</resources>
diff --git a/quickstep/res/values-sw600dp-land/dimens.xml b/quickstep/res/values-sw600dp-land/dimens.xml
index 0cd9b2b..4ee388a 100644
--- a/quickstep/res/values-sw600dp-land/dimens.xml
+++ b/quickstep/res/values-sw600dp-land/dimens.xml
@@ -15,6 +15,7 @@
*/
-->
<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 7494683..daf1f63 100644
--- a/quickstep/res/values-sw600dp/dimens.xml
+++ b/quickstep/res/values-sw600dp/dimens.xml
@@ -14,16 +14,31 @@
* limitations under the License.
*/
-->
+<!-- Applies to small tablet screens -->
<resources>
<dimen name="navigation_key_padding">25dp</dimen>
- <!-- Task View -->
+ <!-- Overview Task Views -->
+ <!-- A touch target for icons, sometimes slightly larger than the icons themselves -->
<dimen name="task_thumbnail_icon_size">48dp</dimen>
+ <!-- The icon size for the focused task, placed in center of touch target -->
<dimen name="task_thumbnail_icon_drawable_size">44dp</dimen>
+ <!-- The space under the focused task icon -->
<dimen name="overview_task_margin">12dp</dimen>
+ <!-- The icon size of all non-focused task icons, placed in center of touch target -->
<dimen name="task_thumbnail_icon_drawable_size_grid">44dp</dimen>
- <dimen name="overview_task_margin_grid">12dp</dimen>
+ <!-- The space between grid rows (when there's 2 rows of thumbnails) -->
<dimen name="overview_grid_row_spacing">28dp</dimen>
+ <!-- The horizontal space between tasks -->
<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>
+ <!-- Overview actions -->
+ <dimen name="overview_actions_top_margin">24dp</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 ceaa8f8..d27561a 100644
--- a/quickstep/res/values-sw720dp/dimens.xml
+++ b/quickstep/res/values-sw720dp/dimens.xml
@@ -14,14 +14,31 @@
* limitations under the License.
*/
-->
+<!-- Applies to large tablet screens -->
<resources>
- <!-- Task View -->
+ <!-- Overview Task Views -->
+ <!-- The primary task thumbnail uses up to this much of the total screen height/width -->
+ <item name="overview_max_scale" format="float" type="dimen">0.7</item>
+ <!-- A touch target for icons, sometimes slightly larger than the icons themselves -->
<dimen name="task_thumbnail_icon_size">48dp</dimen>
+ <!-- The icon size for the focused task, placed in center of touch target -->
<dimen name="task_thumbnail_icon_drawable_size">44dp</dimen>
+ <!-- The space under the focused task icon -->
<dimen name="overview_task_margin">16dp</dimen>
+ <!-- The icon size of all non-focused task icons, placed in center of touch target -->
<dimen name="task_thumbnail_icon_drawable_size_grid">44dp</dimen>
- <dimen name="overview_task_margin_grid">16dp</dimen>
+ <!-- The space between grid rows (when there's 2 rows of thumbnails) -->
<dimen name="overview_grid_row_spacing">36dp</dimen>
+ <!-- The horizontal space between tasks -->
<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>
+
+ <!-- Transient taskbar -->
+ <dimen name="transient_taskbar_size">76dp</dimen>
+ <dimen name="transient_taskbar_icon_size">52dp</dimen>
</resources>
diff --git a/quickstep/res/values-ta/strings.xml b/quickstep/res/values-ta/strings.xml
index 5c73734..edbf3ac 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,25 +77,31 @@
<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_description" msgid="6350320429953234580">"மொபைலைப் பயன்படுத்தத் தயாராகிவிட்டீர்கள்"</string>
- <string name="allset_description_tablet" msgid="7332070270570039247">"உங்கள் டேப்லெட்டைப் பயன்படுத்தத் தயாராகிவிட்டீர்கள்"</string>
+ <string name="allset_button_hint" msgid="2395219947744706291">"முகப்புத் திரைக்குச் செல்வதற்கு முகப்பு பட்டனைத் தட்டவும்"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"உங்கள் <xliff:g id="DEVICE">%1$s</xliff:g> சாதனத்தைப் பயன்படுத்தத் தயாராகிவிட்டீர்கள்"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"சாதனம்"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"சிஸ்டம் வழிசெலுத்தல் அமைப்புகள்"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"பகிர்"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"திரைப் பிரிப்பை பயன்படுத்த வேறு ஆப்ஸை தேர்வுசெய்க"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"ரத்துசெய்"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"தவிர்"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"திரையைச் சுழற்றும்"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"செயல் பட்டியைப் பயன்படுத்தும் விதம்"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"பணிப்பட்டியை எவ்வாறு பயன்படுத்துவது என்பது பற்றிய பலகம் காட்டப்படுகிறது"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"பணிப்பட்டியை எவ்வாறு பயன்படுத்துவது என்பது பற்றிய பலகம் மூடப்பட்டது"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"ஆப்ஸிற்கு இடையே மாற பணிப்பட்டியைப் பயன்படுத்தவும்"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"ஒரே நேரத்தில் இரு ஆப்ஸை உபயோகிக்க பக்கவாட்டிற்கு இழுக்கவும்"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"பணிப்பட்டியை மறைக்கத் தொட்டுப் பிடிக்கவும்"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"செயல் பட்டியைக் காட்டுவதற்கு மேலே சிறிது ஸ்வைப் செய்யவும்"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"அடுத்து"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"பின்செல்"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"மூடுக"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"சமீபத்தியவை"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"அறிவிப்புகள்"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"விரைவு அமைப்புகள்"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"செயல் பட்டி"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"வழிசெலுத்தல் பட்டி"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"மேலே/இடதுபுறம் நகர்த்தும்"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"கீழே/வலதுபுறம் நகர்த்தும்"</string>
</resources>
diff --git a/quickstep/res/values-te/strings.xml b/quickstep/res/values-te/strings.xml
index 332e95d..46e7435 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,25 +77,31 @@
<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_description" msgid="6350320429953234580">"మీరు మీ ఫోన్ను ఉపయోగించడానికి సిద్ధంగా ఉన్నారు"</string>
- <string name="allset_description_tablet" msgid="7332070270570039247">"మీరు మీ టాబ్లెట్ను ఉపయోగించడానికి సిద్ధంగా ఉన్నారు"</string>
+ <string name="allset_button_hint" msgid="2395219947744706291">"మీ మొదటి స్క్రీన్కు వెళ్లడానికి హోమ్ బటన్ను ట్యాప్ చేయండి"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"మీరు ఇప్పుడు మీ <xliff:g id="DEVICE">%1$s</xliff:g>ను ఉపయోగించడం ప్రారంభించవచ్చు"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"పరికరం"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"సిస్టమ్ నావిగేషన్ సెట్టింగ్లు"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"షేర్ చేయండి"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"స్ప్లిట్ స్క్రీన్ ఉపయోగానికి మరొక యాప్ ఎంచుకోండి"</string>
<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>
- <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"రద్దు చేయి"</string>
+ <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"రద్దు చేయండి"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"స్కిప్ చేయండి"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"స్క్రీన్ను తిప్పండి"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"టాస్క్బార్ ఎడ్యుకేషన్"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"టాస్క్బార్ శిక్షణకు సంబంధించిన ప్యానెల్ కనిపించింది"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"టాస్క్బార్ శిక్షణకు సంబంధించిన ప్యానెల్ మూసివేయబడింది"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"యాప్లను స్విచ్ చేయడానికి టాస్క్బార్ను ఉపయోగించండి"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"ఒకేసారి రెండు యాప్లను ఉపయోగించడానికి పక్కకు లాగండి"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"టాస్క్బార్ను దాచడానికి తాకి, నొక్కి ఉంచండి"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"టాస్క్బార్ను చూపడానికి చిన్నగా పైకి స్వైప్ చేయండి"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"తర్వాత"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"వెనుకకు"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"మూసివేయండి"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"ఇటీవలివి"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"నోటిఫికేషన్లు"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"క్విక్ సెట్టింగ్లు"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"టాస్క్బార్"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"నావిగేషన్ బార్"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ఎగువ/ఎడమ వైపునకు తరలించండి"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"దిగువ/కుడి వైపునకు తరలించండి"</string>
</resources>
diff --git a/quickstep/res/values-th/strings.xml b/quickstep/res/values-th/strings.xml
index 59a84ff..0a7c184 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,25 +77,31 @@
<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_description" msgid="6350320429953234580">"คุณเริ่มใช้โทรศัพท์ได้แล้ว"</string>
- <string name="allset_description_tablet" msgid="7332070270570039247">"คุณเริ่มใช้แท็บเล็ตได้แล้ว"</string>
+ <string name="allset_button_hint" msgid="2395219947744706291">"แตะปุ่มหน้าแรกเพื่อไปที่หน้าจอหลัก"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"คุณเริ่มใช้<xliff:g id="DEVICE">%1$s</xliff:g>ได้แล้ว"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"อุปกรณ์"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"การตั้งค่าการนำทางของระบบ"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"แชร์"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"เลือกแอปอื่นเพื่อใช้การแยกหน้าจอ"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"ยกเลิก"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"ข้าม"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"หมุนหน้าจอ"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"แถบงาน Education"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"แถบงาน Education ปรากฎขึ้น"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"ปิดแถบงาน Education แล้ว"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"ใช้แถบงานเพื่อเปลี่ยนแอป"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"ลากไปด้านข้างเพื่อใช้ 2 แอปพร้อมกัน"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"แตะค้างไว้เพื่อซ่อนแถบงาน"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"ปัดขึ้นสั้นๆ เพื่อแสดงแถบงาน"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"ถัดไป"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"กลับ"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"ปิด"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"ล่าสุด"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"การแจ้งเตือน"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"การตั้งค่าด่วน"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"แถบงาน"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"แถบนำทาง"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ย้ายไปที่ด้านบนหรือด้านซ้าย"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ย้ายไปที่ด้านล่างหรือด้านขวา"</string>
</resources>
diff --git a/quickstep/res/values-tl/strings.xml b/quickstep/res/values-tl/strings.xml
index b22d85d..aa69f71 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,25 +77,31 @@
<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>
- <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_button_hint" msgid="2395219947744706291">"I-tap ang button ng home para pumunta sa iyong home screen"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"Handa mo nang simulan ang paggamit sa iyong <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"device"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Mga setting ng navigation ng system"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Ibahagi"</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">"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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Pumili ng ibang app para gamitin ang split screen"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Hindi pinapayagan ng app o ng iyong organisasyon ang pagkilos na ito"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Kanselahin"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Laktawan"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"I-rotate ang screen"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Impormasyon sa taskbar"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Lumabas ang edukasyon sa taskbar"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Sarado ang edukasyon sa taskbar"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Gamitin ang taskbar para magpalipat-lipat sa mga app"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"I-drag sa gilid para makagamit ng dalawang app nang sabay"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Pindutin nang matagal para itago ang taskbar"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Mag-swipe nang bahagya pataas para ipakita ang taskbar"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Susunod"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Bumalik"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Isara"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Mga Kamakailan"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Mga Notification"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Quick Settings"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Taskbar"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigation bar"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Ilipat sa itaas/kaliwa"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Ilipat sa ibaba/kanan"</string>
</resources>
diff --git a/quickstep/res/values-tr/strings.xml b/quickstep/res/values-tr/strings.xml
index b3054f7..3347074 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,25 +77,31 @@
<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>
- <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_button_hint" msgid="2395219947744706291">"Ana ekranınıza gitmek için ana sayfa düğmesine dokunun"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"<xliff:g id="DEVICE">%1$s</xliff:g> cihazınızı kullanmaya hazırsınız"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"cihaz"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Sistem gezinme ayarları"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Paylaş"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Bölünmüş ekran kullanmak için başka bir uygulama seçin"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Uygulamanız veya kuruluşunuz bu işleme izin vermiyor"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"İptal"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Atla"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Ekranı döndür"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Görev çubuğu eğitimi"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Görev çubuğu eğitimi görüntülendi"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Görev çubuğu eğitimi kapatıldı"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Görev çubuğundan uygulamalar arasında geçiş yapabilirsiniz"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Tek seferde iki uygulamayı kullanmak için yana sürükleyin"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Görev çubuğunu gizlemek için basılı tutun"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Görev çubuğunu göstermek için yukarı doğru kısa kaydırın"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"İleri"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Geri"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Kapat"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Son Kullanılanlar"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Bildirimler"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Hızlı Ayarlar"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Görev çubuğu."</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Gezinme çubuğu"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Sol üste taşı"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Sağ alta taşı"</string>
</resources>
diff --git a/quickstep/res/values-uk/strings.xml b/quickstep/res/values-uk/strings.xml
index 490ac2d..ee0314b 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,25 +77,31 @@
<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_description" msgid="6350320429953234580">"Тепер ви можете користуватися телефоном"</string>
- <string name="allset_description_tablet" msgid="7332070270570039247">"Тепер ви можете користуватися планшетом"</string>
+ <string name="allset_button_hint" msgid="2395219947744706291">"Натисніть кнопку головного екрана, щоб відкрити його"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"Тепер ви можете користуватися цим пристроєм: <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"пристрій"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Системні налаштування навігації"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Поділитися"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Щоб розділити екран, виберіть ще один додаток"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Скасувати"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Пропустити"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Обернути екран"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Панель завдань Education"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Панель завдань Education відкрито"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Панель завдань Education закрито"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Переходьте між додатками за допомогою панелі завдань"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Перетягніть убік, щоб використовувати два додатки одночасно"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Натисніть і втримуйте панель завдань, щоб сховати її"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Швидко проведіть пальцем угору, щоб відкрити панель завдань"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Далі"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Назад"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Закрити"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Нещодавні"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Сповіщення"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Швидкі налаштув."</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Панель завдань"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Панель навігації"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Перемістити вгору або вліво"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Перемістити вниз або вправо"</string>
</resources>
diff --git a/quickstep/res/values-ur/strings.xml b/quickstep/res/values-ur/strings.xml
index 8f2da65..93c47df 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,25 +77,31 @@
<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_description" msgid="6350320429953234580">"آپ اپنا فون استعمال شروع کرنے کے لیے تیار ہیں"</string>
- <string name="allset_description_tablet" msgid="7332070270570039247">"آپ اپنے ٹیبلیٹ کا استعمال شروع کرنے کے لیے تیار ہیں"</string>
+ <string name="allset_button_hint" msgid="2395219947744706291">"اپنی ہوم اسکرین پر جانے کے لیے ہوم بٹن پر تھپتھپائیں"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"آپ اپنے <xliff:g id="DEVICE">%1$s</xliff:g> کا استعمال شروع کرنے کے لیے تیار ہیں"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"آلہ"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"سسٹم نیویگیشن کی ترتیبات"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"اشتراک کریں"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"اسپلٹ اسکرین کے استعمال کیلئے دوسری ایپ منتخب کریں"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"منسوخ کریں"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"نظر انداز کریں"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"اسکرین کو گھمائیں"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"ٹاسک بار کی تعلیم"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"ٹاکس بار کا تعلیمی پینل ظاہر ہو گیا"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"ٹاسک بار کا تعلیمی پینل بند ہو گیا"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"ایپس کو سوئچ کرنے کیلئے ٹاسک بار کا استعمال کریں"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"ایک وقت میں دو ایپس استعمال کرنے کے لیے سائیڈ پر گھسیٹیں"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"ٹاسک بار کو کسی بھی وقت چھپانے کیلئے ٹچ کریں اور دبائے رکھیں"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"ٹاسک بار دکھانے کے لیے تھوڑا اوپر سوائپ کریں"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"آگے"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"پیچھے"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"بند کریں"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"حالیہ"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"اطلاعات"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"فوری ترتیبات"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"ٹاسک بار"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"نیویگیشن بار"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"اوپر/بائیں طرف منتقل کریں"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"نیچے/دائیں طرف منتقل کریں"</string>
</resources>
diff --git a/quickstep/res/values-uz/strings.xml b/quickstep/res/values-uz/strings.xml
index 9c47cef..aad0686 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,25 +77,31 @@
<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>
- <string name="allset_description" msgid="6350320429953234580">"Telefoningiz xizmatga tayyor"</string>
- <string name="allset_description_tablet" msgid="7332070270570039247">"Planshetingiz xizmatga tayyor"</string>
+ <string name="allset_button_hint" msgid="2395219947744706291">"Bosh ekranga oʻtish uchun bosh ekran tugmasini bosing"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"<xliff:g id="DEVICE">%1$s</xliff:g> xizmatga tayyor"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"qurilma"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Tizim navigatsiya sozlamalari"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Ulashish"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Ekranni ikkiga ajratish uchun boshqa ilovani tanlang"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Bu amal ilova yoki tashkilotingiz tomonidan taqiqlangan"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Bekor qilish"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Tashlab ketish"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Ekranni burish"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Vazifalar paneli qoʻllanmasi"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Taʼlim vazifalar paneli chiqdi"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Taʼlim vazifalar paneli yopildi"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Ilovalarni vazifalar panelida almashtirish mumkin"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Bir vaqtda ikkita ilova ochish uchun birini yoniga torting"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Vazifalar panelini ustiga bosib turib yashirish mumkin"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Vazifalar panelini koʻrsatish uchun tepaga qisqa suring"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Keyingisi"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Orqaga"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Yopish"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Oxirgilar"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Bildirishnomalar"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Tezkor sozlamalar"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Vazifalar paneli"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigatsiya paneli"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Yuqoriga yoki chapga oʻtkazish"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Pastga yoki oʻngga oʻtkazish"</string>
</resources>
diff --git a/quickstep/res/values-vi/strings.xml b/quickstep/res/values-vi/strings.xml
index 2e5c3e7..5591c52 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,25 +77,31 @@
<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>
- <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_button_hint" msgid="2395219947744706291">"Nhấn vào nút màn hình chính để chuyển đến màn hình chính"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"Bạn có thể bắt đầu sử dụng <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"thiết bị"</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>
<string name="action_share" msgid="2648470652637092375">"Chia sẻ"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Chọn một ứng dụng khác để dùng chế độ chia đôi màn hình"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Ứng dụng hoặc tổ chức của bạn không cho phép thực hiện hành động này"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Hủy"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Bỏ qua"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Xoay màn hình"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Cách sử dụng thanh tác vụ"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Đã hiện bảng hướng dẫn trên thanh tác vụ"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Đã đóng bảng hướng dẫn trên thanh tác vụ"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Dùng thanh tác vụ để chuyển đổi ứng dụng"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Kéo sang bên để dùng hai ứng dụng cùng một lúc"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Chạm và giữ để ẩn thanh tác vụ"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Vuốt lên một chút để hiển thị thanh tác vụ"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Tiếp theo"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Quay lại"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Đóng"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Gần đây"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Thông báo"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Cài đặt nhanh"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"Thanh tác vụ"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Thanh điều hướng"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Chuyển lên trên cùng/sang bên trái"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Chuyển xuống dưới cùng/sang bên phải"</string>
</resources>
diff --git a/quickstep/res/values-zh-rCN/strings.xml b/quickstep/res/values-zh-rCN/strings.xml
index 0b5193f..c15c40d 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,25 +77,31 @@
<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_description" msgid="6350320429953234580">"您可以开始使用手机了"</string>
- <string name="allset_description_tablet" msgid="7332070270570039247">"您可以开始使用平板电脑了"</string>
+ <string name="allset_button_hint" msgid="2395219947744706291">"点按主屏幕按钮即可前往主屏幕"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"您可以开始使用<xliff:g id="DEVICE">%1$s</xliff:g>了"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"设备"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"系统导航设置"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"分享"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"另外选择一个应用才可使用分屏模式"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"取消"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"跳过"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"旋转屏幕"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"任务栏教程"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"任务栏教程已显示"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"任务栏教程已关闭"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"使用任务栏切换应用"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"拖动到一侧,以便一次使用两个应用"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"轻触并按住即可隐藏任务栏"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"稍微向上滑动即可显示任务栏"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"继续"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"返回"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"关闭"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"最近用过"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"通知"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"快捷设置"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"任务栏"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"导航栏"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"移到顶部/左侧"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"移到底部/右侧"</string>
</resources>
diff --git a/quickstep/res/values-zh-rHK/strings.xml b/quickstep/res/values-zh-rHK/strings.xml
index b700da0..399548c 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,25 +77,31 @@
<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_description" msgid="6350320429953234580">"您可以開始使用手機了"</string>
- <string name="allset_description_tablet" msgid="7332070270570039247">"您可以開始使用平板電腦了"</string>
+ <string name="allset_button_hint" msgid="2395219947744706291">"輕按主按鈕即可前往主畫面"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"您可以開始使用 <xliff:g id="DEVICE">%1$s</xliff:g> 了"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"裝置"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"系統導覽設定"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"分享"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"選擇其他應用程式才能使用分割螢幕"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"取消"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"略過"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"旋轉螢幕"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"工作列教學"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"顯示咗工作列教學"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"閂咗工作列教學"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"使用工作列即可切換應用程式"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"拖曳至一側即可同時使用兩個應用程式"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"按住即可隱藏工作列"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"輕輕向上滑動即可顯示工作列"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"繼續"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"返回"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"關閉"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"最近"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"通知"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"快速設定"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"工作列"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"導覽列"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"移至上方/左側"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"移至底部/右側"</string>
</resources>
diff --git a/quickstep/res/values-zh-rTW/strings.xml b/quickstep/res/values-zh-rTW/strings.xml
index e7b74a4..28cf4fc 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,25 +77,31 @@
<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_description" msgid="6350320429953234580">"你可以開始使用手機了"</string>
- <string name="allset_description_tablet" msgid="7332070270570039247">"你可以開始使用平板電腦了"</string>
+ <string name="allset_button_hint" msgid="2395219947744706291">"輕觸主畫面按鈕即可前往主畫面"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"你可以開始使用「<xliff:g id="DEVICE">%1$s</xliff:g>」了"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"裝置"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"系統操作機制設定"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"分享"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"必須選擇另一個應用程式才能使用分割畫面"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"取消"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"略過"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"旋轉螢幕"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"工作列教學課程"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"工作列教學課程已顯示"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"工作列教學課程已關閉"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"使用工作列即可切換應用程式"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"拖曳到一邊即可同時使用兩個應用程式"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"按住即可隱藏工作列"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"稍微向上滑動即可讓工作列顯示在畫面上"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"繼續"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"返回"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"關閉"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"最近使用"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"通知"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"快速設定"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"工作列"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"導覽列"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"移到上方/左側"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"移到底部/右側"</string>
</resources>
diff --git a/quickstep/res/values-zu/strings.xml b/quickstep/res/values-zu/strings.xml
index c69f63d..18b8efb 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,25 +77,31 @@
<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>
- <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_button_hint" msgid="2395219947744706291">"Thepha inkinobho yasekhaya ukuze uye kusikrini sasekhaya"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"Usulungele ukuqala ukusebenzisa i-<xliff:g id="DEVICE">%1$s</xliff:g> yakho"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"idivayisi"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Amasethingi wokuzulazula isistimu"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Yabelana"</string>
<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>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Khetha enye i-app ukuze usebenzise ukuhlukanisa isikrini"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Lesi senzo asivunyelwanga uhlelo lokusebenza noma inhlangano yakho"</string>
<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>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Khansela"</string>
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Yeqa"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Zungezisa isikrini"</string>
+ <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Imfundo ye-taskbar"</string>
<string name="taskbar_edu_opened" msgid="3950252793551919129">"Imfuno yebha yomsebenzi ivelile"</string>
<string name="taskbar_edu_closed" msgid="126643734478892862">"Imfundo yebha yomsebenzi ivaliwe"</string>
- <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Sebenzisa ibha yomsebenzi ukushintsha ama-app"</string>
- <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Hudula ngaseceleni ukuze usebenzise ama-app amabili ngesikhathi esisodwa"</string>
- <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Thinta futhi ubambe, bamba ukuze ufihle ibha yomsebenzi"</string>
+ <!-- no translation found for taskbar_edu_splitscreen (5605512479258053350) -->
+ <skip />
+ <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Swayiphela phezulu okufushane ukuze ubonise i-taskbar"</string>
+ <!-- no translation found for taskbar_edu_suggestions (8215044496435527982) -->
+ <skip />
+ <!-- no translation found for taskbar_edu_features (2338325667726857642) -->
+ <skip />
<string name="taskbar_edu_next" msgid="4007618274426775841">"Okulandelayo"</string>
<string name="taskbar_edu_previous" msgid="459202320127201702">"Emuva"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Vala"</string>
@@ -108,6 +113,8 @@
<string name="taskbar_button_recents" msgid="7273376136216613134">"Okwakamuva"</string>
<string name="taskbar_button_notifications" msgid="7471740351507357318">"Izaziso"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Amasethingi Asheshayo"</string>
+ <string name="taskbar_a11y_title" msgid="6432169809852243110">"I-Taskbar"</string>
+ <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Ibha yokufuna"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Hamba phezulu/kwesokunxele"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Hamba phansi/kwesokudla"</string>
</resources>
diff --git a/quickstep/res/values/colors.xml b/quickstep/res/values/colors.xml
index 185c815..262e418 100644
--- a/quickstep/res/values/colors.xml
+++ b/quickstep/res/values/colors.xml
@@ -76,4 +76,19 @@
<color name="all_set_page_background">#FFFFFFFF</color>
+ <!-- Recents overview -->
+ <color name="recents_filter_icon">#333333</color>
+
+ <!-- Lottie light theme colors. -->
+ <color name="lottie_blue400">#669df6</color>
+ <color name="lottie_blue600">#1a73e8</color>
+ <color name="lottie_green400">#5bb974</color>
+ <color name="lottie_green600">#1e8e3e</color>
+ <color name="lottie_grey200">#e8eaed</color>
+ <color name="lottie_grey600">#80868b</color>
+ <color name="lottie_grey700">#5f6368</color>
+ <color name="lottie_red600">#d93025</color>
+ <color name="lottie_yellow400">#fcc934</color>
+ <color name="lottie_yellow600">#f9ab00</color>
+
</resources>
\ No newline at end of file
diff --git a/quickstep/res/values/config.xml b/quickstep/res/values/config.xml
index 3b4a28b..a91507c 100644
--- a/quickstep/res/values/config.xml
+++ b/quickstep/res/values/config.xml
@@ -25,6 +25,7 @@
<string name="stats_log_manager_class" translatable="false">com.android.quickstep.logging.StatsLogCompatManager</string>
<string name="test_information_handler_class" translatable="false">com.android.quickstep.QuickstepTestInformationHandler</string>
<string name="window_manager_proxy_class" translatable="false">com.android.quickstep.util.SystemWindowManagerProxy</string>
+ <string name="widget_holder_factory_class" translatable="false">com.android.launcher3.uioverrides.QuickstepWidgetHolder$QuickstepHolderFactory</string>
<!-- The number of thumbnails and icons to keep in the cache. The thumbnail cache size also
determines how many thumbnails will be fetched in the background. -->
@@ -43,4 +44,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 0232f86..d97e435 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -31,14 +31,19 @@
<dimen name="overview_minimum_next_prev_size">50dp</dimen>
- <!-- Task View -->
- <dimen name="task_thumbnail_icon_size">48dp</dimen>
- <dimen name="task_thumbnail_icon_drawable_size">44dp</dimen>
- <dimen name="overview_task_margin">16dp</dimen>
- <dimen name="overview_page_spacing">16dp</dimen>
- <dimen name="task_icon_cache_default_icon_size">72dp</dimen>
-
+ <!-- Overview Task Views -->
+ <!-- The primary task thumbnail uses up to this much of the total screen height/width -->
<item name="overview_max_scale" format="float" type="dimen">0.7</item>
+ <!-- A touch target for icons, sometimes slightly larger than the icons themselves -->
+ <dimen name="task_thumbnail_icon_size">48dp</dimen>
+ <!-- The icon size for the focused task, placed in center of touch target -->
+ <dimen name="task_thumbnail_icon_drawable_size">44dp</dimen>
+ <!-- The space under the focused task icon -->
+ <dimen name="overview_task_margin">16dp</dimen>
+ <!-- The horizontal space between tasks -->
+ <dimen name="overview_page_spacing">16dp</dimen>
+
+ <dimen name="task_icon_cache_default_icon_size">72dp</dimen>
<item name="overview_modal_max_scale" format="float" type="dimen">1.1</item>
<!-- Overrideable in overlay that provides the Overview Actions. -->
@@ -197,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>
@@ -247,21 +255,27 @@
<!-- Taskbar -->
<dimen name="taskbar_size">@*android:dimen/taskbar_frame_height</dimen>
<dimen name="taskbar_ime_size">48dp</dimen>
- <dimen name="taskbar_icon_touch_size">48dp</dimen>
+ <dimen name="taskbar_icon_size">44dp</dimen>
+ <dimen name="taskbar_icon_min_touch_size">48dp</dimen>
+ <!-- Note that this applies to both sides of all icons, so visible space is double this. -->
+ <dimen name="taskbar_icon_spacing">12dp</dimen>
<dimen name="taskbar_icon_drag_icon_size">54dp</dimen>
<dimen name="taskbar_folder_margin">16dp</dimen>
<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>
<dimen name="taskbar_stashed_handle_width">220dp</dimen>
+ <dimen name="taskbar_stashed_small_screen">108dp</dimen>
<dimen name="taskbar_unstash_input_area">316dp</dimen>
<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>
@@ -269,11 +283,49 @@
<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">72dp</dimen>
+ <dimen name="transient_taskbar_icon_size">48dp</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>
+ <!-- An additional touch slop to prevent x-axis movement during the swipe up to show taskbar -->
+ <dimen name="transient_taskbar_clamped_offset_bound">16dp</dimen>
+ <!-- Taskbar swipe up thresholds -->
+ <dimen name="taskbar_nav_threshold">40dp</dimen>
+ <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_v2">30dp</dimen>
+ <dimen name="taskbar_app_window_threshold_v2">100dp</dimen>
+ <dimen name="taskbar_home_overview_threshold_v2">200dp</dimen>
+
<!-- Taskbar 3 button spacing -->
<dimen name="taskbar_button_space_inbetween">24dp</dimen>
- <dimen name="taskbar_button_margin_5_5">26dp</dimen>
- <dimen name="taskbar_button_margin_6_5">26dp</dimen>
- <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>
+ <dimen name="taskbar_button_space_inbetween_phone">40dp</dimen>
+ <dimen name="taskbar_button_margin_split">48dp</dimen>
+ <dimen name="taskbar_button_margin_6_5">75dp</dimen>
+ <dimen name="taskbar_button_margin_default">48dp</dimen>
+
+ <!-- Taskbar education tooltip -->
+ <dimen name="taskbar_edu_tooltip_elevation">14dp</dimen>
+ <dimen name="taskbar_edu_tooltip_horizontal_margin">32dp</dimen>
+ <dimen name="taskbar_edu_tooltip_vertical_margin">24dp</dimen>
+ <dimen name="taskbar_edu_tooltip_enter_y_delta">20dp</dimen>
+ <dimen name="taskbar_edu_tooltip_exit_y_delta">-10dp</dimen>
+ <dimen name="taskbar_edu_swipe_lottie_width">348dp</dimen>
+ <dimen name="taskbar_edu_swipe_lottie_height">217dp</dimen>
+ <dimen name="taskbar_edu_features_lottie_width">170dp</dimen>
+ <dimen name="taskbar_edu_features_lottie_height">106dp</dimen>
+ <dimen name="taskbar_edu_features_horizontal_spacing">24dp</dimen>
+
+ <!-- Recents overview -->
+ <dimen name="recents_filter_icon_size">30dp</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/strings.xml b/quickstep/res/values/strings.xml
index 81b0dd2..761934e 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -36,6 +36,13 @@
<!-- Recents: Title of a button that clears the task list, i.e. closes all tasks. [CHAR LIMIT=30] -->
<string name="recents_clear_all">Clear all</string>
+ <!-- Recents: Title of a button that goes back from displaying tasks filtered by package name to displaying all tasks [CHAR LIMIT=30] -->
+ <string name="recents_back" translatable="false">Back</string>
+
+ <!-- TODO: b/260610444. Content description of filtering icons needs to be updated -->
+ <!-- Recents: Content description for the icon on top of taskviews to initiate filtering -->
+ <string name="recents_filter_icon_desc" translatable="false">Click to show only this app\'s tasks</string>
+
<!-- Accessibility title for the list of recent apps [CHAR_LIMIT=none] -->
<string name="accessibility_recent_apps">Recent apps</string>
@@ -68,7 +75,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,10 +194,12 @@
<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>
- <!-- 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] -->
- <string name="allset_description_tablet">You\u2019re ready to start using your tablet</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 the user's device [CHAR LIMIT=NONE] -->
+ <string name="allset_description_generic">You\u2019re ready to start using your <xliff:g id="device" example="Pixel 6">%1$s</xliff:g></string>
+ <!-- A default device name to use in the description of the "All Set" page [CHAR LIMIT=NONE] -->
+ <string name="default_device_name">device</string>
<!-- String linking to navigation settings on "All Set" page [CHAR LIMIT=NONE] -->
<string name="allset_navigation_settings"><annotation id="link">System navigation settings</annotation></string>
@@ -205,7 +213,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>
@@ -223,19 +231,20 @@
<string name="accessibility_rotate_button">Rotate screen</string>
<!-- ******* Taskbar Edu ******* -->
+ <!-- Accessibility title for the taskbar education window. [CHAR_LIMIT=NONE] -->
+ <string name="taskbar_edu_a11y_title">Taskbar education</string>
<!-- Accessibility text spoken when the taskbar education panel appears [CHAR_LIMIT=NONE] -->
<string name="taskbar_edu_opened">Taskbar education appeared</string>
<!-- Accessibility text spoken when the taskbar education panel disappears [CHAR_LIMIT=NONE] -->
<string name="taskbar_edu_closed">Taskbar education closed</string>
- <!-- Text in dialog that lets a user know how they can use the taskbar to switch apps on their device.
- [CHAR_LIMIT=60] -->
- <string name="taskbar_edu_switch_apps">Use the taskbar to switch apps</string>
- <!-- Text in dialog that lets a user know how they can use the taskbar to use multiple apps at once on their device.
- [CHAR_LIMIT=60] -->
- <string name="taskbar_edu_splitscreen">Drag to the side to use two apps at once</string>
- <!-- Text in dialog that lets a user know how they can hide the taskbar on their device.
- [CHAR_LIMIT=60] -->
- <string name="taskbar_edu_stashing">Touch & hold to hide the taskbar</string>
+ <!-- Text in dialog that lets a user know how they can use the taskbar to use multiple apps at once on their device. [CHAR_LIMIT=60] -->
+ <string name="taskbar_edu_splitscreen">Drag an app to the side to use 2 apps at once</string>
+ <!-- Text in dialog that lets a user know how they can show the taskbar on their device. [CHAR_LIMIT=60] -->
+ <string name="taskbar_edu_stashing">Short swipe up to show the taskbar</string>
+ <!-- Text in dialog that lets a user know how the taskbar suggests apps based on their usage. [CHAR_LIMIT=60] -->
+ <string name="taskbar_edu_suggestions">Get app suggestions based on your routine</string>
+ <!-- Title in dialog that shows a user what they can do with the taskbar. [CHAR_LIMIT=60] -->
+ <string name="taskbar_edu_features">Do more with the taskbar</string>
<!-- Text on button to go to the next screen of a tutorial [CHAR_LIMIT=16] -->
<string name="taskbar_edu_next">Next</string>
<!-- Text on button to go to the previous screen of a tutorial [CHAR_LIMIT=16] -->
@@ -258,6 +267,10 @@
<string name="taskbar_button_notifications">Notifications</string>
<!-- Content description for quick settings button [CHAR_LIMIT=16] -->
<string name="taskbar_button_quick_settings">Quick Settings</string>
+ <!-- Accessibility title for the taskbar window. [CHAR_LIMIT=NONE] -->
+ <string name="taskbar_a11y_title">Taskbar</string>
+ <!-- Accessibility title for the taskbar window on phones. [CHAR_LIMIT=NONE] -->
+ <string name="taskbar_phone_a11y_title">Navigation bar</string>
<!-- Label for moving drop target to the top or left side of the screen, depending on orientation (from the taskbar only). -->
<string name="move_drop_target_top_or_left">Move to top/left</string>
diff --git a/quickstep/res/values/styles.xml b/quickstep/res/values/styles.xml
index 7225220..5256e29 100644
--- a/quickstep/res/values/styles.xml
+++ b/quickstep/res/values/styles.xml
@@ -41,7 +41,7 @@
parent="TextAppearance.GestureTutorial">
<item name="android:gravity">start</item>
<item name="android:textColor">?android:attr/textColorPrimary</item>
- <item name="android:fontFamily">google-sans-regular</item>
+ <item name="android:fontFamily">google-sans</item>
<item name="android:letterSpacing">0.03</item>
<item name="android:textSize">36sp</item>
<item name="android:lineHeight">44sp</item>
@@ -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"
@@ -105,6 +106,7 @@
<item name="android:letterSpacing">0.02</item>
<item name="android:textSize">16sp</item>
<item name="android:textAllCaps">false</item>
+ <item name="android:fontFamily">google-sans-text-medium</item>
</style>
<style name="TextAppearance.GestureTutorial.CancelButtonLabel"
@@ -151,6 +153,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>
@@ -182,4 +186,16 @@
<item name="android:textSize">24sp</item>
<item name="android:lines">2</item>
</style>
+
+ <style name="TextAppearance.TaskbarEduTooltip.Title" parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle">
+ <item name="android:gravity">center_horizontal</item>
+ <item name="android:fontFamily">google-sans</item>
+ <item name="android:textSize">24sp</item>
+ </style>
+
+ <style name="TextAppearance.TaskbarEduTooltip.Subtext" parent="android:TextAppearance.Material.Body1">
+ <item name="android:layout_marginTop">16dp</item>
+ <item name="android:fontFamily">google-sans-text</item>
+ <item name="android:textSize">14sp</item>
+ </style>
</resources>
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
deleted file mode 100644
index e21dcba..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 = new DepthController(this);
- 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);
- mDepthController.onOverlayScrollChanged(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);
- }
-
- 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..9f9f2c8 100644
--- a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
+++ b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
@@ -28,13 +28,16 @@
import android.content.Context;
import android.os.Build;
import android.os.Handler;
+import android.os.RemoteException;
+import android.view.IRemoteAnimationFinishedCallback;
+import android.view.RemoteAnimationTarget;
import androidx.annotation.BinderThread;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
+import com.android.systemui.animation.RemoteAnimationDelegate;
import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.lang.ref.WeakReference;
@@ -55,7 +58,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,14 +85,14 @@
@BinderThread
public void onAnimationStart(
int transit,
- RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets,
- RemoteAnimationTargetCompat[] nonAppTargets,
+ RemoteAnimationTarget[] appTargets,
+ RemoteAnimationTarget[] wallpaperTargets,
+ RemoteAnimationTarget[] nonAppTargets,
Runnable runnable) {
Runnable r = () -> {
finishExistingAnimation();
mAnimationResult = new AnimationResult(() -> mAnimationResult = null, runnable);
- getFactory().onCreateAnimation(transit, appTargets, wallpaperTargets, nonAppTargets,
+ getFactory().onAnimationStart(transit, appTargets, wallpaperTargets, nonAppTargets,
mAnimationResult);
};
if (mStartAtFrontOfQueue) {
@@ -99,22 +102,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,14 +120,18 @@
*/
@BinderThread
@Override
- public void onAnimationCancelled() {
+ public void onAnimationCancelled(boolean isKeyguardOccluded) {
postAsyncCallback(mHandler, () -> {
finishExistingAnimation();
getFactory().onAnimationCancelled();
});
}
- public static final class AnimationResult {
+ /**
+ * Used by RemoteAnimationFactory implementations to run the actual animation and its lifecycle
+ * callbacks.
+ */
+ public static final class AnimationResult extends IRemoteAnimationFinishedCallback.Stub {
private final Runnable mSyncFinishRunnable;
private final Runnable mASyncFinishRunnable;
@@ -215,25 +206,41 @@
}
}
}
+
+ /**
+ * When used as a simple IRemoteAnimationFinishedCallback, this method is used to run the
+ * animation finished runnable.
+ */
+ @Override
+ public void onAnimationFinished() throws RemoteException {
+ mASyncFinishRunnable.run();
+ }
}
/**
* Used with LauncherAnimationRunner as an interface for the runner to call back to the
* implementation.
*/
- @FunctionalInterface
- public interface RemoteAnimationFactory {
+ public interface RemoteAnimationFactory extends RemoteAnimationDelegate<AnimationResult> {
/**
* Called on the UI thread when the animation targets are received. The implementation must
* call {@link AnimationResult#setAnimation} with the target animation to be run.
*/
- void onCreateAnimation(int transit,
- RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets,
- RemoteAnimationTargetCompat[] nonAppTargets,
+ @Override
+ @UiThread
+ void onAnimationStart(int transit,
+ RemoteAnimationTarget[] appTargets,
+ RemoteAnimationTarget[] wallpaperTargets,
+ RemoteAnimationTarget[] nonAppTargets,
LauncherAnimationRunner.AnimationResult result);
+ @Override
+ @UiThread
+ default void onAnimationCancelled(boolean isKeyguardOccluded) {
+ onAnimationCancelled();
+ }
+
/**
* Called when the animation is cancelled. This can happen with or without
* the create being called.
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 3e13a44..258e2bc 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,22 +52,22 @@
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.testing.shared.TestProtocol.BAD_STATE;
+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.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS;
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;
@@ -77,16 +87,22 @@
import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings;
-import android.util.Log;
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,8 @@
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.LauncherAnimationRunner.RemoteAnimationFactory;
import com.android.launcher3.anim.AnimationSuccessListener;
+import com.android.launcher3.anim.AnimatorListeners;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.icons.FastBitmapDrawable;
import com.android.launcher3.shortcuts.DeepShortcutView;
@@ -102,7 +120,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,26 +138,19 @@
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;
-import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
@@ -146,8 +159,6 @@
*/
public class QuickstepTransitionManager implements OnDeviceProfileChangeListener {
- private static final String TAG = "QuickstepTransition";
-
private static final boolean ENABLE_SHELL_STARTING_SURFACE =
SystemProperties.getBoolean("persist.debug.shell_starting_surface", true);
@@ -184,6 +195,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.
@@ -196,7 +208,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;
@@ -215,7 +227,7 @@
private RemoteAnimationFactory mKeyguardGoingAwayRunner;
private RemoteAnimationFactory mWallpaperOpenTransitionRunner;
- private RemoteTransitionCompat mLauncherOpenTransition;
+ private RemoteTransition mLauncherOpenTransition;
private LauncherBackAnimationController mBackAnimationController;
private final AnimatorListenerAdapter mForceInvisibleListener = new AnimatorListenerAdapter() {
@@ -292,11 +304,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);
}
/**
@@ -310,7 +322,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;
}
@@ -324,18 +336,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;
}
@@ -353,21 +365,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) {
@@ -381,40 +390,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));
}
/**
@@ -422,10 +410,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;
@@ -447,7 +435,9 @@
4 - rotationChange);
}
}
- if (mDeviceProfile.isTaskbarPresentInApps) {
+ if (mDeviceProfile.isTaskbarPresentInApps
+ && !target.willShowImeOnTarget
+ && !DisplayController.isTransientTaskbar(mLauncher)) {
// Animate to above the taskbar.
bounds.bottom -= target.contentInsets.bottom;
}
@@ -493,6 +483,10 @@
final View appsView = mLauncher.getAppsView();
final float startAlpha = appsView.getAlpha();
final float startScale = SCALE_PROPERTY.get(appsView);
+ if (mDeviceProfile.isTablet) {
+ // AllApps should not fade at all in tablets.
+ alphas = new float[]{1, 1};
+ }
appsView.setAlpha(alphas[0]);
ObjectAnimator alpha = ObjectAnimator.ofFloat(appsView, View.ALPHA, alphas);
@@ -520,6 +514,7 @@
appsView.setAlpha(startAlpha);
SCALE_PROPERTY.set(appsView, startScale);
appsView.setLayerType(View.LAYER_TYPE_NONE, null);
+ mLauncher.resumeExpensiveViewUpdates();
};
} else if (mLauncher.isInState(OVERVIEW)) {
endListener = composeViewContentAnimator(launcherAnimator, alphas, scales);
@@ -550,7 +545,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);
@@ -623,28 +619,9 @@
RecentsView overview = mLauncher.getOverviewPanel();
ObjectAnimator alpha = ObjectAnimator.ofFloat(overview,
RecentsView.CONTENT_ALPHA, alphas);
- Log.d(BAD_STATE, "QTM composeViewContentAnimator alphas=" + Arrays.toString(alphas));
- alpha.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- Log.d(BAD_STATE, "QTM composeViewContentAnimator onStart");
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- float alpha = overview == null ? -1 : RecentsView.CONTENT_ALPHA.get(overview);
- Log.d(BAD_STATE, "QTM composeViewContentAnimator onCancel, alpha=" + alpha);
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- Log.d(BAD_STATE, "QTM composeViewContentAnimator onEnd");
- }
- });
alpha.setDuration(CONTENT_ALPHA_DURATION);
alpha.setInterpolator(LINEAR);
anim.play(alpha);
- Log.d(BAD_STATE, "QTM composeViewContentAnimator setFreezeVisibility=true");
overview.setFreezeViewVisibility(true);
ObjectAnimator scaleAnim = ObjectAnimator.ofFloat(overview, SCALE_PROPERTY, scales);
@@ -653,10 +630,10 @@
anim.play(scaleAnim);
return () -> {
- Log.d(BAD_STATE, "QTM composeViewContentAnimator onEnd setFreezeVisibility=false");
overview.setFreezeViewVisibility(false);
SCALE_PROPERTY.set(overview, 1f);
mLauncher.getStateManager().reapplyState();
+ mLauncher.resumeExpensiveViewUpdates();
};
}
@@ -664,10 +641,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 */);
@@ -679,7 +660,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);
@@ -719,7 +700,7 @@
@Override
public void onAnimationStart(Animator animation) {
LauncherTaskbarUIController taskbarController = mLauncher.getTaskbarUIController();
- if (taskbarController != null && taskbarController.shouldShowEdu()) {
+ if (taskbarController != null && taskbarController.shouldShowEduOnAppLaunch()) {
// LAUNCHER_TASKBAR_EDUCATION_SHOWING is set to true here, when the education
// flow is about to start, to avoid a race condition with other components
// that would show something else to the user as soon as the app is opened.
@@ -735,7 +716,7 @@
}
LauncherTaskbarUIController taskbarController = mLauncher.getTaskbarUIController();
if (taskbarController != null) {
- taskbarController.showEdu();
+ taskbarController.showEduOnAppLaunch();
}
openingTargets.release();
}
@@ -831,10 +812,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);
@@ -855,14 +837,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);
@@ -879,29 +860,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);
@@ -910,7 +888,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());
@@ -919,17 +897,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)
@@ -953,7 +933,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);
@@ -1011,49 +991,45 @@
mAppWindowScale = widgetBackgroundBounds.width() / windowTargetBounds.width();
// Crop scaled app window to match widget
appWindowCrop.set(0 /* left */, 0 /* top */,
- Math.round(windowTargetBounds.width()) /* right */,
+ windowTargetBounds.width() /* right */,
Math.round(widgetBackgroundBounds.height() / mAppWindowScale) /* bottom */);
matrix.setTranslate(widgetBackgroundBounds.left, widgetBackgroundBounds.top);
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());
@@ -1067,54 +1043,37 @@
private ObjectAnimator getBackgroundAnimator() {
// When launching an app from overview that doesn't map to a task, we still want to just
// blur the wallpaper instead of the launcher surface as well
- boolean allowBlurringLauncher = mLauncher.getStateManager().getState() != OVERVIEW;
- DepthController depthController = mLauncher.getDepthController();
- ObjectAnimator backgroundRadiusAnim = ObjectAnimator.ofFloat(depthController, DEPTH,
- BACKGROUND_APP.getDepth(mLauncher))
+ boolean allowBlurringLauncher = mLauncher.getStateManager().getState() != OVERVIEW
+ && BlurUtils.supportsBlursOnWindows();
+
+ MyDepthController depthController = new MyDepthController(mLauncher);
+ ObjectAnimator backgroundRadiusAnim = ObjectAnimator.ofFloat(depthController.stateDepth,
+ MULTI_PROPERTY_VALUE, BACKGROUND_APP.getDepth(mLauncher))
.setDuration(APP_LAUNCH_DURATION);
+
if (allowBlurringLauncher) {
- final SurfaceControl dimLayer;
- if (BlurUtils.supportsBlursOnWindows()) {
- // Create a temporary effect layer, that lives on top of launcher, so we can apply
- // the blur to it. The EffectLayer will be fullscreen, which will help with caching
- // optimizations on the SurfaceFlinger side:
- // - Results would be able to be cached as a texture
- // - There won't be texture allocation overhead, because EffectLayers don't have
- // buffers
- ViewRootImpl viewRootImpl = mLauncher.getDragLayer().getViewRootImpl();
- SurfaceControl parent = viewRootImpl != null
- ? viewRootImpl.getSurfaceControl()
- : null;
- dimLayer = new SurfaceControl.Builder()
- .setName("Blur layer")
- .setParent(parent)
- .setOpaque(false)
- .setHidden(false)
- .setEffectLayer()
- .build();
- } else {
- dimLayer = null;
- }
+ // Create a temporary effect layer, that lives on top of launcher, so we can apply
+ // the blur to it. The EffectLayer will be fullscreen, which will help with caching
+ // optimizations on the SurfaceFlinger side:
+ // - Results would be able to be cached as a texture
+ // - There won't be texture allocation overhead, because EffectLayers don't have
+ // buffers
+ ViewRootImpl viewRootImpl = mLauncher.getDragLayer().getViewRootImpl();
+ SurfaceControl parent = viewRootImpl != null
+ ? viewRootImpl.getSurfaceControl()
+ : null;
+ SurfaceControl dimLayer = new SurfaceControl.Builder()
+ .setName("Blur layer")
+ .setParent(parent)
+ .setOpaque(false)
+ .setHidden(false)
+ .setEffectLayer()
+ .build();
- depthController.setSurface(dimLayer);
- backgroundRadiusAnim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- depthController.setIsInLaunchTransition(true);
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- depthController.setIsInLaunchTransition(false);
- depthController.setSurface(null);
- if (dimLayer != null) {
- new SurfaceControl.Transaction()
- .remove(dimLayer)
- .apply();
- }
- }
- });
+ backgroundRadiusAnim.addListener(AnimatorListeners.forEndCallback(() ->
+ new SurfaceControl.Transaction().remove(dimLayer).apply()));
}
+
return backgroundRadiusAnim;
}
@@ -1128,28 +1087,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);
}
}
@@ -1157,16 +1114,33 @@
* Registers remote animations used when closing apps to home screen.
*/
public void registerRemoteTransitions() {
+ if (ENABLE_SHELL_TRANSITIONS) {
+ SystemUiProxy.INSTANCE.get(mLauncher).shareTransactionQueue();
+ }
if (SEPARATE_RECENTS_ACTIVITY.get()) {
return;
}
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);
@@ -1185,7 +1159,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
@@ -1196,6 +1170,9 @@
}
private void unregisterRemoteTransitions() {
+ if (ENABLE_SHELL_TRANSITIONS) {
+ SystemUiProxy.INSTANCE.get(mLauncher).unshareTransactionQueue();
+ }
if (SEPARATE_RECENTS_ACTIVITY.get()) {
return;
}
@@ -1212,8 +1189,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.
@@ -1225,9 +1202,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++;
}
@@ -1249,8 +1226,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);
@@ -1259,24 +1236,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;
}
@@ -1287,8 +1263,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) {
@@ -1302,7 +1278,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;
}
@@ -1363,15 +1339,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;
@@ -1456,7 +1432,7 @@
animation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
- anim.start(mLauncher, velocityPxPerS);
+ anim.start(mLauncher, mDeviceProfile, velocityPxPerS);
}
});
return anim;
@@ -1465,7 +1441,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();
@@ -1486,10 +1462,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);
@@ -1511,20 +1487,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);
}
});
@@ -1588,11 +1563,12 @@
* the transition.
*/
public Pair<RectFSpringAnim, AnimatorSet> createWallpaperOpenAnimations(
- RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets,
+ RemoteAnimationTarget[] appTargets,
+ RemoteAnimationTarget[] wallpaperTargets,
boolean fromUnlock,
RectF startRect,
- float startWindowCornerRadius) {
+ float startWindowCornerRadius,
+ boolean fromPredictiveBack) {
AnimatorSet anim = null;
RectFSpringAnim rectFSpringAnim = null;
@@ -1626,21 +1602,22 @@
rectFSpringAnim = getClosingWindowAnimators(
anim, appTargets, launcherView, velocity, startRect,
startWindowCornerRadius);
- if (!mLauncher.isInState(LauncherState.ALL_APPS)) {
+ if (mLauncher.isInState(LauncherState.ALL_APPS)) {
+ // Skip scaling all apps, otherwise FloatingIconView will get wrong
+ // layout bounds.
+ skipAllAppsScale = true;
+ } else if (!fromPredictiveBack) {
anim.play(new StaggeredWorkspaceAnim(mLauncher, velocity.y,
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)));
}
// We play StaggeredWorkspaceAnim as a part of the closing window animation.
playWorkspaceReveal = false;
- } else {
- // Skip scaling all apps, otherwise FloatingIconView will get wrong
- // layout bounds.
- skipAllAppsScale = true;
}
} else {
anim.play(getFallbackClosingWindowAnimators(appTargets));
@@ -1696,10 +1673,10 @@
}
@Override
- public void onCreateAnimation(int transit,
- RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets,
- RemoteAnimationTargetCompat[] nonAppTargets,
+ public void onAnimationStart(int transit,
+ RemoteAnimationTarget[] appTargets,
+ RemoteAnimationTarget[] wallpaperTargets,
+ RemoteAnimationTarget[] nonAppTargets,
LauncherAnimationRunner.AnimationResult result) {
if (mLauncher.isDestroyed()) {
AnimatorSet anim = new AnimatorSet();
@@ -1713,10 +1690,12 @@
mLauncher.getStateManager().moveToRestState();
}
+ RectF windowTargetBounds =
+ new RectF(getWindowTargetBounds(appTargets, getRotationChange(appTargets)));
Pair<RectFSpringAnim, AnimatorSet> pair = createWallpaperOpenAnimations(
- appTargets, wallpaperTargets, mFromUnlock,
- new RectF(0, 0, mDeviceProfile.widthPx, mDeviceProfile.heightPx),
- QuickStepContract.getWindowCornerRadius(mLauncher));
+ appTargets, wallpaperTargets, mFromUnlock, windowTargetBounds,
+ QuickStepContract.getWindowCornerRadius(mLauncher),
+ false /* fromPredictiveBack */);
mLauncher.clearForceInvisibleFlag(INVISIBLE_ALL);
result.setAnimation(pair.second, mLauncher);
@@ -1737,10 +1716,10 @@
}
@Override
- public void onCreateAnimation(int transit,
- RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets,
- RemoteAnimationTargetCompat[] nonAppTargets,
+ public void onAnimationStart(int transit,
+ RemoteAnimationTarget[] appTargets,
+ RemoteAnimationTarget[] wallpaperTargets,
+ RemoteAnimationTarget[] nonAppTargets,
LauncherAnimationRunner.AnimationResult result) {
AnimatorSet anim = new AnimatorSet();
boolean launcherClosing =
@@ -1751,7 +1730,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;
@@ -1867,7 +1846,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();
@@ -1878,7 +1857,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;
@@ -1893,10 +1872,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);
@@ -1931,18 +1910,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) {
@@ -1959,4 +1937,17 @@
return Utilities.mapToRange(progress, start, end, 1, 0, ACCEL_1_5);
}
}
+
+ private static class MyDepthController extends DepthController {
+ MyDepthController(Launcher l) {
+ super(l);
+ setCrossWindowBlursEnabled(
+ CrossWindowBlurListeners.getInstance().isCrossWindowBlurEnabled());
+ }
+
+ @Override
+ public void setSurface(SurfaceControl surface) {
+ super.setSurface(surface);
+ }
+ }
}
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..4fbe8cf 100644
--- a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
+++ b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
@@ -30,9 +30,9 @@
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.DeviceProfile;
-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;
@@ -50,7 +50,7 @@
import java.util.stream.Collectors;
@TargetApi(Build.VERSION_CODES.P)
-public class PredictionRowView<T extends Context & ActivityContext & DeviceProfileListenable>
+public class PredictionRowView<T extends Context & ActivityContext>
extends LinearLayout implements OnDeviceProfileChangeListener, FloatingHeaderRow {
private final T mActivityContext;
@@ -117,9 +117,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..ba412c9 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, -1);
mSampleHotseat.addViewToCellLayout(icon, i, info.getViewId(), lp, true);
}
}
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
index 05b8167..0a2a9b3 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
@@ -305,7 +305,7 @@
* Sets or updates the predicted items only once the hotseat becomes hidden to the user
*/
private void applyPredictedItems(FixedContainerItems items) {
- mPredictedItems = items.items;
+ mPredictedItems = new ArrayList(items.items);
if (mPredictedItems.isEmpty()) {
HotseatRestoreHelper.restoreBackup(mLauncher);
}
diff --git a/quickstep/src/com/android/launcher3/model/PredictionUpdateTask.java b/quickstep/src/com/android/launcher3/model/PredictionUpdateTask.java
index 7e3ee7d..e504141 100644
--- a/quickstep/src/com/android/launcher3/model/PredictionUpdateTask.java
+++ b/quickstep/src/com/android/launcher3/model/PredictionUpdateTask.java
@@ -27,13 +27,17 @@
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.LauncherPrefs;
import com.android.launcher3.model.BgDataModel.FixedContainerItems;
import com.android.launcher3.model.QuickstepModelDelegate.PredictorState;
import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
+import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@@ -52,11 +56,12 @@
}
@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
- Utilities.getDevicePrefs(context).edit()
+ LauncherPrefs.getDevicePrefs(context).edit()
.putBoolean(LAST_PREDICTION_ENABLED_STATE, !mTargets.isEmpty()).apply();
Set<UserHandle> usersForChangedShortcuts =
@@ -65,7 +70,7 @@
.map(info -> info.user)
.collect(Collectors.toSet());
- FixedContainerItems fci = new FixedContainerItems(mPredictorState.containerId);
+ List<ItemInfo> items = new ArrayList<>(mTargets.size());
for (AppTarget target : mTargets) {
WorkspaceItemInfo itemInfo;
ShortcutInfo si = target.getShortcutInfo();
@@ -104,10 +109,11 @@
}
}
- itemInfo.container = fci.containerId;
- fci.items.add(itemInfo);
+ itemInfo.container = mPredictorState.containerId;
+ items.add(itemInfo);
}
+ FixedContainerItems fci = new FixedContainerItems(mPredictorState.containerId, items);
dataModel.extraItems.put(fci.containerId, fci);
bindExtraContainerItems(fci);
usersForChangedShortcuts.forEach(
diff --git a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
index 770dfb2..118cfc6 100644
--- a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
+++ b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
@@ -18,12 +18,12 @@
import static android.text.format.DateUtils.DAY_IN_MILLIS;
import static android.text.format.DateUtils.formatElapsedTime;
+import static com.android.launcher3.LauncherPrefs.getDevicePrefs;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_PREDICTION;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
-import static com.android.launcher3.Utilities.getDevicePrefs;
import static com.android.launcher3.hybridhotseat.HotseatPredictionModel.convertDataModelToAppTargetBundle;
import static com.android.launcher3.model.PredictionHelper.getAppTargetFromItemInfo;
import static com.android.launcher3.model.PredictionHelper.wrapAppTargetWithItemLocation;
@@ -117,21 +117,23 @@
// 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);
// Widgets prediction isn't used frequently. And thus, it is not persisted on disk.
mDataModel.extraItems.put(mWidgetsRecommendationState.containerId,
- new FixedContainerItems(mWidgetsRecommendationState.containerId));
+ new FixedContainerItems(mWidgetsRecommendationState.containerId,
+ new ArrayList<>()));
mActive = true;
}
@@ -432,15 +434,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 +462,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 +477,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..d8fd51a 100644
--- a/quickstep/src/com/android/launcher3/model/WellbeingModel.java
+++ b/quickstep/src/com/android/launcher3/model/WellbeingModel.java
@@ -18,7 +18,7 @@
import static android.content.ContentResolver.SCHEME_CONTENT;
-import static com.android.launcher3.Utilities.newContentObserver;
+import static com.android.launcher3.util.SimpleBroadcastReceiver.getPackageFilter;
import android.annotation.TargetApi;
import android.app.RemoteAction;
@@ -57,7 +57,6 @@
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.util.BgObjectWithLooper;
import com.android.launcher3.util.MainThreadInitializedObject;
-import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.SimpleBroadcastReceiver;
@@ -119,7 +118,7 @@
if (!TextUtils.isEmpty(mWellbeingProviderPkg)) {
mContext.registerReceiver(
new SimpleBroadcastReceiver(t -> restartObserver()),
- PackageManagerHelper.getPackageFilter(mWellbeingProviderPkg,
+ getPackageFilter(mWellbeingProviderPkg,
Intent.ACTION_PACKAGE_ADDED, Intent.ACTION_PACKAGE_CHANGED,
Intent.ACTION_PACKAGE_REMOVED, Intent.ACTION_PACKAGE_DATA_CLEARED,
Intent.ACTION_PACKAGE_RESTARTED),
diff --git a/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java b/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
index 9cd9d85..6160378 100644
--- a/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
+++ b/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
@@ -18,20 +18,23 @@
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.model.data.ItemInfo;
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 +55,50 @@
* 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);
+ }
+
+ List<ItemInfo> items = servicePredictedItems.stream()
+ .map(it -> new PendingAddWidgetInfo(it.widgetInfo, CONTAINER_WIDGETS_PREDICTION))
+ .collect(Collectors.toList());
+ FixedContainerItems fixedContainerItems =
+ new FixedContainerItems(mPredictorState.containerId, items);
+
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..45d1b11 100644
--- a/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java
+++ b/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java
@@ -15,43 +15,72 @@
*/
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.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
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 androidx.annotation.Nullable;
+
+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;
+import com.android.systemui.shared.recents.model.Task;
+
+import java.util.function.Consumer;
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 +97,53 @@
return;
}
+ StatsLogManager.EventEnum splitEvent = getLogEventForPosition(mPosition.stagePosition);
RecentsView recentsView = mTarget.getOverviewPanel();
- recentsView.initiateSplitSelect(
- new SplitSelectSource(mOriginalView, new BitmapDrawable(bitmap), intent,
- mPosition));
+ // Check if there is already an instance of this app running, if so, initiate the split
+ // using that.
+ recentsView.findLastActiveTaskAndRunCallback(
+ intent.getComponent(),
+ (Consumer<Task>) foundTask -> {
+ SplitSelectSource source = new SplitSelectSource(mOriginalView,
+ new BitmapDrawable(bitmap), intent, mPosition, mItemInfo,
+ splitEvent, foundTask);
+ if (ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
+ startSplitToHome(source);
+ } else {
+ 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, source.alreadyRunningTask);
+
+ RecentsView recentsView = mTarget.getOverviewPanel();
+ recentsView.getPagedOrientationHandler().getInitialSplitPlaceholderBounds(
+ mSplitPlaceholderSize, mSplitPlaceholderInset, mTarget.getDeviceProfile(),
+ controller.getActiveSplitStagePosition(), mTempRect);
+
+ PendingAnimation anim = new PendingAnimation(TABLET_HOME_TO_SPLIT.getDuration());
+ RectF startingTaskRect = new RectF();
+ final 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.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mTarget.getDragLayer().removeView(floatingTaskView);
+ controller.resetState();
+ }
+ });
+ anim.buildAnim().start();
}
}
@@ -81,13 +153,21 @@
public final Drawable drawable;
public final Intent intent;
public final SplitPositionOption position;
+ public final ItemInfo itemInfo;
+ public final StatsLogManager.EventEnum splitEvent;
+ @Nullable
+ public final Task alreadyRunningTask;
public SplitSelectSource(View view, Drawable drawable, Intent intent,
- SplitPositionOption position) {
+ SplitPositionOption position, ItemInfo itemInfo,
+ StatsLogManager.EventEnum splitEvent, @Nullable Task foundTask) {
this.view = view;
this.drawable = drawable;
this.intent = intent;
this.position = position;
+ this.itemInfo = itemInfo;
+ this.splitEvent = splitEvent;
+ this.alreadyRunningTask = foundTask;
}
}
}
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 eda0823..867e168 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
@@ -19,17 +19,12 @@
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.app.WallpaperManager;
-import android.os.IBinder;
-import android.os.SystemProperties;
-import android.util.FloatProperty;
-import android.view.AttachedSurfaceControl;
import android.view.CrossWindowBlurListeners;
-import android.view.SurfaceControl;
import android.view.View;
import android.view.ViewRootImpl;
import android.view.ViewTreeObserver;
@@ -37,12 +32,10 @@
import com.android.launcher3.BaseActivity;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.statemanager.StateManager.StateHandler;
import com.android.launcher3.states.StateAnimationConfig;
-import com.android.systemui.shared.system.BlurUtils;
+import com.android.quickstep.util.BaseDepthController;
import java.io.PrintWriter;
import java.util.function.Consumer;
@@ -50,138 +43,43 @@
/**
* Controls blur and wallpaper zoom, for the Launcher surface only.
*/
-public class DepthController implements StateHandler<LauncherState>,
+public class DepthController extends BaseDepthController implements StateHandler<LauncherState>,
BaseActivity.MultiWindowModeChangedListener {
- private static final boolean OVERLAY_SCROLL_ENABLED = false;
- public static final FloatProperty<DepthController> DEPTH =
- new FloatProperty<DepthController>("depth") {
- @Override
- public void setValue(DepthController depthController, float depth) {
- depthController.setDepth(depth);
- }
+ private final ViewTreeObserver.OnDrawListener mOnDrawListener = this::onLauncherDraw;
- @Override
- public Float get(DepthController depthController) {
- return depthController.mDepth;
- }
- };
+ private final Consumer<Boolean> mCrossWindowBlurListener = this::setCrossWindowBlursEnabled;
- /**
- * 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 =
- new ViewTreeObserver.OnDrawListener() {
- @Override
- public void onDraw() {
- View view = mLauncher.getDragLayer();
- ViewRootImpl viewRootImpl = view.getViewRootImpl();
- boolean applied = setSurface(
- viewRootImpl != null ? viewRootImpl.getSurfaceControl() : null);
- if (!applied) {
- dispatchTransactionSurface(mDepth);
- }
- view.post(() -> view.getViewTreeObserver().removeOnDrawListener(this));
- }
- };
-
- private final Consumer<Boolean> mCrossWindowBlurListener = new Consumer<Boolean>() {
- @Override
- public void accept(Boolean enabled) {
- mCrossWindowBlursEnabled = enabled;
- dispatchTransactionSurface(mDepth);
- }
- };
-
- private final Runnable mOpaquenessListener = new Runnable() {
- @Override
- public void run() {
- dispatchTransactionSurface(mDepth);
- }
- };
-
- private final Launcher mLauncher;
- /**
- * Blur radius when completely zoomed out, in pixels.
- */
- private int mMaxBlurRadius;
- private boolean mCrossWindowBlursEnabled;
- private WallpaperManager mWallpaperManager;
- private SurfaceControl mSurface;
- /**
- * How visible the -1 overlay is, from 0 to 1.
- */
- private float mOverlayScrollProgress;
- /**
- * Ratio from 0 to 1, where 0 is fully zoomed out, and 1 is zoomed in.
- * @see android.service.wallpaper.WallpaperService.Engine#onZoomChanged(float)
- */
- private float mDepth;
- /**
- * Last blur value, in pixels, that was applied.
- * For debugging purposes.
- */
- private int mCurrentBlur;
- /**
- * If we're launching and app and should not be blurring the screen for performance reasons.
- */
- private boolean mBlurDisabledForAppLaunch;
- /**
- * If we requested early wake-up offsets to SurfaceFlinger.
- */
- private boolean mInEarlyWakeUp;
+ private final Runnable mOpaquenessListener = this::applyDepthAndBlur;
// Workaround for animating the depth when multiwindow mode changes.
private boolean mIgnoreStateChangesDuringMultiWindowAnimation = false;
- // Hints that there is potentially content behind Launcher and that we shouldn't optimize by
- // marking the launcher surface as opaque. Only used in certain Launcher states.
- private boolean mHasContentBehindLauncher;
-
private View.OnAttachStateChangeListener mOnAttachListener;
public DepthController(Launcher l) {
- mLauncher = l;
+ super(l);
+ }
+
+ private void onLauncherDraw() {
+ View view = mLauncher.getDragLayer();
+ ViewRootImpl viewRootImpl = view.getViewRootImpl();
+ setSurface(viewRootImpl != null ? viewRootImpl.getSurfaceControl() : null);
+ view.post(() -> view.getViewTreeObserver().removeOnDrawListener(mOnDrawListener));
}
private void ensureDependencies() {
- if (mWallpaperManager == null) {
- mMaxBlurRadius = mLauncher.getResources().getInteger(R.integer.max_depth_blur_radius);
- mWallpaperManager = mLauncher.getSystemService(WallpaperManager.class);
- }
-
if (mLauncher.getRootView() != null && mOnAttachListener == null) {
+ View rootView = mLauncher.getRootView();
mOnAttachListener = new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View view) {
+ CrossWindowBlurListeners.getInstance().addListener(mLauncher.getMainExecutor(),
+ mCrossWindowBlurListener);
+ mLauncher.getScrimView().addOpaquenessListener(mOpaquenessListener);
+
// To handle the case where window token is invalid during last setDepth call.
- IBinder windowToken = mLauncher.getRootView().getWindowToken();
- if (windowToken != null) {
- mWallpaperManager.setWallpaperZoomOut(windowToken, mDepth);
- }
- onAttached();
+ applyDepthAndBlur();
}
@Override
@@ -190,23 +88,13 @@
mLauncher.getScrimView().removeOpaquenessListener(mOpaquenessListener);
}
};
- mLauncher.getRootView().addOnAttachStateChangeListener(mOnAttachListener);
- if (mLauncher.getRootView().isAttachedToWindow()) {
- onAttached();
+ rootView.addOnAttachStateChangeListener(mOnAttachListener);
+ if (rootView.isAttachedToWindow()) {
+ mOnAttachListener.onViewAttachedToWindow(rootView);
}
}
}
- private void onAttached() {
- CrossWindowBlurListeners.getInstance().addListener(mLauncher.getMainExecutor(),
- mCrossWindowBlurListener);
- mLauncher.getScrimView().addOpaquenessListener(mOpaquenessListener);
- }
-
- public void setHasContentBehindLauncher(boolean hasContentBehindLauncher) {
- mHasContentBehindLauncher = hasContentBehindLauncher;
- }
-
/**
* Sets if the underlying activity is started or not
*/
@@ -219,38 +107,14 @@
}
}
- /**
- * Sets the specified app target surface to apply the blur to.
- * @return true when surface was valid and transaction was dispatched.
- */
- public boolean setSurface(SurfaceControl surface) {
- // Set launcher as the SurfaceControl when we don't need an external target anymore.
- if (surface == null) {
- ViewRootImpl viewRootImpl = mLauncher.getDragLayer().getViewRootImpl();
- surface = viewRootImpl != null ? viewRootImpl.getSurfaceControl() : null;
- }
- if (mSurface != surface) {
- mSurface = surface;
- if (surface != null) {
- dispatchTransactionSurface(mDepth);
- return true;
- }
- }
- return false;
- }
-
@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) {
- dispatchTransactionSurface(mDepth);
- } else if (toState == LauncherState.BACKGROUND_APP) {
+ stateDepth.setValue(toState.getDepth(mLauncher));
+ if (toState == LauncherState.BACKGROUND_APP) {
mLauncher.getDragLayer().getViewTreeObserver().addOnDrawListener(mOnDrawListener);
}
}
@@ -264,102 +128,21 @@
}
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));
}
- /**
- * If we're launching an app from the home screen.
- */
- public void setIsInLaunchTransition(boolean inLaunchTransition) {
- boolean blurEnabled = SystemProperties.getBoolean("ro.launcher.blur.appLaunch", true);
- mBlurDisabledForAppLaunch = inLaunchTransition && !blurEnabled;
- if (!inLaunchTransition) {
- // Reset depth at the end of the launch animation, so the wallpaper won't be
- // zoomed out if an app crashes.
- setDepth(0f);
- }
- }
-
- 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);
- float depthF = depthI / 256f;
- if (Float.compare(mDepth, depthF) == 0) {
- return;
- }
- dispatchTransactionSurface(depthF);
- mDepth = depthF;
- }
-
- public void onOverlayScrollChanged(float progress) {
- if (!OVERLAY_SCROLL_ENABLED) {
- return;
- }
- // Add some padding to the progress, such we don't change the depth on the last frames of
- // the animation. It's possible that a user flinging the feed quickly would scroll
- // horizontally by accident, causing the device to enter client composition unnecessarily.
- progress = Math.min(progress * 1.1f, 1f);
-
- // Round out the progress to dedupe frequent, non-perceptable updates
- int progressI = (int) (progress * 256);
- float progressF = Utilities.boundToRange(progressI / 256f, 0f, 1f);
- if (Float.compare(mOverlayScrollProgress, progressF) == 0) {
- return;
- }
- mOverlayScrollProgress = progressF;
- dispatchTransactionSurface(mDepth);
- }
-
- private boolean dispatchTransactionSurface(float depth) {
- boolean supportsBlur = BlurUtils.supportsBlursOnWindows();
- if (supportsBlur && (mSurface == null || !mSurface.isValid())) {
- return false;
- }
+ @Override
+ protected void applyDepthAndBlur() {
ensureDependencies();
- depth = Math.max(depth, mOverlayScrollProgress);
- IBinder windowToken = mLauncher.getRootView().getWindowToken();
- if (windowToken != null) {
- mWallpaperManager.setWallpaperZoomOut(windowToken, depth);
- }
-
- if (supportsBlur) {
- boolean hasOpaqueBg = mLauncher.getScrimView().isFullyOpaque();
- boolean isSurfaceOpaque = !mHasContentBehindLauncher && hasOpaqueBg;
-
- mCurrentBlur = !mCrossWindowBlursEnabled || mBlurDisabledForAppLaunch || hasOpaqueBg
- ? 0 : (int) (depth * mMaxBlurRadius);
- SurfaceControl.Transaction transaction = new SurfaceControl.Transaction()
- .setBackgroundBlurRadius(mSurface, mCurrentBlur)
- .setOpaque(mSurface, isSurfaceOpaque);
-
- // Set early wake-up flags when we know we're executing an expensive operation, this way
- // SurfaceFlinger will adjust its internal offsets to avoid jank.
- boolean wantsEarlyWakeUp = depth > 0 && depth < 1;
- if (wantsEarlyWakeUp && !mInEarlyWakeUp) {
- transaction.setEarlyWakeupStart();
- mInEarlyWakeUp = true;
- } else if (!wantsEarlyWakeUp && mInEarlyWakeUp) {
- transaction.setEarlyWakeupEnd();
- mInEarlyWakeUp = false;
- }
-
- AttachedSurfaceControl rootSurfaceControl =
- mLauncher.getRootView().getRootSurfaceControl();
- if (rootSurfaceControl != null) {
- rootSurfaceControl.applyTransactionOnDraw(transaction);
- }
- }
- return true;
+ super.applyDepthAndBlur();
}
@Override
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() {
@@ -377,10 +160,9 @@
writer.println(prefix + "\tmMaxBlurRadius=" + mMaxBlurRadius);
writer.println(prefix + "\tmCrossWindowBlursEnabled=" + mCrossWindowBlursEnabled);
writer.println(prefix + "\tmSurface=" + mSurface);
- writer.println(prefix + "\tmOverlayScrollProgress=" + mOverlayScrollProgress);
- 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..ae121e2
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
@@ -0,0 +1,149 @@
+/*
+ * 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;
+ private boolean mGestureInProgress;
+
+ public DesktopVisibilityController(Launcher launcher) {
+ mLauncher = launcher;
+ }
+
+ /**
+ * Whether desktop mode is supported.
+ */
+ private boolean isDesktopModeSupported() {
+ return SystemProperties.getBoolean("persist.wm.debug.desktop_mode", false)
+ || SystemProperties.getBoolean("persist.wm.debug.desktop_mode_2", 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 (!isDesktopModeSupported()) {
+ return;
+ }
+ if (freeformTasksVisible != mFreeformTasksVisible) {
+ mFreeformTasksVisible = freeformTasksVisible;
+ if (mFreeformTasksVisible) {
+ setLauncherViewsVisibility(View.INVISIBLE);
+ if (!mInOverviewState) {
+ // When freeform is visible & we're not in overview, we want launcher to appear
+ // paused, this ensures that taskbar displays.
+ markLauncherPaused();
+ }
+ } else {
+ setLauncherViewsVisibility(View.VISIBLE);
+ // If freeform isn't visible ensure that launcher appears resumed to behave
+ // normally.
+ markLauncherResumed();
+ }
+ }
+ }
+
+ /**
+ * Sets whether the overview is visible and updates launcher visibility based on that.
+ */
+ public void setOverviewStateEnabled(boolean overviewStateEnabled) {
+ if (!isDesktopModeSupported()) {
+ return;
+ }
+ if (overviewStateEnabled != mInOverviewState) {
+ mInOverviewState = overviewStateEnabled;
+ if (mInOverviewState) {
+ setLauncherViewsVisibility(View.VISIBLE);
+ markLauncherResumed();
+ } else if (mFreeformTasksVisible) {
+ setLauncherViewsVisibility(View.INVISIBLE);
+ markLauncherPaused();
+ }
+ }
+ }
+
+ /**
+ * Whether recents gesture is currently in progress.
+ */
+ public boolean isGestureInProgress() {
+ return mGestureInProgress;
+ }
+
+ /**
+ * Sets whether recents gesture is in progress.
+ */
+ public void setGestureInProgress(boolean gestureInProgress) {
+ if (!isDesktopModeSupported()) {
+ return;
+ }
+ if (gestureInProgress != mGestureInProgress) {
+ mGestureInProgress = gestureInProgress;
+ }
+ }
+
+ private void setLauncherViewsVisibility(int visibility) {
+ View workspaceView = mLauncher.getWorkspace();
+ if (workspaceView != null) {
+ workspaceView.setVisibility(visibility);
+ }
+ View dragLayer = mLauncher.getDragLayer();
+ if (dragLayer != null) {
+ dragLayer.setVisibility(visibility);
+ }
+ }
+
+ private void markLauncherPaused() {
+ StatefulActivity<LauncherState> activity =
+ QuickstepLauncher.ACTIVITY_TRACKER.getCreatedActivity();
+ if (activity != null) {
+ activity.setPaused();
+ }
+ }
+
+ private void markLauncherResumed() {
+ StatefulActivity<LauncherState> activity =
+ QuickstepLauncher.ACTIVITY_TRACKER.getCreatedActivity();
+ // Check activity state before calling setResumed(). Launcher may have been actually
+ // paused (eg fullscreen task moved to front).
+ // In this case we should not mark the activity as resumed.
+ if (activity != null && activity.isResumed()) {
+ activity.setResumed();
+ }
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java b/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java
index b4052e3..48352a2 100644
--- a/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java
@@ -19,25 +19,27 @@
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
-import com.android.launcher3.DeviceProfile.DeviceProfileListenable;
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
+import com.android.launcher3.LauncherPrefs;
+import com.android.launcher3.util.OnboardingPrefs;
import com.android.launcher3.util.Themes;
-import com.android.launcher3.views.AppLauncher;
+import com.android.launcher3.views.ActivityContext;
import java.util.ArrayList;
import java.util.List;
// TODO(b/218912746): Share more behavior to avoid all apps context depending directly on taskbar.
/** Base for common behavior between taskbar window contexts. */
-public abstract class BaseTaskbarContext extends ContextThemeWrapper implements AppLauncher,
- DeviceProfileListenable {
+public abstract class BaseTaskbarContext extends ContextThemeWrapper implements ActivityContext {
protected final LayoutInflater mLayoutInflater;
private final List<OnDeviceProfileChangeListener> mDPChangeListeners = new ArrayList<>();
+ private final OnboardingPrefs<BaseTaskbarContext> mOnboardingPrefs;
public BaseTaskbarContext(Context windowContext) {
super(windowContext, Themes.getActivityThemeRes(windowContext));
mLayoutInflater = LayoutInflater.from(this).cloneInContext(this);
+ mOnboardingPrefs = new OnboardingPrefs<>(this, LauncherPrefs.getPrefs(this));
}
@Override
@@ -50,6 +52,11 @@
return mDPChangeListeners;
}
+ @Override
+ public OnboardingPrefs<BaseTaskbarContext> getOnboardingPrefs() {
+ return mOnboardingPrefs;
+ }
+
/** Callback invoked when a drag is initiated within this context. */
public abstract void onDragStart();
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..633325b 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,12 +24,13 @@
*/
public class DesktopTaskbarUIController extends TaskbarUIController {
- private final BaseQuickstepLauncher mLauncher;
+ private final QuickstepLauncher mLauncher;
- public DesktopTaskbarUIController(BaseQuickstepLauncher launcher) {
+ public DesktopTaskbarUIController(QuickstepLauncher launcher) {
mLauncher = launcher;
}
+ @SuppressWarnings("MissingSuperCall") // TODO: Fix me
@Override
protected void init(TaskbarControllers taskbarControllers) {
super.init(taskbarControllers);
@@ -37,14 +38,15 @@
mControllers.taskbarViewController.updateRunningApps();
}
+ @SuppressWarnings("MissingSuperCall") // TODO: Fix me
@Override
protected void onDestroy() {
super.onDestroy();
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..474dc3d 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,13 +70,15 @@
* Currently this animation just force stashes the taskbar in Overview.
*/
public Animator createAnimToRecentsState(RecentsState toState, long duration) {
- boolean forceStashed = toState.hasOverviewActions();
- TaskbarStashController controller = mControllers.taskbarStashController;
+ boolean useStashedLauncherState = toState.hasOverviewActions();
+ boolean stashedLauncherState =
+ useStashedLauncherState && !FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get();
+ TaskbarStashController stashController = 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);
- return controller.applyStateWithoutStart(duration);
+ stashController.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE, stashedLauncherState);
+ stashController.updateStateForFlag(FLAG_IN_APP, !useStashedLauncherState);
+ return stashController.createApplyStateAnimator(duration);
}
private void animateToRecentsState(RecentsState toState) {
@@ -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 6df31e5..edb8721 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -15,41 +15,45 @@
*/
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.config.FeatureFlags.ENABLE_TASKBAR_EDU_TOOLTIP;
+import static com.android.launcher3.statemanager.BaseState.FLAG_NON_INTERACTIVE;
+import static com.android.launcher3.taskbar.TaskbarEduTooltipControllerKt.TOOLTIP_STEP_FEATURES;
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;
import android.annotation.ColorInt;
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.anim.AnimatedFloat;
+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.MultiPropertyFactory;
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;
import java.util.Set;
-import java.util.stream.Stream;
/**
* A data source which integrates with a Launcher instance
@@ -63,9 +67,14 @@
public static final int WIDGETS_PAGE_PROGRESS_INDEX = 2;
public static final int SYSUI_SURFACE_PROGRESS_INDEX = 3;
- private final SparseArray<Float> mTaskbarInAppDisplayProgress = new SparseArray<>(4);
+ private static final int DISPLAY_PROGRESS_COUNT = 4;
- private final BaseQuickstepLauncher mLauncher;
+ private final AnimatedFloat mTaskbarInAppDisplayProgress = new AnimatedFloat();
+ private final MultiPropertyFactory<AnimatedFloat> mTaskbarInAppDisplayProgressMultiProp =
+ new MultiPropertyFactory<>(mTaskbarInAppDisplayProgress,
+ AnimatedFloat.VALUE, DISPLAY_PROGRESS_COUNT, Float::max);
+
+ private final QuickstepLauncher mLauncher;
private final DeviceProfile.OnDeviceProfileChangeListener mOnDeviceProfileChangeListener =
dp -> {
@@ -81,7 +90,7 @@
private final TaskbarLauncherStateController
mTaskbarLauncherStateController = new TaskbarLauncherStateController();
- public LauncherTaskbarUIController(BaseQuickstepLauncher launcher) {
+ public LauncherTaskbarUIController(QuickstepLauncher launcher) {
mLauncher = launcher;
}
@@ -115,7 +124,8 @@
@Override
protected boolean isTaskbarTouchable() {
- return !mTaskbarLauncherStateController.isAnimatingToLauncher();
+ return !(mTaskbarLauncherStateController.isAnimatingToLauncher()
+ && mTaskbarLauncherStateController.goingToAlignedLauncherState());
}
public void setShouldDelayLauncherStateAnim(boolean shouldDelayLauncherStateAnim) {
@@ -124,24 +134,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 +163,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 +182,15 @@
}
}
+ // Launcher is resumed during the swipe-to-overview gesture under shell-transitions, so
+ // avoid updating taskbar state in that situation (when it's non-interactive -- or
+ // "background") to avoid premature animations.
+ if (ENABLE_SHELL_TRANSITIONS && isResumed
+ && mLauncher.getStateManager().getState().hasFlag(FLAG_NON_INTERACTIVE)
+ && !mLauncher.getStateManager().getState().isTaskbarAlignedWithHotseat(mLauncher)) {
+ return null;
+ }
+
mTaskbarLauncherStateController.updateStateForFlag(FLAG_RESUMED, isResumed);
return mTaskbarLauncherStateController.applyState(fromInit ? 0 : duration, startAnimation);
}
@@ -200,16 +203,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 +237,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,
@@ -259,36 +263,52 @@
mTaskbarOverrideBackgroundAlpha.updateValue(forceHide ? 0 : 1);
}
- @Override
- public Stream<ItemInfoWithIcon> getAppIconsForEdu() {
- return Arrays.stream(mLauncher.getAppsView().getAppsStore().getApps());
- }
-
/**
- * Starts the taskbar education flow, if the user hasn't seen it yet.
+ * Starts a Taskbar EDU flow, if the user should see one upon launching an application.
*/
- public void showEdu() {
- if (!shouldShowEdu()) {
+ public void showEduOnAppLaunch() {
+ if (!shouldShowEduOnAppLaunch()) {
return;
}
- mLauncher.getOnboardingPrefs().markChecked(OnboardingPrefs.TASKBAR_EDU_SEEN);
- mControllers.taskbarEduController.showEdu();
+ // Transient and persistent bottom sheet.
+ if (!ENABLE_TASKBAR_EDU_TOOLTIP.get()) {
+ mLauncher.getOnboardingPrefs().markChecked(OnboardingPrefs.TASKBAR_EDU_SEEN);
+ mControllers.taskbarEduController.showEdu();
+ return;
+ }
+
+ // Persistent features EDU tooltip.
+ if (!DisplayController.isTransientTaskbar(mLauncher)) {
+ mControllers.taskbarEduTooltipController.maybeShowFeaturesEdu();
+ return;
+ }
+
+ // Transient swipe EDU tooltip.
+ mControllers.taskbarEduTooltipController.maybeShowSwipeEdu();
}
/**
- * Whether the taskbar education should be shown.
+ * Returns {@code true} if a Taskbar education should be shown on application launch.
*/
- public boolean shouldShowEdu() {
- return !Utilities.IS_RUNNING_IN_TEST_HARNESS
- && !mLauncher.getOnboardingPrefs().getBoolean(OnboardingPrefs.TASKBAR_EDU_SEEN);
- }
+ public boolean shouldShowEduOnAppLaunch() {
+ if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+ return false;
+ }
- /**
- * Manually ends the taskbar education flow.
- */
- public void hideEdu() {
- mControllers.taskbarEduController.hideEdu();
+ // Transient and persistent bottom sheet.
+ if (!ENABLE_TASKBAR_EDU_TOOLTIP.get()) {
+ return !mLauncher.getOnboardingPrefs().getBoolean(OnboardingPrefs.TASKBAR_EDU_SEEN);
+ }
+
+ // Persistent features EDU tooltip.
+ if (!DisplayController.isTransientTaskbar(mLauncher)) {
+ return !mLauncher.getOnboardingPrefs().hasReachedMaxCount(
+ OnboardingPrefs.TASKBAR_EDU_TOOLTIP_STEP);
+ }
+
+ // Transient swipe EDU tooltip.
+ return mControllers.taskbarEduTooltipController.getTooltipStep() < TOOLTIP_STEP_FEATURES;
}
@Override
@@ -301,9 +321,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);
+ }
}
/**
@@ -315,42 +342,44 @@
* 1 => use in-app layout
*/
public void onTaskbarInAppDisplayProgressUpdate(float progress, int progressIndex) {
+ mTaskbarInAppDisplayProgressMultiProp.get(progressIndex).setValue(progress);
if (mControllers == null) {
// This method can be called before init() is called.
return;
}
- mTaskbarInAppDisplayProgress.put(progressIndex, progress);
- if (!mControllers.taskbarStashController.isInApp()
+ if (mControllers.uiController.isIconAlignedWithHotseat()
&& !mTaskbarLauncherStateController.isAnimatingToLauncher()) {
// Only animate the nav buttons while home and not animating home, otherwise let
// the TaskbarViewController handle it.
mControllers.navbarButtonsViewController
.getTaskbarNavButtonTranslationYForInAppDisplay()
.updateValue(mLauncher.getDeviceProfile().getTaskbarOffsetY()
- * getInAppDisplayProgress());
+ * mTaskbarInAppDisplayProgress.value);
}
}
/** Returns true iff any in-app display progress > 0. */
public boolean shouldUseInAppLayout() {
- return getInAppDisplayProgress() > 0;
+ return mTaskbarInAppDisplayProgress.value > 0;
}
- private float getInAppDisplayProgress(int index) {
- if (!mTaskbarInAppDisplayProgress.contains(index)) {
- mTaskbarInAppDisplayProgress.put(index, 0f);
- }
- return mTaskbarInAppDisplayProgress.get(index);
+ @Override
+ public void onExpandPip() {
+ super.onExpandPip();
+ mTaskbarLauncherStateController.updateStateForFlag(FLAG_RESUMED, false);
+ mTaskbarLauncherStateController.applyState();
}
- private float getInAppDisplayProgress() {
- return Stream.of(
- getInAppDisplayProgress(MINUS_ONE_PAGE_PROGRESS_INDEX),
- getInAppDisplayProgress(ALL_APPS_PAGE_PROGRESS_INDEX),
- getInAppDisplayProgress(WIDGETS_PAGE_PROGRESS_INDEX),
- getInAppDisplayProgress(SYSUI_SURFACE_PROGRESS_INDEX))
- .max(Float::compareTo)
- .get();
+ @Override
+ public boolean isIconAlignedWithHotseat() {
+ return mTaskbarLauncherStateController.isIconAlignedWithHotseat();
+ }
+
+ @Override
+ public boolean isHotseatIconOnTopWhenAligned() {
+ return mTaskbarLauncherStateController.isInHotseatOnTopStates()
+ && mTaskbarInAppDisplayProgressMultiProp.get(MINUS_ONE_PAGE_PROGRESS_INDEX)
+ .getValue() == 0;
}
@Override
@@ -361,29 +390,21 @@
"%s\tmTaskbarOverrideBackgroundAlpha=%.2f",
prefix,
mTaskbarOverrideBackgroundAlpha.value));
-
pw.println(String.format("%s\tTaskbar in-app display progress:", prefix));
- if (mControllers == null) {
- pw.println(String.format("%s\t\tMissing mControllers", prefix));
- } else {
- pw.println(String.format(
- "%s\t\tprogress at MINUS_ONE_PAGE_PROGRESS_INDEX=%.2f",
- prefix,
- getInAppDisplayProgress(MINUS_ONE_PAGE_PROGRESS_INDEX)));
- pw.println(String.format(
- "%s\t\tprogress at ALL_APPS_PAGE_PROGRESS_INDEX=%.2f",
- prefix,
- getInAppDisplayProgress(ALL_APPS_PAGE_PROGRESS_INDEX)));
- pw.println(String.format(
- "%s\t\tprogress at WIDGETS_PAGE_PROGRESS_INDEX=%.2f",
- prefix,
- getInAppDisplayProgress(WIDGETS_PAGE_PROGRESS_INDEX)));
- pw.println(String.format(
- "%s\t\tprogress at SYSUI_SURFACE_PROGRESS_INDEX=%.2f",
- prefix,
- getInAppDisplayProgress(SYSUI_SURFACE_PROGRESS_INDEX)));
- }
+ mTaskbarInAppDisplayProgressMultiProp.dump(
+ prefix + "\t",
+ pw,
+ "mTaskbarInAppDisplayProgressMultiProp",
+ "MINUS_ONE_PAGE_PROGRESS_INDEX",
+ "ALL_APPS_PAGE_PROGRESS_INDEX",
+ "WIDGETS_PAGE_PROGRESS_INDEX",
+ "SYSUI_SURFACE_PROGRESS_INDEX");
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 9ab2b63..2b976df 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -15,16 +15,23 @@
*/
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.TaskbarNavButtonController.BUTTON_A11Y;
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_BACK;
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_HOME;
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_IME_SWITCH;
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_RECENTS;
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;
@@ -36,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;
@@ -45,9 +51,9 @@
import android.annotation.LayoutRes;
import android.content.pm.ActivityInfo.Config;
import android.content.res.ColorStateList;
-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;
@@ -63,6 +69,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;
@@ -73,16 +80,19 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AlphaUpdateListener;
+import com.android.launcher3.anim.AnimatedFloat;
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;
-import com.android.quickstep.AnimatedFloat;
import com.android.systemui.shared.rotation.FloatingRotationButton;
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;
@@ -109,12 +119,19 @@
private static final int FLAG_NOTIFICATION_SHADE_EXPANDED = 1 << 10;
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<>();
@@ -122,12 +139,14 @@
private final TaskbarActivityContext mContext;
private final FrameLayout mNavButtonsView;
- private final ViewGroup mNavButtonContainer;
+ private final LinearLayout mNavButtonContainer;
// Used for IME+A11Y buttons
private final ViewGroup mEndContextualContainer;
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);
@@ -142,6 +161,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();
@@ -160,9 +182,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;
@@ -173,6 +196,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;
}
/**
@@ -180,9 +205,15 @@
*/
public void init(TaskbarControllers controllers) {
mControllers = controllers;
- mNavButtonsView.getLayoutParams().height = mContext.getDeviceProfile().taskbarSize;
-
boolean isThreeButtonNav = mContext.isThreeButtonNav();
+ DeviceProfile deviceProfile = mContext.getDeviceProfile();
+ Resources resources = mContext.getResources();
+ Point p = !mContext.isUserSetupComplete()
+ ? new Point(0, controllers.taskbarActivityContext.getSetupWindowHeight())
+ : DimensionUtils.getTaskbarPhoneDimensions(deviceProfile, resources,
+ TaskbarManager.isPhoneMode(deviceProfile));
+ mNavButtonsView.getLayoutParams().height = p.y;
+
mIsImeRenderingNavButtons =
InputMethodService.canImeRenderGesturalNavButtons() && mContext.imeDrawsImeNavBar();
if (!mIsImeRenderingNavButtons) {
@@ -197,10 +228,15 @@
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()
+ .get(ALPHA_INDEX_SMALL_SCREEN),
+ flags -> (flags & FLAG_SMALL_SCREEN) == 0));
+
mPropertyHolders.add(new StatePropertyHolder(mControllers.taskbarDragLayerController
.getKeyguardBgTaskbar(), flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0));
@@ -227,94 +263,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();
-
- 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 = mContext.getResources().getConfiguration().uiMode
- & Configuration.UI_MODE_NIGHT_MASK;
- boolean isDarkTheme = mode == Configuration.UI_MODE_NIGHT_YES;
- mTaskbarNavButtonDarkIntensity.updateValue(isDarkTheme ? 0 : 1);
-
- if (mIsImeRenderingNavButtons) {
- // Hide the back button while the IME is visible during SUW
- mPropertyHolders.add(new StatePropertyHolder(mBackButton,
- flags -> (flags & FLAG_IME_VISIBLE) == 0));
- }
- } else if (isInKidsMode) {
- int iconSize = mContext.getResources().getDimensionPixelSize(
- R.dimen.taskbar_icon_size_kids);
- int buttonWidth = mContext.getResources().getDimensionPixelSize(
- R.dimen.taskbar_nav_buttons_width_kids);
- int buttonHeight = mContext.getResources().getDimensionPixelSize(
- R.dimen.taskbar_nav_buttons_height_kids);
- int buttonRadius = mContext.getResources().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 = mContext.getResources().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 = mContext.getResources().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);
- }
+ updateStateForFlag(FLAG_SMALL_SCREEN, isPhoneButtonNavMode(mContext));
// Animate taskbar background when either..
// notification shade expanded AND not on keyguard
@@ -348,7 +306,7 @@
if (!mIsImeRenderingNavButtons) {
View imeDownButton = addButton(R.drawable.ic_sysbar_back, BUTTON_BACK,
mStartContextualContainer, mControllers.navButtonController, R.id.back);
- imeDownButton.setRotation(Utilities.isRtl(mContext.getResources()) ? 90 : -90);
+ imeDownButton.setRotation(Utilities.isRtl(resources) ? 90 : -90);
// Only show when IME is visible.
mPropertyHolders.add(new StatePropertyHolder(imeDownButton,
flags -> (flags & FLAG_IME_VISIBLE) != 0));
@@ -383,7 +341,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
@@ -411,26 +369,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()));
@@ -514,6 +472,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
*/
@@ -573,6 +537,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;
@@ -620,6 +604,9 @@
}
private void updateNavButtonTranslationY() {
+ if (isPhoneButtonNavMode(mContext)) {
+ return;
+ }
final float normalTranslationY = mTaskbarNavButtonTranslationY.value;
final float imeAdjustmentTranslationY = mTaskbarNavButtonTranslationYForIme.value;
TaskbarUIController uiController = mControllers.uiController;
@@ -636,8 +623,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));
}
@@ -679,46 +669,160 @@
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 */
+ 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;
+ mNavButtonsView.getLayoutParams().height =
+ mControllers.taskbarActivityContext.getSetupWindowHeight();
+ mNavButtonContainer.setLayoutParams(navButtonsLayoutParams);
+ }
+
+ /**
+ * 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() {
- if (!mContext.isThreeButtonNav()) {
- return;
- }
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();
- // Add spacing after the end of the last nav button
- FrameLayout.LayoutParams navButtonParams =
- (FrameLayout.LayoutParams) mNavButtonContainer.getLayoutParams();
- 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;
+ if (TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW) {
+ boolean isThreeButtonNav = mContext.isThreeButtonNav();
+
+ NavButtonLayoutter navButtonLayoutter =
+ NavButtonLayoutFactory.Companion.getUiLayoutter(
+ dp, mNavButtonsView, res, isInKidsMode, isInSetup, isThreeButtonNav,
+ TaskbarManager.isPhoneMode(dp));
+ navButtonLayoutter.layoutButtons(dp, isContextualButtonShowing());
+ return;
}
- 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();
- 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);
+ 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));
+ } 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() {
@@ -729,6 +833,10 @@
}
moveNavButtonsBackToTaskbarWindow();
+ mNavButtonContainer.removeAllViews();
+ mEndContextualContainer.removeAllViews();
+ mStartContextualContainer.removeAllViews();
+ mAllButtons.clear();
}
/**
@@ -747,14 +855,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);
}
});
@@ -782,7 +890,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);
}
@@ -933,9 +1041,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 800e162..74e7375 100644
--- a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
@@ -25,13 +25,16 @@
import android.view.View;
import android.view.ViewOutlineProvider;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.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;
import java.io.PrintWriter;
@@ -55,15 +58,16 @@
private final TaskbarActivityContext mActivity;
private final SharedPreferences mPrefs;
private final StashedHandleView mStashedHandleView;
- private final int mStashedHandleWidth;
+ private int mStashedHandleWidth;
private final int mStashedHandleHeight;
- private final RegionSamplingHelper mRegionSamplingHelper;
+ private RegionSamplingHelper mRegionSamplingHelper;
private final MultiValueAlpha mTaskbarStashedHandleAlpha;
private final AnimatedFloat mTaskbarStashedHandleHintScale = new AnimatedFloat(
this::updateStashedHandleHintScale);
// 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();
@@ -77,7 +81,7 @@
public StashedHandleViewController(TaskbarActivityContext activity,
StashedHandleView stashedHandleView) {
mActivity = activity;
- mPrefs = Utilities.getPrefs(mActivity);
+ mPrefs = LauncherPrefs.getPrefs(mActivity);
mStashedHandleView = stashedHandleView;
mTaskbarStashedHandleAlpha = new MultiValueAlpha(mStashedHandleView, NUM_ALPHA_CHANNELS);
mTaskbarStashedHandleAlpha.setUpdateVisibility(true);
@@ -85,30 +89,30 @@
mPrefs.getBoolean(SHARED_PREFS_STASHED_HANDLE_REGION_DARK_KEY, false),
false /* animate */);
final Resources resources = mActivity.getResources();
- mStashedHandleWidth = resources.getDimensionPixelSize(R.dimen.taskbar_stashed_handle_width);
mStashedHandleHeight = resources.getDimensionPixelSize(
R.dimen.taskbar_stashed_handle_height);
- mRegionSamplingHelper = new RegionSamplingHelper(mStashedHandleView,
- new RegionSamplingHelper.SamplingCallback() {
- @Override
- public void onRegionDarknessChanged(boolean isRegionDark) {
- mStashedHandleView.updateHandleColor(isRegionDark, true /* animate */);
- mPrefs.edit().putBoolean(SHARED_PREFS_STASHED_HANDLE_REGION_DARK_KEY,
- isRegionDark).apply();
- }
-
- @Override
- public Rect getSampledRegion(View sampledView) {
- return mStashedHandleView.getSampledRegion();
- }
- }, Executors.UI_HELPER_EXECUTOR);
}
public void init(TaskbarControllers controllers) {
mControllers = controllers;
- mStashedHandleView.getLayoutParams().height = mActivity.getDeviceProfile().taskbarSize;
+ DeviceProfile deviceProfile = mActivity.getDeviceProfile();
+ Resources resources = mActivity.getResources();
+ if (isPhoneGestureNavMode(mActivity.getDeviceProfile())) {
+ mTaskbarSize = resources.getDimensionPixelSize(R.dimen.taskbar_size);
+ mStashedHandleWidth =
+ resources.getDimensionPixelSize(R.dimen.taskbar_stashed_small_screen);
+ } else {
+ 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(0);
+ mTaskbarStashedHandleAlpha.get(ALPHA_INDEX_STASHED).setValue(
+ isPhoneGestureNavMode(deviceProfile) ? 1 : 0);
mTaskbarStashedHandleHintScale.updateValue(1f);
final int stashedTaskbarHeight = mControllers.taskbarStashController.getStashedHeight();
@@ -135,13 +139,40 @@
view.setPivotX(stashedCenterX);
view.setPivotY(stashedCenterY);
});
+ initRegionSampler();
+ if (isPhoneGestureNavMode(deviceProfile)) {
+ onIsStashedChanged(true);
+ }
}
+ private void initRegionSampler() {
+ mRegionSamplingHelper = new RegionSamplingHelper(mStashedHandleView,
+ new RegionSamplingHelper.SamplingCallback() {
+ @Override
+ public void onRegionDarknessChanged(boolean isRegionDark) {
+ mStashedHandleView.updateHandleColor(isRegionDark, true /* animate */);
+ mPrefs.edit().putBoolean(SHARED_PREFS_STASHED_HANDLE_REGION_DARK_KEY,
+ isRegionDark).apply();
+ }
+
+ @Override
+ public Rect getSampledRegion(View sampledView) {
+ return mStashedHandleView.getSampledRegion();
+ }
+ }, Executors.UI_HELPER_EXECUTOR);
+ }
+
+
public void onDestroy() {
mRegionSamplingHelper.stopAndDestroy();
+ mRegionSamplingHelper = null;
}
- public MultiValueAlpha getStashedHandleAlpha() {
+ private boolean isPhoneGestureNavMode(DeviceProfile deviceProfile) {
+ return TaskbarManager.isPhoneMode(deviceProfile) && !mActivity.isThreeButtonNav();
+ }
+
+ public MultiPropertyFactory<View> getStashedHandleAlpha() {
return mTaskbarStashedHandleAlpha;
}
@@ -155,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;
@@ -194,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 c00bcb9..c01e670 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -16,6 +16,7 @@
package com.android.launcher3.taskbar;
import static android.content.pm.PackageManager.FEATURE_PC;
+import static android.os.Trace.TRACE_TAG_APP;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
@@ -23,8 +24,12 @@
import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE;
-import static com.android.launcher3.testing.shared.ResourceUtils.getBoolByName;
+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;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING;
@@ -42,6 +47,7 @@
import android.graphics.Rect;
import android.os.Process;
import android.os.SystemProperties;
+import android.os.Trace;
import android.provider.Settings;
import android.util.Log;
import android.view.Display;
@@ -74,23 +80,31 @@
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.touch.ItemClickHandler.ItemClickProxy;
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.quickstep.views.TaskView;
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;
+import java.util.function.Consumer;
/**
* The {@link ActivityContext} with which we inflate Taskbar-related Views. This allows UI elements
@@ -118,7 +132,7 @@
// The size we should return to when we call setTaskbarWindowFullscreen(false)
private int mLastRequestedNonFullscreenHeight;
- private final NavigationMode mNavMode;
+ private NavigationMode mNavMode;
private final boolean mImeDrawsImeNavBar;
private final ViewCache mViewCache = new ViewCache();
@@ -126,33 +140,40 @@
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);
-
final Resources resources = getResources();
+ matchDeviceProfile(launcherDp, getResources());
+
mNavMode = DisplayController.getNavigationMode(windowContext);
mImeDrawsImeNavBar = getBoolByName(IME_DRAWS_IME_NAV_BAR_RES_NAME, resources, false);
mIsSafeModeEnabled = TraceHelper.allowIpcs("isSafeMode",
() -> getPackageManager().isSafeMode());
- 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(
- Settings.Secure.getUriFor(Settings.Secure.NAV_BAR_KIDS_MODE), 0);
- mIsNavBarKidsMode = SettingsCache.INSTANCE.get(this).getValue(
+ mIsNavBarForceVisible = settingsCache.getValue(
Settings.Secure.getUriFor(Settings.Secure.NAV_BAR_KIDS_MODE), 0);
- updateIconSize(resources);
+ // 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);
// Get display and corners first, as views might use them in constructor.
Display display = windowContext.getDisplay();
@@ -164,8 +185,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);
@@ -194,7 +217,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),
@@ -202,12 +227,15 @@
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,
- new TaskbarInsetsController(this),
- new VoiceInteractionWindowController(this));
+ new TaskbarEduTooltipController(this));
}
public void init(@NonNull TaskbarSharedState sharedState) {
@@ -218,7 +246,12 @@
mControllers.init(sharedState);
updateSysuiStateFlags(sharedState.sysuiStateFlags, true /* fromInit */);
- mWindowManager.addView(mDragLayer, mWindowLayoutParams);
+ if (!mAddedWindow) {
+ mWindowManager.addView(mDragLayer, mWindowLayoutParams);
+ mAddedWindow = true;
+ } else {
+ mWindowManager.updateViewLayout(mDragLayer, mWindowLayoutParams);
+ }
}
@Override
@@ -227,10 +260,10 @@
}
/** Updates {@link DeviceProfile} instances for any Taskbar windows. */
- public void updateDeviceProfile(DeviceProfile dp) {
- mControllers.taskbarAllAppsController.updateDeviceProfile(dp);
- mDeviceProfile = dp.copy(this);
- updateIconSize(getResources());
+ public void updateDeviceProfile(DeviceProfile launcherDp, NavigationMode navMode) {
+ mNavMode = navMode;
+ mControllers.taskbarOverlayController.updateLauncherDeviceProfile(launcherDp);
+ matchDeviceProfile(launcherDp, getResources());
AbstractFloatingView.closeAllOpenViewsExcept(this, false, TYPE_REBIND_SAFE);
// Reapply fullscreen to take potential new screen size into account.
@@ -239,11 +272,41 @@
dispatchDeviceProfileChanged();
}
+ @Override
+ public void dispatchDeviceProfileChanged() {
+ super.dispatchDeviceProfileChanged();
+ Trace.instantForTrack(TRACE_TAG_APP, "TaskbarActivityContext#DeviceProfileChanged",
+ getDeviceProfile().toSmallString());
+ }
+
+ /**
+ * Copy the original DeviceProfile, match the number of hotseat icons and qsb width and update
+ * the icon size
+ */
+ private void matchDeviceProfile(DeviceProfile originDeviceProfile, Resources resources) {
+ mDeviceProfile = originDeviceProfile.copy(this);
+ // Taskbar should match the number of icons of hotseat
+ mDeviceProfile.numShownHotseatIcons = originDeviceProfile.numShownHotseatIcons;
+ // Same QSB width to have a smooth animation
+ mDeviceProfile.hotseatQsbWidth = originDeviceProfile.hotseatQsbWidth;
+ // Update the size of the icons
+ updateIconSize(resources);
+ }
+
+
private void updateIconSize(Resources resources) {
- float taskbarIconSize = resources.getDimension(R.dimen.taskbar_icon_size);
- mDeviceProfile.updateIconSize(1, resources);
- float iconScale = taskbarIconSize / mDeviceProfile.iconSizePx;
- mDeviceProfile.updateIconSize(iconScale, resources);
+ mDeviceProfile.iconSizePx = resources.getDimensionPixelSize(
+ DisplayController.isTransientTaskbar(this)
+ ? R.dimen.transient_taskbar_icon_size
+ : R.dimen.taskbar_icon_size);
+ mDeviceProfile.updateIconSize(1f, resources);
+ }
+
+ /**
+ * Returns the View bounds of transient taskbar.
+ */
+ public Rect getTransientTaskbarBounds() {
+ return mTransientTaskbarBounds;
}
@VisibleForTesting
@@ -263,28 +326,49 @@
* @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;
windowLayoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
windowLayoutParams.privateFlags =
WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
+ windowLayoutParams.accessibilityTitle = getString(
+ TaskbarManager.isPhoneMode(mDeviceProfile)
+ ? R.string.taskbar_phone_a11y_title
+ : R.string.taskbar_a11y_title);
return windowLayoutParams;
}
public void onConfigurationChanged(@Config int configChanges) {
mControllers.onConfigurationChanged(configChanges);
+ if (!mIsUserSetupComplete) {
+ setTaskbarWindowHeight(getSetupWindowHeight());
+ }
}
public boolean isThreeButtonNav() {
@@ -430,7 +514,7 @@
@Override
public void onDragEnd() {
- maybeSetTaskbarWindowNotFullscreen();
+ onDragEndOrViewRemoved();
}
@Override
@@ -461,7 +545,10 @@
mIsDestroyed = true;
setUIController(TaskbarUIController.DEFAULT);
mControllers.onDestroy();
- mWindowManager.removeViewImmediate(mDragLayer);
+ if (!FLAG_HIDE_NAVBAR_WINDOW) {
+ mWindowManager.removeViewImmediate(mDragLayer);
+ mAddedWindow = false;
+ }
}
public void updateSysuiStateFlags(int systemUiStateFlags, boolean fromInit) {
@@ -496,7 +583,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()
@@ -524,32 +611,38 @@
}
public void onNavButtonsDarkIntensityChanged(float darkIntensity) {
- if (!isUserSetupComplete()) {
- return;
- }
mControllers.navbarButtonsViewController.getTaskbarNavButtonDarkIntensity()
.updateValue(darkIntensity);
}
/**
+ * 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() {
@@ -596,9 +689,32 @@
* 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) {
+ return isThreeButtonNav() ?
+ resources.getDimensionPixelSize(R.dimen.taskbar_size) :
+ resources.getDimensionPixelSize(R.dimen.taskbar_stashed_size);
+ }
+
+ if (!isUserSetupComplete()) {
+ return getSetupWindowHeight();
+ }
+
+ if (DisplayController.isTransientTaskbar(this)) {
+ int taskbarSize = resources.getDimensionPixelSize(R.dimen.transient_taskbar_size);
+ return taskbarSize
+ + (2 * resources.getDimensionPixelSize(R.dimen.transient_taskbar_margin))
+ + resources.getDimensionPixelSize(R.dimen.transient_taskbar_shadow_blur);
+ }
+
return mDeviceProfile.taskbarSize + Math.max(getLeftCornerRadius(), getRightCornerRadius());
}
+ public int getSetupWindowHeight() {
+ return getResources().getDimensionPixelSize(R.dimen.taskbar_suw_frame);
+ }
+
/**
* Either adds or removes {@link WindowManager.LayoutParams#FLAG_NOT_FOCUSABLE} on the taskbar
* window.
@@ -627,7 +743,9 @@
/** Adds the given view to WindowManager with the provided LayoutParams (creates new window). */
public void addWindowView(View view, WindowManager.LayoutParams windowLayoutParams) {
- mWindowManager.addView(view, windowLayoutParams);
+ if (!view.isAttachedToWindow()) {
+ mWindowManager.addView(view, windowLayoutParams);
+ }
}
/** Removes the given view from WindowManager. See {@link #addWindowView}. */
@@ -643,6 +761,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();
@@ -672,45 +791,68 @@
});
});
} else if (tag instanceof WorkspaceItemInfo) {
+ // Tapping a launchable icon on Taskbar
WorkspaceItemInfo info = (WorkspaceItemInfo) tag;
- 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);
+ if (!info.isDisabled() || !ItemClickHandler.handleDisabledItemClicked(info, this)) {
+ TaskbarUIController taskbarUIController = mControllers.uiController;
+ RecentsView recents = taskbarUIController.getRecentsView();
+ if (recents != null && recents.isSplitSelectionActive()) {
+ // If we are selecting a second app for split, launch the split tasks
+ taskbarUIController.triggerSecondAppForSplit(info, info.intent, view);
+ } 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 {
+ launchFromTaskbarPreservingSplitIfVisible(recents, 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);
+ // Tapping an item in AllApps
+ AppInfo info = (AppInfo) tag;
+ TaskbarUIController taskbarUIController = mControllers.uiController;
+ RecentsView recents = taskbarUIController.getRecentsView();
+ if (recents != null
+ && taskbarUIController.getRecentsView().isSplitSelectionActive()) {
+ // If we are selecting a second app for split, launch the split tasks
+ taskbarUIController.triggerSecondAppForSplit(info, info.intent, view);
+ } else {
+ launchFromTaskbarPreservingSplitIfVisible(recents, info);
+ mControllers.uiController.onTaskbarIconLaunched(info);
+ }
+ mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
+ } else if (tag instanceof ItemClickProxy) {
+ ((ItemClickProxy) tag).onItemClicked(view);
} else {
Log.e(TAG, "Unknown type clicked: " + tag);
}
@@ -718,6 +860,31 @@
AbstractFloatingView.closeAllOpenViews(this);
}
+ /**
+ * Run when the user taps a Taskbar icon while in Overview. If the tapped app is currently
+ * visible to the user in Overview, or is part of a visible split pair, we expand the TaskView
+ * as if the user tapped on it (preserving the split pair). Otherwise, launch it normally
+ * (potentially breaking a split pair).
+ */
+ private void launchFromTaskbarPreservingSplitIfVisible(RecentsView recents, ItemInfo info) {
+ recents.findLastActiveTaskAndRunCallback(
+ info.getTargetComponent(),
+ (Consumer<Task>) foundTask -> {
+ if (foundTask != null) {
+ TaskView foundTaskView =
+ recents.getTaskViewByTaskId(foundTask.key.id);
+ if (foundTaskView != null
+ && foundTaskView.isVisibleToUser()) {
+ TestLogging.recordEvent(
+ TestProtocol.SEQUENCE_MAIN, "start: taskbarAppIcon");
+ foundTaskView.launchTasks();
+ return;
+ }
+ }
+ startItemInfoActivity(info);
+ });
+ }
+
private void startItemInfoActivity(ItemInfo info) {
Intent intent = new Intent(info.getIntent())
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
@@ -746,6 +913,40 @@
}
/**
+ * Called when we want to unstash taskbar when user performs swipes up gesture.
+ */
+ public void onSwipeToUnstashTaskbar() {
+ mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(false);
+ mControllers.taskbarEduTooltipController.hide();
+ }
+
+ /** Returns {@code true} if taskbar All Apps is open. */
+ public boolean isTaskbarAllAppsOpen() {
+ return mControllers.taskbarAllAppsController.isOpen();
+ }
+
+ /**
+ * 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.
*/
@@ -753,11 +954,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();
}
@@ -791,12 +1023,13 @@
}
mControllers.taskbarStashController.addUnstashToHotseatAnimation(fullAnimation, duration);
- if (!FeatureFlags.ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT.get()) {
+ View allAppsButton = mControllers.taskbarViewController.getAllAppsButtonView();
+ if (allAppsButton != null && !FeatureFlags.ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT.get()) {
ValueAnimator alphaOverride = ValueAnimator.ofFloat(0, 1);
alphaOverride.setDuration(duration);
alphaOverride.addUpdateListener(a -> {
// Override the alpha updates in the icon alignment animation.
- mControllers.taskbarViewController.getAllAppsButtonView().setAlpha(0);
+ allAppsButton.setAlpha(0);
});
fullAnimation.play(alphaOverride);
}
@@ -830,6 +1063,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:");
@@ -844,6 +1081,11 @@
pw.println(String.format(
"%s\tmBindInProgress=%b", prefix, mBindingItems));
mControllers.dumpLogs(prefix + "\t", pw);
- mDeviceProfile.dump(prefix, 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..2bfc7dd 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendController.java
@@ -33,21 +33,31 @@
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;
+ // Taskbar EDU overlay is open above the Taskbar. */
+ public static final int FLAG_AUTOHIDE_SUSPEND_EDU_OPEN = 1 << 3;
@IntDef(flag = true, value = {
FLAG_AUTOHIDE_SUSPEND_FULLSCREEN,
FLAG_AUTOHIDE_SUSPEND_DRAGGING,
+ FLAG_AUTOHIDE_SUSPEND_TOUCHING,
+ FLAG_AUTOHIDE_SUSPEND_EDU_OPEN,
})
@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 +69,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 +104,8 @@
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");
+ appendFlag(str, flags, FLAG_AUTOHIDE_SUSPEND_EDU_OPEN, "FLAG_AUTOHIDE_SUSPEND_EDU_OPEN");
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..cf3af08 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
@@ -17,20 +17,37 @@
package com.android.launcher3.taskbar
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.Utilities.mapToRange
+import com.android.launcher3.anim.Interpolators
+import com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound
+import com.android.launcher3.util.DisplayController
-/**
- * Helps draw the taskbar background, made up of a rectangle plus two inverted rounded corners.
- */
+/** Helps draw the taskbar background, made up of a rectangle plus two inverted rounded corners. */
class TaskbarBackgroundRenderer(context: TaskbarActivityContext) {
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 +56,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()
@@ -53,23 +95,57 @@
invertedRightCornerPath.op(square, circle, Path.Op.DIFFERENCE)
}
- /**
- * Draws the background with the given paint and height, on the provided canvas.
- */
+ /** Draws the background with the given paint and height, on the provided canvas. */
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 e076754..ea70de4 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
@@ -21,7 +21,9 @@
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
+import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.taskbar.allapps.TaskbarAllAppsController;
+import com.android.launcher3.taskbar.overlay.TaskbarOverlayController;
import com.android.systemui.shared.rotation.RotationButtonController;
import java.io.PrintWriter;
@@ -51,11 +53,15 @@
public final TaskbarPopupController taskbarPopupController;
public final TaskbarForceVisibleImmersiveController taskbarForceVisibleImmersiveController;
public final TaskbarAllAppsController taskbarAllAppsController;
- public final TaskbarRecentAppsController taskbarRecentAppsController;
public final TaskbarInsetsController taskbarInsetsController;
public final VoiceInteractionWindowController voiceInteractionWindowController;
+ public final TaskbarRecentAppsController taskbarRecentAppsController;
+ public final TaskbarTranslationController taskbarTranslationController;
+ public final TaskbarOverlayController taskbarOverlayController;
+ public final TaskbarEduTooltipController taskbarEduTooltipController;
@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 +71,16 @@
@Nullable private TaskbarSharedState mSharedState = null;
+ // Roundness property for round corner above taskbar .
+ private final AnimatedFloat mCornerRoundness = new AnimatedFloat(this::updateCornerRoundness);
+
+ /**
+ * Want to add a new controller? Don't forget to:
+ * * Call init
+ * * Call onDestroy
+ * * Add to mControllersToLog
+ * * Add tests by adding this controller to TaskbarBaseTestCase.kt and extending that class
+ */
public TaskbarControllers(TaskbarActivityContext taskbarActivityContext,
TaskbarDragController taskbarDragController,
TaskbarNavButtonController navButtonController,
@@ -81,10 +97,13 @@
TaskbarAutohideSuspendController taskbarAutoHideSuspendController,
TaskbarPopupController taskbarPopupController,
TaskbarForceVisibleImmersiveController taskbarForceVisibleImmersiveController,
+ TaskbarOverlayController taskbarOverlayController,
TaskbarAllAppsController taskbarAllAppsController,
- TaskbarRecentAppsController taskbarRecentAppsController,
TaskbarInsetsController taskbarInsetsController,
- VoiceInteractionWindowController voiceInteractionWindowController) {
+ VoiceInteractionWindowController voiceInteractionWindowController,
+ TaskbarTranslationController taskbarTranslationController,
+ TaskbarRecentAppsController taskbarRecentAppsController,
+ TaskbarEduTooltipController taskbarEduTooltipController) {
this.taskbarActivityContext = taskbarActivityContext;
this.taskbarDragController = taskbarDragController;
this.navButtonController = navButtonController;
@@ -101,10 +120,13 @@
this.taskbarAutohideSuspendController = taskbarAutoHideSuspendController;
this.taskbarPopupController = taskbarPopupController;
this.taskbarForceVisibleImmersiveController = taskbarForceVisibleImmersiveController;
+ this.taskbarOverlayController = taskbarOverlayController;
this.taskbarAllAppsController = taskbarAllAppsController;
- this.taskbarRecentAppsController = taskbarRecentAppsController;
this.taskbarInsetsController = taskbarInsetsController;
this.voiceInteractionWindowController = voiceInteractionWindowController;
+ this.taskbarTranslationController = taskbarTranslationController;
+ this.taskbarRecentAppsController = taskbarRecentAppsController;
+ this.taskbarEduTooltipController = taskbarEduTooltipController;
}
/**
@@ -129,11 +151,14 @@
taskbarEduController.init(this);
taskbarPopupController.init(this);
taskbarForceVisibleImmersiveController.init(this);
+ taskbarOverlayController.init(this);
taskbarAllAppsController.init(this, sharedState.allAppsVisible);
- taskbarRecentAppsController.init(this);
navButtonController.init(this);
taskbarInsetsController.init(this);
voiceInteractionWindowController.init(this);
+ taskbarRecentAppsController.init(this);
+ taskbarTranslationController.init(this);
+ taskbarEduTooltipController.init(this);
mControllersToLog = new LoggableTaskbarController[] {
taskbarDragController, navButtonController, navbarButtonsViewController,
@@ -141,8 +166,14 @@
taskbarUnfoldAnimationController, taskbarKeyguardController,
stashedHandleViewController, taskbarStashController, taskbarEduController,
taskbarAutohideSuspendController, taskbarPopupController, taskbarInsetsController,
+ voiceInteractionWindowController, taskbarTranslationController,
+ taskbarEduTooltipController
+ };
+ mBackgroundRendererControllers = new BackgroundRendererController[] {
+ taskbarDragLayerController, taskbarScrimViewController,
voiceInteractionWindowController
};
+ mCornerRoundness.updateValue(TaskbarBackgroundRenderer.DEFAULT_ROUNDNESS);
mAreAllControllersInitialized = true;
for (Runnable postInitCallback : mPostInitCallbacks) {
@@ -159,12 +190,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 +211,14 @@
taskbarAutohideSuspendController.onDestroy();
taskbarPopupController.onDestroy();
taskbarForceVisibleImmersiveController.onDestroy();
- taskbarAllAppsController.onDestroy();
- taskbarRecentAppsController.onDestroy();
+ taskbarOverlayController.onDestroy();
navButtonController.onDestroy();
taskbarInsetsController.onDestroy();
voiceInteractionWindowController.onDestroy();
+ taskbarRecentAppsController.onDestroy();
mControllersToLog = null;
+ mBackgroundRendererControllers = null;
}
/**
@@ -218,6 +252,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 +278,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 49c5f6e..0361c75 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");
@@ -192,8 +207,13 @@
if (FeatureFlags.ENABLE_TASKBAR_POPUP_MENU.get()
&& !shouldStartDrag(0)) {
- // Immediately close the popup menu.
- mDragView.setOnAnimationEndCallback(() -> callOnDragStart());
+ mDragView.setOnAnimationEndCallback(() -> {
+ // Drag might be cancelled during the DragView animation, so check
+ // mIsPreDrag again.
+ if (mIsInPreDrag) {
+ callOnDragStart();
+ }
+ });
}
}
@@ -287,11 +307,20 @@
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 {
+ // stash the transient taskbar
+ mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
+
+ 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 +388,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);
@@ -392,9 +421,14 @@
if (dragEvent.getResult()) {
maybeOnDragEnd();
} else {
+ // un-stash the transient taskbar in case drag and drop was canceled
+ mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(false);
+
// This will take care of calling maybeOnDragEnd() after the animation
animateGlobalDragViewToOriginalPosition(btv, dragEvent);
}
+ mActivity.getDragLayer().setOnDragListener(null);
+
return true;
}
return false;
@@ -421,6 +455,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 +504,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;
@@ -505,7 +542,7 @@
// maybeOnDragEnd()
SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
transaction.remove(dragSurface);
- SurfaceSyncGroup syncGroup = new SurfaceSyncGroup();
+ SurfaceSyncGroup syncGroup = new SurfaceSyncGroup("TaskBarController");
syncGroup.addSyncCompleteCallback(mActivity.getMainExecutor(), transaction::close);
syncGroup.addToSync(viewRoot);
syncGroup.addTransactionToSync(transaction);
@@ -516,6 +553,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 +638,7 @@
@Override
protected void exitDrag() {
- if (mDragObject != null) {
+ if (mDragObject != null && !mDisallowGlobalDrag) {
mActivity.getDragLayer().removeView(mDragObject.dragView);
}
}
@@ -555,6 +654,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 +668,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 1c8148b..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,28 +79,33 @@
mControllers = mControllerCallbacks.getTouchControllers();
}
- private void onComputeTaskbarInsets(InsetsInfo insetsInfo) {
+ private void onComputeTaskbarInsets(ViewTreeObserver.InternalInsetsInfo insetsInfo) {
if (mControllerCallbacks != null) {
mControllerCallbacks.updateInsetsTouchability(insetsInfo);
}
}
+ protected void onDestroy(boolean forceDestroy) {
+ if (forceDestroy) {
+ getViewTreeObserver().removeOnComputeInternalInsetsListener(mTaskbarInsetsComputer);
+ }
+ }
+
protected void onDestroy() {
- ViewTreeObserverWrapper.removeOnComputeInsetsListener(mTaskbarInsetsComputer);
+ onDestroy(!TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- ViewTreeObserverWrapper.addOnComputeInsetsListener(getViewTreeObserver(),
- mTaskbarInsetsComputer);
+ getViewTreeObserver().addOnComputeInternalInsetsListener(mTaskbarInsetsComputer);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
- onDestroy();
+ onDestroy(true);
}
@Override
@@ -112,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) {
@@ -146,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 77ef83c..cd27a46 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
@@ -16,23 +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.anim.AnimatedFloat;
+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);
@@ -59,6 +67,7 @@
mTaskbarDragLayer = taskbarDragLayer;
final Resources resources = mTaskbarDragLayer.getResources();
mFolderMargin = resources.getDimensionPixelSize(R.dimen.taskbar_folder_margin);
+ updateGestureHeight();
}
public void init(TaskbarControllers controllers) {
@@ -118,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
@@ -128,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);
@@ -154,26 +188,70 @@
*/
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();
}
/**
* Returns how tall the background should be drawn at the bottom of the screen.
*/
public int getTaskbarBackgroundHeight() {
- return mActivity.getDeviceProfile().taskbarSize;
+ DeviceProfile deviceProfile = mActivity.getDeviceProfile();
+ if (TaskbarManager.isPhoneMode(deviceProfile)) {
+ Resources resources = mActivity.getResources();
+ 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..d3f1b2f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduController.java
@@ -15,69 +15,33 @@
*/
package com.android.launcher3.taskbar;
-import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
-import static com.android.launcher3.anim.Interpolators.ACCEL_2;
-import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
-import static com.android.launcher3.anim.Interpolators.DEACCEL;
-import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.Keyframe;
-import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
-import android.animation.TimeInterpolator;
-import android.content.res.Resources;
-import android.text.TextUtils;
+import android.view.LayoutInflater;
import android.view.View;
+import android.view.ViewGroup;
+import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
-import com.android.launcher3.icons.BitmapInfo;
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.uioverrides.PredictedAppIcon;
+import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
+import com.android.launcher3.taskbar.overlay.TaskbarOverlayController;
+import com.android.launcher3.util.DisplayController;
+
+import com.airbnb.lottie.LottieAnimationView;
import java.io.PrintWriter;
-import java.util.Collections;
-import java.util.List;
-import java.util.stream.Collectors;
/** Handles the Taskbar Education flow. */
public class TaskbarEduController implements TaskbarControllers.LoggableTaskbarController {
- private static final long WAVE_ANIM_DELAY = 250;
- private static final long WAVE_ANIM_STAGGER = 50;
- private static final long WAVE_ANIM_EACH_ICON_DURATION = 633;
- private static final long WAVE_ANIM_SLOT_MACHINE_DURATION = 1085;
- // The fraction of each icon's animation at which we reach the top point of the wave.
- private static final float WAVE_ANIM_FRACTION_TOP = 0.4f;
- // The fraction of each icon's animation at which we reach the bottom, before overshooting.
- private static final float WAVE_ANIM_FRACTION_BOTTOM = 0.9f;
- private static final TimeInterpolator WAVE_ANIM_TO_TOP_INTERPOLATOR = FAST_OUT_SLOW_IN;
- private static final TimeInterpolator WAVE_ANIM_TO_BOTTOM_INTERPOLATOR = ACCEL_2;
- private static final TimeInterpolator WAVE_ANIM_OVERSHOOT_INTERPOLATOR = DEACCEL;
- private static final TimeInterpolator WAVE_ANIM_OVERSHOOT_RETURN_INTERPOLATOR = ACCEL_DEACCEL;
- private static final float WAVE_ANIM_ICON_SCALE = 1.2f;
- // How many icons to cycle through in the slot machine (+ the original icon at each end).
- private static final int WAVE_ANIM_SLOT_MACHINE_NUM_ICONS = 3;
-
private final TaskbarActivityContext mActivity;
- private final float mWaveAnimTranslationY;
- private final float mWaveAnimTranslationYReturnOvershoot;
// Initialized in init.
TaskbarControllers mControllers;
private TaskbarEduView mTaskbarEduView;
- private Animator mAnim;
+ private TaskbarEduPagedView mPagedView;
public TaskbarEduController(TaskbarActivityContext activity) {
mActivity = activity;
-
- final Resources resources = activity.getResources();
- mWaveAnimTranslationY = resources.getDimension(R.dimen.taskbar_edu_wave_anim_trans_y);
- mWaveAnimTranslationYReturnOvershoot = resources.getDimension(
- R.dimen.taskbar_edu_wave_anim_trans_y_return_overshoot);
}
public void init(TaskbarControllers controllers) {
@@ -85,123 +49,59 @@
}
void showEdu() {
- mActivity.setTaskbarWindowFullscreen(true);
- mActivity.getDragLayer().post(() -> {
- mTaskbarEduView = (TaskbarEduView) mActivity.getLayoutInflater().inflate(
- R.layout.taskbar_edu, mActivity.getDragLayer(), false);
- mTaskbarEduView.init(new TaskbarEduCallbacks());
- mTaskbarEduView.addOnCloseListener(() -> mTaskbarEduView = null);
- mTaskbarEduView.show();
- startAnim(createWaveAnim());
- });
- }
+ TaskbarOverlayController overlayController = mControllers.taskbarOverlayController;
+ TaskbarOverlayContext overlayContext = overlayController.requestWindow();
+ LayoutInflater layoutInflater = overlayContext.getLayoutInflater();
- void hideEdu() {
- if (mTaskbarEduView != null) {
- mTaskbarEduView.close(true /* animate */);
- }
- }
+ mTaskbarEduView = (TaskbarEduView) layoutInflater.inflate(
+ R.layout.taskbar_edu, overlayContext.getDragLayer(), false);
+ mPagedView = mTaskbarEduView.findViewById(R.id.content);
+ layoutInflater.inflate(
+ DisplayController.isTransientTaskbar(overlayContext)
+ ? R.layout.taskbar_edu_pages_transient
+ : R.layout.taskbar_edu_pages_persistent,
+ mPagedView,
+ true);
- /**
- * Starts the given animation, ending the previous animation first if it's still playing.
- */
- private void startAnim(Animator anim) {
- if (mAnim != null) {
- mAnim.end();
- }
- mAnim = anim;
- mAnim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mAnim = null;
- }
- });
- mAnim.start();
- }
+ // Provide enough room for taskbar.
+ View startButton = mTaskbarEduView.findViewById(R.id.edu_start_button);
+ ViewGroup.MarginLayoutParams layoutParams =
+ (ViewGroup.MarginLayoutParams) startButton.getLayoutParams();
+ DeviceProfile dp = overlayContext.getDeviceProfile();
+ layoutParams.bottomMargin += DisplayController.isTransientTaskbar(overlayContext)
+ ? dp.taskbarSize + dp.transientTaskbarMargin
+ : dp.taskbarSize;
- /**
- * Creates a staggered "wave" animation where each icon translates and scales up in succession.
- */
- private Animator createWaveAnim() {
- AnimatorSet waveAnim = new AnimatorSet();
- View[] icons = mControllers.taskbarViewController.getIconViews();
- for (int i = 0; i < icons.length; i++) {
- View icon = icons[i];
- AnimatorSet iconAnim = new AnimatorSet();
+ mTaskbarEduView.init(new TaskbarEduCallbacks());
- Keyframe[] scaleKeyframes = new Keyframe[] {
- Keyframe.ofFloat(0, 1f),
- Keyframe.ofFloat(WAVE_ANIM_FRACTION_TOP, WAVE_ANIM_ICON_SCALE),
- Keyframe.ofFloat(WAVE_ANIM_FRACTION_BOTTOM, 1f),
- Keyframe.ofFloat(1f, 1f)
- };
- scaleKeyframes[1].setInterpolator(WAVE_ANIM_TO_TOP_INTERPOLATOR);
- scaleKeyframes[2].setInterpolator(WAVE_ANIM_TO_BOTTOM_INTERPOLATOR);
-
- Keyframe[] translationYKeyframes = new Keyframe[] {
- Keyframe.ofFloat(0, 0f),
- Keyframe.ofFloat(WAVE_ANIM_FRACTION_TOP, -mWaveAnimTranslationY),
- Keyframe.ofFloat(WAVE_ANIM_FRACTION_BOTTOM, 0f),
- // Half of the remaining fraction overshoots, then the other half returns to 0.
- Keyframe.ofFloat(
- WAVE_ANIM_FRACTION_BOTTOM + (1 - WAVE_ANIM_FRACTION_BOTTOM) / 2f,
- mWaveAnimTranslationYReturnOvershoot),
- Keyframe.ofFloat(1f, 0f)
- };
- translationYKeyframes[1].setInterpolator(WAVE_ANIM_TO_TOP_INTERPOLATOR);
- translationYKeyframes[2].setInterpolator(WAVE_ANIM_TO_BOTTOM_INTERPOLATOR);
- translationYKeyframes[3].setInterpolator(WAVE_ANIM_OVERSHOOT_INTERPOLATOR);
- translationYKeyframes[4].setInterpolator(WAVE_ANIM_OVERSHOOT_RETURN_INTERPOLATOR);
-
- iconAnim.play(ObjectAnimator.ofPropertyValuesHolder(icon,
- PropertyValuesHolder.ofKeyframe(SCALE_PROPERTY, scaleKeyframes))
- .setDuration(WAVE_ANIM_EACH_ICON_DURATION));
- iconAnim.play(ObjectAnimator.ofPropertyValuesHolder(icon,
- PropertyValuesHolder.ofKeyframe(View.TRANSLATION_Y, translationYKeyframes))
- .setDuration(WAVE_ANIM_EACH_ICON_DURATION));
-
- if (icon instanceof PredictedAppIcon) {
- // Play slot machine animation through random icons from AllAppsList.
- PredictedAppIcon predictedAppIcon = (PredictedAppIcon) icon;
- ItemInfo itemInfo = (ItemInfo) icon.getTag();
- List<BitmapInfo> iconsToAnimate = mControllers.uiController.getAppIconsForEdu()
- .filter(appInfo -> !TextUtils.equals(appInfo.title, itemInfo.title))
- .map(appInfo -> appInfo.bitmap)
- .filter(bitmap -> !bitmap.isNullOrLowRes())
- .collect(Collectors.toList());
- // Pick n icons at random.
- Collections.shuffle(iconsToAnimate);
- if (iconsToAnimate.size() > WAVE_ANIM_SLOT_MACHINE_NUM_ICONS) {
- iconsToAnimate = iconsToAnimate.subList(0, WAVE_ANIM_SLOT_MACHINE_NUM_ICONS);
- }
- Animator slotMachineAnim = predictedAppIcon.createSlotMachineAnim(iconsToAnimate);
- if (slotMachineAnim != null) {
- iconAnim.play(slotMachineAnim.setDuration(WAVE_ANIM_SLOT_MACHINE_DURATION));
- }
- }
-
- iconAnim.setStartDelay(WAVE_ANIM_STAGGER * i);
- waveAnim.play(iconAnim);
- }
- waveAnim.setStartDelay(WAVE_ANIM_DELAY);
- return waveAnim;
+ mControllers.navbarButtonsViewController.setSlideInViewVisible(true);
+ mTaskbarEduView.setOnCloseBeginListener(
+ () -> mControllers.navbarButtonsViewController.setSlideInViewVisible(false));
+ mTaskbarEduView.addOnCloseListener(() -> mTaskbarEduView = null);
+ mTaskbarEduView.show();
}
@Override
public void dumpLogs(String prefix, PrintWriter pw) {
pw.println(prefix + "TaskbarEduController:");
-
pw.println(prefix + "\tisShowingEdu=" + (mTaskbarEduView != null));
- pw.println(prefix + "\tmWaveAnimTranslationY=" + mWaveAnimTranslationY);
- pw.println(prefix + "\tmWaveAnimTranslationYReturnOvershoot="
- + mWaveAnimTranslationYReturnOvershoot);
}
/**
* Callbacks for {@link TaskbarEduView} to interact with its controller.
*/
class TaskbarEduCallbacks {
- void onPageChanged(int currentPage, int pageCount) {
+ void onPageChanged(int prevPage, int currentPage, int pageCount) {
+ // Reset previous pages' animation.
+ LottieAnimationView prevAnimation = mPagedView.getChildAt(prevPage)
+ .findViewById(R.id.animation);
+ prevAnimation.cancelAnimation();
+ prevAnimation.setFrame(0);
+
+ mPagedView.getChildAt(currentPage)
+ .<LottieAnimationView>findViewById(R.id.animation)
+ .playAnimation();
+
if (currentPage == 0) {
mTaskbarEduView.updateStartButton(R.string.taskbar_edu_close,
v -> mTaskbarEduView.close(true /* animate */));
@@ -217,5 +117,17 @@
v -> mTaskbarEduView.snapToPage(currentPage + 1));
}
}
+
+ int getIconLayoutBoundsWidth() {
+ return mControllers.taskbarViewController.getIconLayoutWidth();
+ }
+
+ int getOpenDuration() {
+ return mControllers.taskbarOverlayController.getOpenDuration();
+ }
+
+ int getCloseDuration() {
+ return mControllers.taskbarOverlayController.getCloseDuration();
+ }
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduPagedView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduPagedView.java
index 8e57ea6..6cd6512 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduPagedView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduPagedView.java
@@ -48,7 +48,7 @@
void setControllerCallbacks(TaskbarEduCallbacks controllerCallbacks) {
mControllerCallbacks = controllerCallbacks;
- mControllerCallbacks.onPageChanged(getCurrentPage(), getPageCount());
+ mControllerCallbacks.onPageChanged(getCurrentPage(), getCurrentPage(), getPageCount());
}
@Override
@@ -67,7 +67,7 @@
@Override
protected void notifyPageSwitchListener(int prevPage) {
super.notifyPageSwitchListener(prevPage);
- mControllerCallbacks.onPageChanged(getCurrentPage(), getPageCount());
+ mControllerCallbacks.onPageChanged(prevPage, getCurrentPage(), getPageCount());
}
@Override
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltip.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltip.kt
new file mode 100644
index 0000000..7dda73f
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltip.kt
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.taskbar
+
+import android.animation.AnimatorSet
+import android.animation.ValueAnimator
+import android.content.Context
+import android.provider.Settings
+import android.provider.Settings.Secure.LAUNCHER_TASKBAR_EDUCATION_SHOWING
+import android.util.AttributeSet
+import android.view.MotionEvent
+import android.view.MotionEvent.ACTION_DOWN
+import android.view.View
+import android.view.ViewGroup
+import android.view.animation.Interpolator
+import com.android.launcher3.AbstractFloatingView
+import com.android.launcher3.R
+import com.android.launcher3.anim.AnimatorListeners
+import com.android.launcher3.popup.RoundedArrowDrawable
+import com.android.launcher3.util.Themes
+import com.android.launcher3.views.ActivityContext
+import com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE
+import com.android.systemui.animation.Interpolators.EMPHASIZED_DECELERATE
+import com.android.systemui.animation.Interpolators.STANDARD
+
+private const val ENTER_DURATION_MS = 300L
+private const val EXIT_DURATION_MS = 150L
+
+/** Floating tooltip for Taskbar education. */
+class TaskbarEduTooltip
+@JvmOverloads
+constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0,
+) : AbstractFloatingView(context, attrs, defStyleAttr) {
+
+ private val activityContext: ActivityContext = ActivityContext.lookupContext(context)
+
+ private val backgroundColor =
+ Themes.getAttrColor(context, com.android.internal.R.attr.colorSurface)
+
+ private val tooltipCornerRadius = Themes.getDialogCornerRadius(context)
+ private val arrowWidth = resources.getDimension(R.dimen.popup_arrow_width)
+ private val arrowHeight = resources.getDimension(R.dimen.popup_arrow_height)
+ private val arrowPointRadius = resources.getDimension(R.dimen.popup_arrow_corner_radius)
+
+ private val enterYDelta = resources.getDimension(R.dimen.taskbar_edu_tooltip_enter_y_delta)
+ private val exitYDelta = resources.getDimension(R.dimen.taskbar_edu_tooltip_exit_y_delta)
+
+ /** Container where the tooltip's body should be inflated. */
+ lateinit var content: ViewGroup
+ private set
+ private lateinit var arrow: View
+
+ /** Callback invoked when the tooltip is being closed. */
+ var onCloseCallback: () -> Unit = {}
+ private var openCloseAnimator: AnimatorSet? = null
+
+ /** Animates the tooltip into view. */
+ fun show() {
+ if (isOpen) {
+ return
+ }
+ mIsOpen = true
+ activityContext.dragLayer.addView(this)
+ openCloseAnimator = createOpenCloseAnimator(isOpening = true).apply { start() }
+ }
+
+ override fun onFinishInflate() {
+ super.onFinishInflate()
+
+ content = findViewById(R.id.content)
+ arrow = findViewById(R.id.arrow)
+ arrow.background =
+ RoundedArrowDrawable(
+ arrowWidth,
+ arrowHeight,
+ arrowPointRadius,
+ tooltipCornerRadius,
+ measuredWidth.toFloat(),
+ measuredHeight.toFloat(),
+ (measuredWidth - arrowWidth) / 2, // arrowOffsetX
+ 0f, // arrowOffsetY
+ false, // isPointingUp
+ true, // leftAligned
+ backgroundColor,
+ )
+ }
+
+ override fun handleClose(animate: Boolean) {
+ if (!isOpen) {
+ return
+ }
+
+ onCloseCallback()
+ if (!animate) {
+ return closeComplete()
+ }
+
+ openCloseAnimator?.cancel()
+ openCloseAnimator = createOpenCloseAnimator(isOpening = false)
+ openCloseAnimator?.addListener(AnimatorListeners.forEndCallback(this::closeComplete))
+ openCloseAnimator?.start()
+ }
+
+ override fun isOfType(type: Int): Boolean = type and TYPE_TASKBAR_EDUCATION_DIALOG != 0
+
+ override fun onControllerInterceptTouchEvent(ev: MotionEvent?): Boolean {
+ if (ev?.action == ACTION_DOWN && !activityContext.dragLayer.isEventOverView(this, ev)) {
+ close(true)
+ }
+ return false
+ }
+
+ override fun onDetachedFromWindow() {
+ super.onDetachedFromWindow()
+ Settings.Secure.putInt(mContext.contentResolver, LAUNCHER_TASKBAR_EDUCATION_SHOWING, 0)
+ }
+
+ private fun closeComplete() {
+ openCloseAnimator?.cancel()
+ openCloseAnimator = null
+ mIsOpen = false
+ activityContext.dragLayer.removeView(this)
+ }
+
+ private fun createOpenCloseAnimator(isOpening: Boolean): AnimatorSet {
+ val duration: Long
+ val alphaValues: FloatArray
+ val translateYValues: FloatArray
+ val fadeInterpolator: Interpolator
+ val translateYInterpolator: Interpolator
+
+ if (isOpening) {
+ duration = ENTER_DURATION_MS
+ alphaValues = floatArrayOf(0f, 1f)
+ translateYValues = floatArrayOf(enterYDelta, 0f)
+ fadeInterpolator = STANDARD
+ translateYInterpolator = EMPHASIZED_DECELERATE
+ } else {
+ duration = EXIT_DURATION_MS
+ alphaValues = floatArrayOf(1f, 0f)
+ translateYValues = floatArrayOf(0f, exitYDelta)
+ fadeInterpolator = EMPHASIZED_ACCELERATE
+ translateYInterpolator = EMPHASIZED_ACCELERATE
+ }
+
+ val fade =
+ ValueAnimator.ofFloat(*alphaValues).apply {
+ interpolator = fadeInterpolator
+ addUpdateListener {
+ val alpha = it.animatedValue as Float
+ content.alpha = alpha
+ arrow.alpha = alpha
+ }
+ }
+
+ val translateY =
+ ValueAnimator.ofFloat(*translateYValues).apply {
+ interpolator = translateYInterpolator
+ addUpdateListener {
+ val translationY = it.animatedValue as Float
+ content.translationY = translationY
+ arrow.translationY = translationY
+ }
+ }
+
+ return AnimatorSet().apply {
+ this.duration = duration
+ playTogether(fade, translateY)
+ }
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt
new file mode 100644
index 0000000..c4d1ab2
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.taskbar
+
+import android.graphics.PorterDuff.Mode.SRC_ATOP
+import android.graphics.PorterDuffColorFilter
+import android.view.View
+import android.view.ViewGroup
+import androidx.annotation.IntDef
+import androidx.annotation.LayoutRes
+import com.airbnb.lottie.LottieAnimationView
+import com.airbnb.lottie.LottieProperty.COLOR_FILTER
+import com.airbnb.lottie.model.KeyPath
+import com.android.launcher3.R
+import com.android.launcher3.Utilities
+import com.android.launcher3.Utilities.IS_RUNNING_IN_TEST_HARNESS
+import com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_EDU_TOOLTIP
+import com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_EDU_OPEN
+import com.android.launcher3.taskbar.TaskbarControllers.LoggableTaskbarController
+import com.android.launcher3.util.DisplayController
+import com.android.launcher3.util.OnboardingPrefs.TASKBAR_EDU_TOOLTIP_STEP
+import java.io.PrintWriter
+
+/** First EDU step for swiping up to show transient Taskbar. */
+const val TOOLTIP_STEP_SWIPE = 0
+/** Second EDU step for explaining Taskbar functionality when unstashed. */
+const val TOOLTIP_STEP_FEATURES = 1
+/**
+ * EDU is completed.
+ *
+ * This value should match the maximum count for [TASKBAR_EDU_TOOLTIP_STEP].
+ */
+const val TOOLTIP_STEP_NONE = 2
+
+/** Current step in the tooltip EDU flow. */
+@Retention(AnnotationRetention.SOURCE)
+@IntDef(TOOLTIP_STEP_SWIPE, TOOLTIP_STEP_FEATURES, TOOLTIP_STEP_NONE)
+annotation class TaskbarEduTooltipStep
+
+/** Controls stepping through the Taskbar tooltip EDU. */
+class TaskbarEduTooltipController(val activityContext: TaskbarActivityContext) :
+ LoggableTaskbarController {
+
+ private val isTooltipEnabled = !IS_RUNNING_IN_TEST_HARNESS && ENABLE_TASKBAR_EDU_TOOLTIP.get()
+ private val isOpen: Boolean
+ get() = tooltip?.isOpen ?: false
+
+ private lateinit var controllers: TaskbarControllers
+
+ @TaskbarEduTooltipStep
+ var tooltipStep: Int
+ get() {
+ return activityContext.onboardingPrefs?.getCount(TASKBAR_EDU_TOOLTIP_STEP)
+ ?: TOOLTIP_STEP_NONE
+ }
+ private set(step) {
+ activityContext.onboardingPrefs?.setEventCount(step, TASKBAR_EDU_TOOLTIP_STEP)
+ }
+
+ private var tooltip: TaskbarEduTooltip? = null
+
+ fun init(controllers: TaskbarControllers) {
+ this.controllers = controllers
+ }
+
+ /** Shows swipe EDU tooltip if it is the current [tooltipStep]. */
+ fun maybeShowSwipeEdu() {
+ if (
+ !isTooltipEnabled ||
+ !DisplayController.isTransientTaskbar(activityContext) ||
+ tooltipStep > TOOLTIP_STEP_SWIPE
+ ) {
+ return
+ }
+
+ tooltipStep = TOOLTIP_STEP_FEATURES
+ inflateTooltip(R.layout.taskbar_edu_swipe)
+ tooltip?.apply {
+ findViewById<LottieAnimationView>(R.id.swipe_animation).supportLightTheme()
+ show()
+ }
+ }
+
+ /**
+ * Shows feature EDU tooltip if this step has not been seen.
+ *
+ * If [TOOLTIP_STEP_SWIPE] has not been seen at this point, the first step is skipped because a
+ * swipe up is necessary to show this step.
+ */
+ fun maybeShowFeaturesEdu() {
+ if (!isTooltipEnabled || tooltipStep > TOOLTIP_STEP_FEATURES) {
+ return
+ }
+
+ tooltipStep = TOOLTIP_STEP_NONE
+ inflateTooltip(R.layout.taskbar_edu_features)
+ tooltip?.apply {
+ findViewById<LottieAnimationView>(R.id.splitscreen_animation).supportLightTheme()
+ findViewById<LottieAnimationView>(R.id.suggestions_animation).supportLightTheme()
+
+ findViewById<View>(R.id.done_button)?.setOnClickListener { hide() }
+ if (DisplayController.isTransientTaskbar(activityContext)) {
+ (layoutParams as ViewGroup.MarginLayoutParams).bottomMargin +=
+ activityContext.deviceProfile.taskbarSize
+ }
+ show()
+ }
+ }
+
+ /** Closes the current [tooltip]. */
+ fun hide() = tooltip?.close(true)
+
+ /** Initializes [tooltip] with content from [contentResId]. */
+ private fun inflateTooltip(@LayoutRes contentResId: Int) {
+ val overlayContext = controllers.taskbarOverlayController.requestWindow()
+ val tooltip =
+ overlayContext.layoutInflater.inflate(
+ R.layout.taskbar_edu_tooltip,
+ overlayContext.dragLayer,
+ false
+ ) as TaskbarEduTooltip
+
+ controllers.taskbarAutohideSuspendController.updateFlag(
+ FLAG_AUTOHIDE_SUSPEND_EDU_OPEN,
+ true
+ )
+ tooltip.onCloseCallback = {
+ this.tooltip = null
+ controllers.taskbarAutohideSuspendController.updateFlag(
+ FLAG_AUTOHIDE_SUSPEND_EDU_OPEN,
+ false
+ )
+ controllers.taskbarStashController.updateAndAnimateTransientTaskbar(true)
+ }
+
+ overlayContext.layoutInflater.inflate(contentResId, tooltip.content, true)
+ this.tooltip = tooltip
+ }
+
+ override fun dumpLogs(prefix: String?, pw: PrintWriter?) {
+ pw?.println("$(prefix)TaskbarEduController:")
+ pw?.println("$prefix\tisTooltipEnabled=$isTooltipEnabled")
+ pw?.println("$prefix\tisOpen=$isOpen")
+ pw?.println("$prefix\ttooltipStep=$tooltipStep")
+ }
+}
+
+/**
+ * Maps colors in the dark-themed Lottie assets to their light-themed equivalents.
+ *
+ * For instance, `".blue100" to R.color.lottie_blue400` means objects that are material blue100 in
+ * dark theme should be changed to material blue400 in light theme.
+ */
+private val DARK_TO_LIGHT_COLORS =
+ mapOf(
+ ".blue100" to R.color.lottie_blue400,
+ ".blue400" to R.color.lottie_blue600,
+ ".green100" to R.color.lottie_green400,
+ ".green400" to R.color.lottie_green600,
+ ".grey300" to R.color.lottie_grey600,
+ ".grey400" to R.color.lottie_grey700,
+ ".grey800" to R.color.lottie_grey200,
+ ".red400" to R.color.lottie_red600,
+ ".yellow100" to R.color.lottie_yellow400,
+ ".yellow400" to R.color.lottie_yellow600,
+ )
+
+private fun LottieAnimationView.supportLightTheme() {
+ if (Utilities.isDarkTheme(context)) {
+ return
+ }
+
+ addLottieOnCompositionLoadedListener {
+ DARK_TO_LIGHT_COLORS.forEach { (key, color) ->
+ addValueCallback(KeyPath("**", key, "**"), COLOR_FILTER) {
+ PorterDuffColorFilter(context.getColor(color), SRC_ATOP)
+ }
+ }
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduView.java
index 89d67be..5702b6b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduView.java
@@ -15,7 +15,7 @@
*/
package com.android.launcher3.taskbar;
-import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
+import static com.android.launcher3.anim.Interpolators.EMPHASIZED;
import android.animation.PropertyValuesHolder;
import android.content.Context;
@@ -24,21 +24,23 @@
import android.util.AttributeSet;
import android.util.Pair;
import android.view.View;
+import android.view.animation.Interpolator;
import android.widget.Button;
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;
- private static final int DEFAULT_CLOSE_DURATION = 200;
-
private final Rect mInsets = new Rect();
+ // Initialized in init.
+ private TaskbarEduController.TaskbarEduCallbacks mTaskbarEduCallbacks;
+
private Button mStartButton;
private Button mEndButton;
private TaskbarEduPagedView mPagedView;
@@ -56,11 +58,17 @@
if (mPagedView != null) {
mPagedView.setControllerCallbacks(callbacks);
}
+ mTaskbarEduCallbacks = callbacks;
}
@Override
protected void handleClose(boolean animate) {
- handleClose(animate, DEFAULT_CLOSE_DURATION);
+ handleClose(animate, mTaskbarEduCallbacks.getCloseDuration());
+ }
+
+ @Override
+ protected Interpolator getIdleInterpolator() {
+ return EMPHASIZED;
}
@Override
@@ -101,6 +109,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();
@@ -139,8 +163,8 @@
mIsOpen = true;
mOpenCloseAnimator.setValues(
PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
- mOpenCloseAnimator.setInterpolator(AGGRESSIVE_EASE);
- mOpenCloseAnimator.setDuration(DEFAULT_OPEN_DURATION).start();
+ mOpenCloseAnimator.setInterpolator(EMPHASIZED);
+ mOpenCloseAnimator.setDuration(mTaskbarEduCallbacks.getOpenDuration()).start();
}
void snapToPage(int page) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarForceVisibleImmersiveController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarForceVisibleImmersiveController.java
index c99cebb..ffaee45 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarForceVisibleImmersiveController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarForceVisibleImmersiveController.java
@@ -18,21 +18,22 @@
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.anim.AnimatedFloat;
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..9f24565 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
@@ -18,31 +18,30 @@
import android.graphics.Insets
import android.graphics.Region
import android.view.InsetsFrameProvider
-import android.view.InsetsState.ITYPE_BOTTOM_MANDATORY_GESTURES
import android.view.InsetsState
+import android.view.InsetsState.ITYPE_BOTTOM_MANDATORY_GESTURES
+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
-/**
- * Handles the insets that Taskbar provides to underlying apps and the IME.
- */
-class TaskbarInsetsController(val context: TaskbarActivityContext): LoggableTaskbarController {
+/** Handles the insets that Taskbar provides to underlying apps and the IME. */
+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 +54,7 @@
this.controllers = controllers
windowLayoutParams = context.windowLayoutParams
- val wmWrapper: WindowManagerWrapper = getInstance()
- wmWrapper.setProvidesInsetsTypes(
+ setProvidesInsetsTypes(
windowLayoutParams,
intArrayOf(
ITYPE_EXTRA_NAVIGATION_BAR,
@@ -76,78 +74,128 @@
}
fun onTaskbarWindowHeightOrInsetsChanged() {
- var contentHeight = controllers.taskbarStashController.contentHeightToReportToApps
- contentRegion.set(0, windowLayoutParams.height - contentHeight,
- context.deviceProfile.widthPx, windowLayoutParams.height)
- var tappableHeight = controllers.taskbarStashController.tappableHeightToReportToApps
+ val touchableHeight = controllers.taskbarStashController.touchableHeight
+ touchableRegion.set(
+ 0,
+ windowLayoutParams.height - touchableHeight,
+ context.deviceProfile.widthPx,
+ windowLayoutParams.height
+ )
+ 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)
- } else if (provider.type == ITYPE_BOTTOM_TAPPABLE_ELEMENT
- || provider.type == ITYPE_BOTTOM_MANDATORY_GESTURES) {
- provider.insetsSize = Insets.of(0, 0, 0, tappableHeight)
+ if (
+ provider.type == ITYPE_EXTRA_NAVIGATION_BAR ||
+ provider.type == ITYPE_BOTTOM_MANDATORY_GESTURES
+ ) {
+ provider.insetsSize = getInsetsByNavMode(contentHeight)
+ } else if (provider.type == ITYPE_BOTTOM_TAPPABLE_ELEMENT) {
+ provider.insetsSize = getInsetsByNavMode(tappableHeight)
}
}
- val imeInsetsSize = Insets.of(0, 0, 0, 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 insetsSizeOverride = arrayOf(
- InsetsFrameProvider.InsetsSizeOverride(
- TYPE_INPUT_METHOD,
- imeInsetsSize
- ),
- InsetsFrameProvider.InsetsSizeOverride(
- TYPE_VOICE_INTERACTION,
- visInsetsSize
+ val imeInsetsSize = getInsetsByNavMode(taskbarHeightForIme)
+ val insetsSizeOverride =
+ arrayOf(
+ InsetsFrameProvider.InsetsSizeOverride(TYPE_INPUT_METHOD, imeInsetsSize),
)
- )
+ // Use 0 tappableElement insets for the VoiceInteractionWindow when gesture nav is enabled.
+ val visInsetsSizeForGestureNavTappableElement = getInsetsByNavMode(0)
+ val insetsSizeOverrideForGestureNavTappableElement =
+ arrayOf(
+ InsetsFrameProvider.InsetsSizeOverride(TYPE_INPUT_METHOD, imeInsetsSize),
+ InsetsFrameProvider.InsetsSizeOverride(
+ TYPE_VOICE_INTERACTION,
+ visInsetsSizeForGestureNavTappableElement
+ ),
+ )
for (provider in windowLayoutParams.providedInsets) {
- provider.insetsSizeOverrides = insetsSizeOverride
+ if (context.isGestureNav && provider.type == ITYPE_BOTTOM_TAPPABLE_ELEMENT) {
+ provider.insetsSizeOverrides = insetsSizeOverrideForGestureNavTappableElement
+ } else {
+ provider.insetsSizeOverrides = insetsSizeOverride
+ }
+ }
+ }
+
+ /**
+ * @return [Insets] where the [bottomInset] is either used as a bottom inset or
+ * ```
+ * right/left inset if using 3 button nav
+ * ```
+ */
+ 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 InsetsInfo.setTouchableInsets
+ * @see InternalInsetsInfo.setTouchableInsets
*/
- fun updateInsetsTouchability(insetsInfo: InsetsInfo) {
+ fun updateInsetsTouchability(insetsInfo: ViewTreeObserver.InternalInsetsInfo) {
insetsInfo.touchableRegion.setEmpty()
// Always have nav buttons be touchable
controllers.navbarButtonsViewController.addVisibleButtonsRegion(
- context.dragLayer, insetsInfo.touchableRegion
+ context.dragLayer,
+ insetsInfo.touchableRegion
)
var insetsIsTouchableRegion = true
if (context.dragLayer.alpha < AlphaUpdateListener.ALPHA_CUTOFF_THRESHOLD) {
// Let touches pass through us.
- insetsInfo.setTouchableInsets(InsetsInfo.TOUCHABLE_INSETS_REGION)
- } else if (controllers.navbarButtonsViewController.isImeVisible) {
- insetsInfo.setTouchableInsets(InsetsInfo.TOUCHABLE_INSETS_REGION)
+ insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION)
+ } else if (
+ controllers.navbarButtonsViewController.isImeVisible &&
+ controllers.taskbarStashController.isStashed()
+ ) {
+ 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)
- } else if (controllers.taskbarViewController.areIconsVisible()
- || AbstractFloatingView.hasOpenView(context, AbstractFloatingView.TYPE_ALL)
- || context.isNavBarKidsModeActive
+ 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
) {
// 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)
}
@@ -156,8 +204,12 @@
pw.println(prefix + "TaskbarInsetsController:")
pw.println("$prefix\twindowHeight=${windowLayoutParams.height}")
for (provider in windowLayoutParams.providedInsets) {
- pw.print("$prefix\tprovidedInsets: (type=" + InsetsState.typeToString(provider.type)
- + " insetsSize=" + provider.insetsSize)
+ pw.print(
+ "$prefix\tprovidedInsets: (type=" +
+ InsetsState.typeToString(provider.type) +
+ " insetsSize=" +
+ provider.insetsSize
+ )
if (provider.insetsSizeOverrides != null) {
pw.print(" insetsSizeOverrides={")
for ((i, overrideSize) in provider.insetsSizeOverrides.withIndex()) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarKeyguardController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarKeyguardController.java
index 0808fab..edf4648 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarKeyguardController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarKeyguardController.java
@@ -10,12 +10,10 @@
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
import android.app.KeyguardManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.util.ScreenOnTracker;
+import com.android.launcher3.util.ScreenOnTracker.ScreenOnListener;
import com.android.systemui.shared.system.QuickStepContract;
import java.io.PrintWriter;
@@ -30,6 +28,7 @@
SYSUI_STATE_OVERVIEW_DISABLED | SYSUI_STATE_HOME_DISABLED |
SYSUI_STATE_BACK_DISABLED | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
+ private final ScreenOnListener mScreenOnListener;
private final TaskbarActivityContext mContext;
private int mKeyguardSysuiFlags;
private boolean mBouncerShowing;
@@ -37,22 +36,20 @@
private final KeyguardManager mKeyguardManager;
private boolean mIsScreenOff;
- private final BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- mIsScreenOff = true;
- AbstractFloatingView.closeOpenViews(mContext, false, TYPE_ALL);
- }
- };
-
public TaskbarKeyguardController(TaskbarActivityContext context) {
mContext = context;
+ mScreenOnListener = isOn -> {
+ if (!isOn) {
+ mIsScreenOff = true;
+ AbstractFloatingView.closeOpenViews(mContext, false, TYPE_ALL);
+ }
+ };
mKeyguardManager = mContext.getSystemService(KeyguardManager.class);
}
public void init(NavbarButtonsViewController navbarButtonUIController) {
mNavbarButtonsViewController = navbarButtonUIController;
- mContext.registerReceiver(mScreenOffReceiver, new IntentFilter(Intent.ACTION_SCREEN_OFF));
+ ScreenOnTracker.INSTANCE.get(mContext).addListener(mScreenOnListener);
}
public void updateStateForSysuiFlags(int systemUiStateFlags) {
@@ -104,7 +101,7 @@
}
public void onDestroy() {
- mContext.unregisterReceiver(mScreenOffReceiver);
+ ScreenOnTracker.INSTANCE.get(mContext).removeListener(mScreenOnListener);
}
@Override
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index 85e76b2..d9773d4 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -31,14 +31,16 @@
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.AnimatedFloat;
import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.statemanager.StateManager;
-import com.android.launcher3.util.MultiValueAlpha;
-import com.android.quickstep.AnimatedFloat;
+import com.android.launcher3.uioverrides.QuickstepLauncher;
+import com.android.launcher3.uioverrides.states.OverviewState;
+import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
import com.android.quickstep.RecentsAnimationCallbacks;
import com.android.quickstep.RecentsAnimationController;
import com.android.quickstep.views.RecentsView;
@@ -48,7 +50,6 @@
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.StringJoiner;
-import java.util.function.Supplier;
/**
* Track LauncherState, RecentsAnimation, resumed state for task bar in one place here and animate
@@ -63,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;
@@ -84,14 +83,16 @@
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 DeviceProfile.OnDeviceProfileChangeListener mOnDeviceProfileChangeListener =
+ dp -> updateIconAlphaForHome(mIconAlphaForHome.getValue());
+
private final StateManager.StateListener<LauncherState> mStateListener =
new StateManager.StateListener<LauncherState>() {
@@ -118,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;
@@ -129,18 +137,12 @@
mTaskbarBackgroundAlpha = mControllers.taskbarDragLayerController
.getTaskbarBackgroundAlpha();
- MultiValueAlpha taskbarIconAlpha = mControllers.taskbarViewController.getTaskbarIconAlpha();
- mIconAlphaForHome = taskbarIconAlpha.getProperty(ALPHA_INDEX_HOME);
- mIconAlphaForHome.setConsumer(
- alpha -> {
- mLauncher.getHotseat().setIconsAlpha(alpha > 0 ? 0 : 1);
- if (mLauncher.getDeviceProfile().isQsbInline) {
- mLauncher.getHotseat().setQsbAlpha(alpha > 0 ? 0 : 1);
- }
- });
+ mTaskbarCornerRoundness = mControllers.getTaskbarCornerRoundness();
+ mIconAlphaForHome = mControllers.taskbarViewController
+ .getTaskbarIconAlpha().get(ALPHA_INDEX_HOME);
- mIconAlignmentForResumedState.finishAnimation();
- onIconAlignmentRatioChangedForAppAndHomeTransition();
+ mIconAlignment.finishAnimation();
+ onIconAlignmentRatioChanged();
mLauncher.getStateManager().addStateListener(mStateListener);
@@ -150,20 +152,19 @@
applyState(0);
mCanSyncViews = true;
+ mLauncher.addOnDeviceProfileChangeListener(mOnDeviceProfileChangeListener);
}
public void onDestroy() {
mCanSyncViews = false;
- mIconAlignmentForResumedState.finishAnimation();
- mIconAlignmentForGestureState.finishAnimation();
- mIconAlignmentForLauncherState.finishAnimation();
+ mIconAlignment.finishAnimation();
- mIconAlphaForHome.setConsumer(null);
mLauncher.getHotseat().setIconsAlpha(1f);
mLauncher.getStateManager().removeStateListener(mStateListener);
mCanSyncViews = true;
+ mLauncher.removeOnDeviceProfileChangeListener(mOnDeviceProfileChangeListener);
}
public Animator createAnimToLauncher(@NonNull LauncherState toState,
@@ -176,12 +177,19 @@
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);
- animatorSet.play(stashController.applyStateWithoutStart(duration));
+ animatorSet.play(stashController.createApplyStateAnimator(duration));
animatorSet.play(applyState(duration, false));
+ if (mTaskBarRecentsAnimationListener != null) {
+ mTaskBarRecentsAnimationListener.endGestureStateOverride(
+ !mLauncher.isInState(LauncherState.OVERVIEW));
+ }
mTaskBarRecentsAnimationListener = new TaskBarRecentsAnimationListener(callbacks);
callbacks.addListener(mTaskBarRecentsAnimationListener);
((RecentsView) mLauncher.getOverviewPanel()).setTaskLaunchListener(() ->
@@ -190,7 +198,7 @@
}
public boolean isAnimatingToLauncher() {
- return mIsAnimatingToLauncherViaResume || mIsAnimatingToLauncherViaGesture;
+ return mIsAnimatingToLauncher;
}
public void setShouldDelayLauncherStateAnim(boolean shouldDelayLauncherStateAnim) {
@@ -250,11 +258,20 @@
}
private Animator onStateChangeApplied(int changedFlags, long duration, boolean start) {
+ final boolean goingToLauncher = isInLauncher();
+ final float toAlignment = isIconAlignedWithHotseat() ? 1 : 0;
+ boolean handleOpenFloatingViews = false;
+ 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);
@@ -264,96 +281,89 @@
updateStateForFlag(FLAG_RESUMED, false);
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);
- }
-
- resumeAlignAnim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mIsAnimatingToLauncherViaResume = false;
- }
-
- @Override
- public void onAnimationStart(Animator animation) {
- mIsAnimatingToLauncherViaResume = isResumed;
-
- TaskbarStashController stashController =
- mControllers.taskbarStashController;
- stashController.updateStateForFlag(FLAG_IN_APP, !isResumed);
- stashController.applyState(duration);
- }
- });
- animatorSet.play(resumeAlignAnim);
+ if (mLauncherState == LauncherState.NORMAL) {
+ // We're changing state to home, should close open popups e.g. Taskbar AllApps
+ handleOpenFloatingViews = true;
}
}
-
- 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);
+ if (hasAnyFlag(changedFlags, FLAGS_LAUNCHER)) {
+ animatorSet.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mIsAnimatingToLauncher = false;
}
- 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();
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mIsAnimatingToLauncher = goingToLauncher;
+
+ TaskbarStashController stashController =
+ mControllers.taskbarStashController;
+ if (DEBUG) {
+ Log.d(TAG, "onAnimationStart - FLAG_IN_APP: " + !goingToLauncher);
}
- });
- animatorSet.play(gestureAlignAnim);
- }
+ stashController.updateStateForFlag(FLAG_IN_APP, !goingToLauncher);
+ stashController.applyState(duration);
+ }
+ });
+
+ // Handle closing open popups when going home/overview
+ handleOpenFloatingViews = true;
+ }
+ if (handleOpenFloatingViews && goingToLauncher) {
+ AbstractFloatingView.closeAllOpenViews(mControllers.taskbarActivityContext);
}
- 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);
+ 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(goingToLauncher ? 0 : 1)
+ 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();
}
@@ -361,21 +371,39 @@
}
/** 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;
+ }
+ }
+
+ /**
+ * Returns if the current Launcher state has hotseat on top of other elemnets.
+ */
+ public boolean isInHotseatOnTopStates() {
+ return mLauncherState != LauncherState.ALL_APPS;
}
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);
+ Animator stashAnimator = stashController.createApplyStateAnimator(duration);
if (stashAnimator != null) {
stashAnimator.addListener(new AnimatorListenerAdapter() {
@Override
@@ -389,61 +417,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) {
@@ -453,26 +449,18 @@
}
}
- private void updateIconAlignment(float alignment, Float endAlignment) {
- mControllers.taskbarViewController.setLauncherIconAlignment(
- alignment, endAlignment, mLauncher.getDeviceProfile());
-
- // 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);
+ private void updateIconAlphaForHome(float alpha) {
+ mIconAlphaForHome.setValue(alpha);
+ boolean hotseatVisible = alpha == 0
+ || (!mControllers.uiController.isHotseatIconOnTopWhenAligned()
+ && mIconAlignment.value > 0);
+ /*
+ * Hide Launcher Hotseat icons when Taskbar icons have opacity. Both icon sets
+ * should not be visible at the same time.
+ */
+ mLauncher.getHotseat().setIconsAlpha(hotseatVisible ? 1 : 0);
+ mLauncher.getHotseat().setQsbAlpha(
+ mLauncher.getDeviceProfile().isQsbInline && !hotseatVisible ? 0 : 1);
}
private final class TaskBarRecentsAnimationListener implements
@@ -504,11 +492,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();
}
@@ -516,29 +504,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(
@@ -547,13 +530,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 06262c0..a58906f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -30,19 +30,22 @@
import android.hardware.display.DisplayManager;
import android.net.Uri;
import android.os.Handler;
+import android.os.SystemProperties;
import android.provider.Settings;
import android.view.Display;
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;
@@ -58,6 +61,9 @@
*/
public class TaskbarManager {
+ public static final boolean FLAG_HIDE_NAVBAR_WINDOW =
+ SystemProperties.getBoolean("persist.wm.debug.hide_navbar_window", false);
+
private static final Uri USER_SETUP_COMPLETE_URI = Settings.Secure.getUriFor(
Settings.Secure.USER_SETUP_COMPLETE);
@@ -78,6 +84,7 @@
// It's destruction/creation will be managed by the activity.
private final ScopedUnfoldTransitionProgressProvider mUnfoldProgressProvider =
new NonDestroyableScopedUnfoldTransitionProgressProvider();
+ private NavigationMode mNavMode;
private TaskbarActivityContext mTaskbarActivityContext;
private StatefulActivity mActivity;
@@ -128,9 +135,11 @@
| ActivityInfo.CONFIG_SCREEN_SIZE;
boolean requiresRecreate = (configDiff & configsRequiringRecreate) != 0;
if ((configDiff & ActivityInfo.CONFIG_SCREEN_SIZE) != 0
- && mTaskbarActivityContext != null && dp != null) {
+ && mTaskbarActivityContext != null && dp != null
+ && !isPhoneMode(dp)) {
// Additional check since this callback gets fired multiple times w/o
// screen size changing, or when simply rotating the device.
+ // In the case of phone device rotation, we do want to call recreateTaskbar()
DeviceProfile oldDp = mTaskbarActivityContext.getDeviceProfile();
boolean isOrientationChange =
(configDiff & ActivityInfo.CONFIG_ORIENTATION) != 0;
@@ -147,10 +156,14 @@
} else {
// Config change might be handled without re-creating the taskbar
if (mTaskbarActivityContext != null) {
- if (dp != null && dp.isTaskbarPresent) {
- mTaskbarActivityContext.updateDeviceProfile(dp);
+ if (dp != null && !isTaskbarPresent(dp)) {
+ destroyExistingTaskbar();
+ } else {
+ if (dp != null && isTaskbarPresent(dp)) {
+ mTaskbarActivityContext.updateDeviceProfile(dp, mNavMode);
+ }
+ mTaskbarActivityContext.onConfigurationChanged(configDiff);
}
- mTaskbarActivityContext.onConfigurationChanged(configDiff);
}
}
mOldConfig = newConfig;
@@ -159,12 +172,15 @@
@Override
public void onLowMemory() { }
};
- mShutdownReceiver = new SimpleBroadcastReceiver(i -> destroyExistingTaskbar());
+ mShutdownReceiver = new SimpleBroadcastReceiver(i ->
+ destroyExistingTaskbar());
mDispInfoChangeListener = (context, info, flags) -> {
if ((flags & CHANGE_FLAGS) != 0) {
+ mNavMode = info.navigationMode;
recreateTaskbar();
}
};
+ mNavMode = mDisplayController.getInfo().navigationMode;
mDisplayController.addChangeListener(mDispInfoChangeListener);
SettingsCache.INSTANCE.get(mContext).register(USER_SETUP_COMPLETE_URI,
mUserSetupCompleteListener);
@@ -179,7 +195,9 @@
private void destroyExistingTaskbar() {
if (mTaskbarActivityContext != null) {
mTaskbarActivityContext.onDestroy();
- mTaskbarActivityContext = null;
+ if (!FLAG_HIDE_NAVBAR_WINDOW) {
+ mTaskbarActivityContext = null;
+ }
}
}
@@ -210,8 +228,9 @@
return;
}
mActivity = activity;
- mUnfoldProgressProvider.setSourceProvider(getUnfoldTransitionProgressProviderForActivity(
- activity));
+ UnfoldTransitionProgressProvider unfoldTransitionProgressProvider =
+ getUnfoldTransitionProgressProviderForActivity(activity);
+ mUnfoldProgressProvider.setSourceProvider(unfoldTransitionProgressProvider);
if (mTaskbarActivityContext != null) {
mTaskbarActivityContext.setUIController(
@@ -225,8 +244,8 @@
*/
private UnfoldTransitionProgressProvider getUnfoldTransitionProgressProviderForActivity(
StatefulActivity activity) {
- if (activity instanceof BaseQuickstepLauncher) {
- return ((BaseQuickstepLauncher) activity).getUnfoldTransitionProgressProvider();
+ if (activity instanceof QuickstepLauncher) {
+ return ((QuickstepLauncher) activity).getUnfoldTransitionProgressProvider();
}
return null;
}
@@ -235,11 +254,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);
@@ -260,24 +279,33 @@
}
}
- private void recreateTaskbar() {
+ /**
+ * This method is called multiple times (ex. initial init, then when user unlocks) in which case
+ * we fully want to destroy an existing taskbar and create a new one.
+ * In other case (folding/unfolding) we don't need to remove and add window.
+ */
+ @VisibleForTesting
+ public void recreateTaskbar() {
+ DeviceProfile dp = mUserUnlocked ?
+ LauncherAppState.getIDP(mContext).getDeviceProfile(mContext) : null;
+
destroyExistingTaskbar();
- DeviceProfile dp =
- mUserUnlocked ? LauncherAppState.getIDP(mContext).getDeviceProfile(mContext) : null;
-
- boolean isTaskBarEnabled = dp != null && dp.isTaskbarPresent;
-
+ boolean isTaskBarEnabled = dp != null && isTaskbarPresent(dp);
if (!isTaskBarEnabled) {
SystemUiProxy.INSTANCE.get(mContext)
.notifyTaskbarStatus(/* visible */ false, /* stashed */ false);
return;
}
- mTaskbarActivityContext = new TaskbarActivityContext(mContext, dp, mNavButtonController,
- mUnfoldProgressProvider);
-
+ if (mTaskbarActivityContext == null) {
+ mTaskbarActivityContext = new TaskbarActivityContext(mContext, dp, mNavButtonController,
+ mUnfoldProgressProvider);
+ } else {
+ mTaskbarActivityContext.updateDeviceProfile(dp, mNavMode);
+ }
mTaskbarActivityContext.init(mSharedState);
+
if (mActivity != null) {
mTaskbarActivityContext.setUIController(
createTaskbarUIControllerForActivity(mActivity));
@@ -291,6 +319,12 @@
}
}
+ public void onLongPressHomeEnabled(boolean assistantLongPressEnabled) {
+ if (mNavButtonController != null) {
+ mNavButtonController.setAssistantLongPressEnabled(assistantLongPressEnabled);
+ }
+ }
+
/**
* Sets the flag indicating setup UI is visible
*/
@@ -301,6 +335,26 @@
}
}
+ /**
+ * @return {@code true} if provided device profile isn't a large screen profile
+ * and we are using a single window for taskbar and navbar.
+ */
+ public static boolean isPhoneMode(DeviceProfile deviceProfile) {
+ return TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW && deviceProfile.isPhone;
+ }
+
+ /**
+ * @return {@code true} if {@link #isPhoneMode(DeviceProfile)} is true and we're using
+ * 3 button-nav
+ */
+ public static boolean isPhoneButtonNavMode(TaskbarActivityContext context) {
+ return isPhoneMode(context.getDeviceProfile()) && context.isThreeButtonNav();
+ }
+
+ private boolean isTaskbarPresent(DeviceProfile deviceProfile) {
+ return FLAG_HIDE_NAVBAR_WINDOW || deviceProfile.isTaskbarPresent;
+ }
+
public void onRotationProposal(int rotation, boolean isValid) {
if (mTaskbarActivityContext != null) {
mTaskbarActivityContext.onRotationProposal(rotation, isValid);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
index a395548..5bb958a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
@@ -67,6 +67,7 @@
private long mLastScreenPinLongPress;
private boolean mScreenPinned;
+ private boolean mAssistantLongPressEnabled;
@Override
public void dumpLogs(String prefix, PrintWriter pw) {
@@ -251,6 +252,10 @@
mStatsLogManager = null;
}
+ public void setAssistantLongPressEnabled(boolean assistantLongPressEnabled) {
+ mAssistantLongPressEnabled = assistantLongPressEnabled;
+ }
+
private void logEvent(StatsLogManager.LauncherEvent event) {
if (mStatsLogManager == null) {
Log.w(TAG, "No stats log manager to log taskbar button event");
@@ -289,7 +294,7 @@
}
private void startAssistant() {
- if (mScreenPinned) {
+ if (mScreenPinned || !mAssistantLongPressEnabled) {
return;
}
Bundle args = new Bundle();
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..88767dd 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
@@ -22,7 +22,7 @@
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
-import com.android.quickstep.AnimatedFloat;
+import com.android.launcher3.anim.AnimatedFloat;
import com.android.quickstep.SystemUiProxy;
import java.io.PrintWriter;
@@ -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 8533145..ef79b8e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -17,38 +17,50 @@
import static android.view.HapticFeedbackConstants.LONG_PRESS;
-import static com.android.launcher3.LauncherState.ALL_APPS;
+import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
+import static com.android.launcher3.anim.Interpolators.INSTANT;
+import static com.android.launcher3.config.FeatureFlags.FORCE_PERSISTENT_TASKBAR;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_LONGPRESS_HIDE;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_LONGPRESS_SHOW;
import static com.android.launcher3.taskbar.Utilities.appendFlag;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
+import android.animation.ValueAnimator;
import android.annotation.Nullable;
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.LauncherPrefs;
+import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.testing.shared.TestProtocol;
-import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
-import com.android.quickstep.AnimatedFloat;
+import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
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;
@@ -58,25 +70,26 @@
*/
public class TaskbarStashController implements TaskbarControllers.LoggableTaskbarController {
- private static final String TAG = "TaskbarStashController";
-
public static final int FLAG_IN_APP = 1 << 0;
public static final int FLAG_STASHED_IN_APP_MANUAL = 1 << 1; // long press, persisted
- public static final int FLAG_STASHED_IN_APP_PINNED = 1 << 2; // app pinning
+ public static final int FLAG_STASHED_IN_SYSUI_STATE = 1 << 2; // app pinning, keyguard, etc.
public static final int FLAG_STASHED_IN_APP_EMPTY = 1 << 3; // no hotseat icons
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;
// 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_IN_SYSUI_STATE | FLAG_STASHED_IN_APP_EMPTY | FLAG_STASHED_IN_APP_SETUP
+ | FLAG_STASHED_IN_APP_IME | FLAG_STASHED_IN_TASKBAR_ALL_APPS
+ | FLAG_STASHED_SMALL_SCREEN | FLAG_STASHED_IN_APP_AUTO;
private static final int FLAGS_STASHED_IN_APP_IGNORING_IME =
FLAGS_STASHED_IN_APP & ~FLAG_STASHED_IN_APP_IME;
@@ -86,13 +99,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.
@@ -130,6 +143,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;
@@ -142,11 +158,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). */
@@ -158,7 +174,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(
@@ -166,15 +185,32 @@
boolean inApp = hasAnyFlag(flags, FLAGS_IN_APP);
boolean stashedInApp = hasAnyFlag(flags, FLAGS_STASHED_IN_APP);
boolean stashedLauncherState = hasAnyFlag(flags, FLAG_IN_STASHED_LAUNCHER_STATE);
- return (inApp && stashedInApp) || (!inApp && stashedLauncherState);
+ boolean stashedInTaskbarAllApps =
+ hasAnyFlag(flags, FLAG_STASHED_IN_TASKBAR_ALL_APPS);
+ boolean stashedForSmallScreen = hasAnyFlag(flags, FLAG_STASHED_SMALL_SCREEN);
+ return (inApp && stashedInApp) || (!inApp && stashedLauncherState)
+ || stashedInTaskbarAllApps || stashedForSmallScreen;
});
public TaskbarStashController(TaskbarActivityContext activity) {
mActivity = activity;
- mPrefs = Utilities.getPrefs(mActivity);
+ mPrefs = LauncherPrefs.getPrefs(mActivity);
mSystemUiProxy = SystemUiProxy.INSTANCE.get(activity);
- mUnstashedHeight = mActivity.getDeviceProfile().taskbarSize;
- mStashedHeight = mActivity.getDeviceProfile().stashedTaskbarSize;
+ if (isPhoneMode()) {
+ // DeviceProfile's taskbar vars aren't initialized w/ the flag off
+ Resources resources = mActivity.getResources();
+ 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;
+ }
+
}
public void init(TaskbarControllers controllers, boolean setupUIVisible) {
@@ -185,24 +221,36 @@
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()
+ boolean isTransientTaskbar = DisplayController.isTransientTaskbar(mActivity);
+ // We use supportsVisualStashing() here instead of supportsManualStashing() because we want
+ // it to work properly for tests that recreate taskbar. This check is here just to ensure
+ // that taskbar unstashes when going to 3 button mode (supportsVisualStashing() false).
+ boolean isManuallyStashedInApp = supportsVisualStashing()
+ && !isTransientTaskbar
+ && !FORCE_PERSISTENT_TASKBAR.get()
&& mPrefs.getBoolean(SHARED_PREFS_STASHED_KEY, DEFAULT_STASHED_PREF);
boolean isInSetup = !mActivity.isUserSetupComplete() || setupUIVisible;
updateStateForFlag(FLAG_STASHED_IN_APP_MANUAL, isManuallyStashedInApp);
+ updateStateForFlag(FLAG_STASHED_IN_APP_AUTO, isTransientTaskbar);
updateStateForFlag(FLAG_STASHED_IN_APP_SETUP, isInSetup);
updateStateForFlag(FLAG_IN_SETUP, isInSetup);
- applyState();
+ updateStateForFlag(FLAG_STASHED_SMALL_SCREEN, isPhoneMode()
+ && !mActivity.isThreeButtonNav());
+ // 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());
}
@@ -212,23 +260,38 @@
* state.
*/
public boolean supportsVisualStashing() {
- return mControllers.uiController.supportsVisualStashing();
+ return !mActivity.isThreeButtonNav() && mControllers.uiController.supportsVisualStashing();
}
/**
* Returns whether the user can manually stash the taskbar based on the current device state.
*/
protected boolean supportsManualStashing() {
+ if (FORCE_PERSISTENT_TASKBAR.get()) {
+ return false;
+ }
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;
}
/**
@@ -266,7 +329,14 @@
* Returns whether the taskbar should be stashed in the current LauncherState.
*/
public boolean isInStashedLauncherState() {
- return hasAnyFlag(FLAG_IN_STASHED_LAUNCHER_STATE) && supportsVisualStashing();
+ return (hasAnyFlag(FLAG_IN_STASHED_LAUNCHER_STATE) && supportsVisualStashing());
+ }
+
+ /**
+ * @return {@code true} if we're not on a large screen AND using gesture nav
+ */
+ private boolean isPhoneMode() {
+ return TaskbarManager.isPhoneMode(mActivity.getDeviceProfile());
}
private boolean hasAnyFlag(int flagMask) {
@@ -290,18 +360,35 @@
}
/**
+ * Returns the height that taskbar will be touchable.
+ */
+ public int getTouchableHeight() {
+ int bottomMargin = 0;
+ if (DisplayController.isTransientTaskbar(mActivity)) {
+ bottomMargin = mActivity.getResources().getDimensionPixelSize(
+ R.dimen.transient_taskbar_margin);
+ }
+ return mIsStashed ? mStashedHeight : (mUnstashedHeight + bottomMargin);
+ }
+
+ /**
* Returns the height that taskbar will inset when inside apps.
- * @see WindowInsets.Type#navigationBars()
- * @see WindowInsets.Type#systemBars()
+ * @see android.view.WindowInsets.Type#navigationBars()
+ * @see android.view.WindowInsets.Type#systemBars()
*/
public int getContentHeightToReportToApps() {
+ if ((isPhoneMode() && !mActivity.isThreeButtonNav())
+ || DisplayController.isTransientTaskbar(mActivity)) {
+ return getStashedHeight();
+ }
+
if (supportsVisualStashing() && hasAnyFlag(FLAGS_REPORT_STASHED_INSETS_TO_APP)) {
DeviceProfile dp = mActivity.getDeviceProfile();
- if (hasAnyFlag(FLAG_STASHED_IN_APP_SETUP) && dp.isTaskbarPresent && !dp.isLandscape) {
+ if (hasAnyFlag(FLAG_STASHED_IN_APP_SETUP) && dp.isTaskbarPresent) {
// We always show the back button in SUW but in portrait the SUW layout may not
- // be wide enough to support overlapping the nav bar with its content. For now,
- // just inset by the bar height.
- return mUnstashedHeight;
+ // be wide enough to support overlapping the nav bar with its content.
+ // We're sending different res values in portrait vs landscape
+ return mActivity.getResources().getDimensionPixelSize(R.dimen.taskbar_suw_insets);
}
boolean isAnimating = mAnimator != null && mAnimator.isStarted();
if (!mControllers.stashedHandleViewController.isStashedHandleVisible()
@@ -314,12 +401,13 @@
}
return mStashedHeight;
}
+
return mUnstashedHeight;
}
/**
* Returns the height that taskbar will inset when inside apps.
- * @see WindowInsets.Type#tappableElement()
+ * @see android.view.WindowInsets.Type#tappableElement()
*/
public int getTappableHeightToReportToApps() {
int contentHeight = getContentHeightToReportToApps();
@@ -331,6 +419,25 @@
}
/**
+ * Stash or unstashes the transient taskbar.
+ */
+ public void updateAndAnimateTransientTaskbar(boolean stash) {
+ if (!DisplayController.isTransientTaskbar(mActivity)) {
+ return;
+ }
+
+ if (stash && mControllers.taskbarAutohideSuspendController.isSuspended()) {
+ // Avoid stashing if autohide is currently suspended.
+ 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.
*/
@@ -391,8 +498,8 @@
createAnimToIsStashed(
/* isStashed= */ false,
placeholderDuration,
- /* startDelay= */ 0,
- /* animateBg= */ false);
+ /* animateBg= */ false,
+ /* changedFlags=*/ 0);
animation.play(mAnimator);
}
@@ -400,30 +507,30 @@
* Create a stash animation and save to {@link #mAnimator}.
* @param isStashed whether it's a stash animation or an unstash animation
* @param duration duration of the animation
- * @param startDelay how many milliseconds to delay the animation after starting it.
* @param animateBg whether the taskbar's background should be animated
*/
- private void createAnimToIsStashed(
- boolean isStashed, long duration, long startDelay, boolean animateBg) {
+ private void createAnimToIsStashed(boolean isStashed, long duration, boolean animateBg,
+ int changedFlags) {
if (mAnimator != null) {
mAnimator.cancel();
}
mAnimator = new AnimatorSet();
addJankMonitorListener(mAnimator, /* appearing= */ !mIsStashed);
+ final float stashTranslation = isPhoneMode() ? 0 :
+ (mUnstashedHeight - mStashedHeight) / 2f;
if (!supportsVisualStashing()) {
// Just hide/show the icons and background instead of stashing into a handle.
mAnimator.play(mIconAlphaForStash.animateToValue(isStashed ? 0 : 1)
.setDuration(duration));
+ mAnimator.playTogether(mTaskbarBackgroundOffset.animateToValue(isStashed ? 1 : 0)
+ .setDuration(duration));
+ mAnimator.playTogether(mIconTranslationYForStash.animateToValue(isStashed ?
+ stashTranslation : 0)
+ .setDuration(duration));
mAnimator.play(mTaskbarImeBgAlpha.animateToValue(
hasAnyFlag(FLAG_STASHED_IN_APP_IME) ? 0 : 1).setDuration(duration));
- mAnimator.setStartDelay(startDelay);
- mAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mAnimator = null;
- }
- });
+ mAnimator.addListener(AnimatorListeners.forEndCallback(() -> mAnimator = null));
return;
}
@@ -435,10 +542,13 @@
final float firstHalfDurationScale;
final float secondHalfDurationScale;
+ // If Hotseat is not the top element during animation to/from Launcher, fade in/out a
+ // already stashed Taskbar.
+ boolean skipStashAnimation = !mControllers.uiController.isHotseatIconOnTopWhenAligned()
+ && hasAnyFlag(changedFlags, FLAG_IN_APP);
if (isStashed) {
firstHalfDurationScale = 0.75f;
secondHalfDurationScale = 0.5f;
- final float stashTranslation = (mUnstashedHeight - mStashedHeight) / 2f;
fullLengthAnimatorSet.play(mIconTranslationYForStash.animateToValue(stashTranslation));
if (animateBg) {
@@ -450,11 +560,17 @@
firstHalfAnimatorSet.playTogether(
mIconAlphaForStash.animateToValue(0),
- mIconScaleForStash.animateToValue(STASHED_TASKBAR_SCALE)
+ mIconScaleForStash.animateToValue(isPhoneMode() ?
+ 0 : STASHED_TASKBAR_SCALE)
);
secondHalfAnimatorSet.playTogether(
mTaskbarStashedHandleAlpha.animateToValue(1)
);
+
+ if (skipStashAnimation) {
+ fullLengthAnimatorSet.setInterpolator(INSTANT);
+ firstHalfAnimatorSet.setInterpolator(INSTANT);
+ }
} else {
firstHalfDurationScale = 0.5f;
secondHalfDurationScale = 0.75f;
@@ -475,6 +591,11 @@
secondHalfAnimatorSet.playTogether(
mIconAlphaForStash.animateToValue(1)
);
+
+ if (skipStashAnimation) {
+ fullLengthAnimatorSet.setInterpolator(FINAL_FRAME);
+ secondHalfAnimatorSet.setInterpolator(FINAL_FRAME);
+ }
}
fullLengthAnimatorSet.play(mControllers.stashedHandleViewController
@@ -490,29 +611,28 @@
mAnimator.playTogether(fullLengthAnimatorSet, firstHalfAnimatorSet,
secondHalfAnimatorSet);
- mAnimator.setStartDelay(startDelay);
mAnimator.addListener(new AnimatorListenerAdapter() {
@Override
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() {
@@ -576,19 +696,17 @@
}
public void applyState(long duration) {
- mStatePropertyHolder.setState(mState, duration, true);
+ createApplyStateAnimator(duration).start();
}
public void applyState(long duration, long startDelay) {
- mStatePropertyHolder.setState(mState, duration, startDelay, true);
+ Animator animator = createApplyStateAnimator(duration);
+ animator.setStartDelay(startDelay);
+ animator.start();
}
- public Animator applyStateWithoutStart() {
- return applyStateWithoutStart(TASKBAR_STASH_DURATION);
- }
-
- public Animator applyStateWithoutStart(long duration) {
- return mStatePropertyHolder.setState(mState, duration, false);
+ public Animator createApplyStateAnimator(long duration) {
+ return mStatePropertyHolder.createSetStateAnimator(mState, duration);
}
/**
@@ -603,34 +721,26 @@
// Only update the following flags when system gesture is not in progress.
boolean shouldStashForIme = shouldStashForIme();
- maybeResetStashedInAppAllApps(
- hasAnyFlag(FLAG_STASHED_IN_APP_IME) == shouldStashForIme);
+ updateStateForFlag(FLAG_STASHED_IN_TASKBAR_ALL_APPS, false);
if (hasAnyFlag(FLAG_STASHED_IN_APP_IME) != shouldStashForIme) {
updateStateForFlag(FLAG_STASHED_IN_APP_IME, shouldStashForIme);
applyState(TASKBAR_STASH_DURATION_FOR_IME, getTaskbarStashStartDelayForIme());
+ } else {
+ applyState(mControllers.taskbarOverlayController.getCloseDuration());
}
}
/**
- * Reset stashed in all apps only if no system gesture is in progress.
+ * Resets the flag if no system gesture is in progress.
* <p>
* Otherwise, the reset should be deferred until after the gesture is finished.
*
* @see #setSystemGestureInProgress
*/
- public void maybeResetStashedInAppAllApps() {
- maybeResetStashedInAppAllApps(true);
- }
-
- private void maybeResetStashedInAppAllApps(boolean applyState) {
- if (mIsSystemGestureInProgress) {
- return;
- }
-
- updateStateForFlag(FLAG_STASHED_IN_APP_ALL_APPS, false);
- if (applyState) {
- applyState(ALL_APPS.getTransitionDuration(
- mControllers.taskbarActivityContext, false /* isToState */));
+ public void resetFlagIfNoGestureInProgress(int flag) {
+ if (!mIsSystemGestureInProgress) {
+ updateStateForFlag(flag, false);
+ applyState(mControllers.taskbarOverlayController.getCloseDuration());
}
}
@@ -653,12 +763,18 @@
long animDuration = TASKBAR_STASH_DURATION;
long startDelay = 0;
- updateStateForFlag(FLAG_STASHED_IN_APP_PINNED,
- hasAnyFlag(systemUiStateFlags, SYSUI_STATE_SCREEN_PINNING));
+ updateStateForFlag(FLAG_STASHED_IN_SYSUI_STATE, hasAnyFlag(systemUiStateFlags,
+ SYSUI_STATE_SCREEN_PINNING
+ | SYSUI_STATE_BOUNCER_SHOWING
+ | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING
+ | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED
+ | SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED
+ | SYSUI_STATE_QUICK_SETTINGS_EXPANDED));
// Only update FLAG_STASHED_IN_APP_IME when system gesture is not in progress.
mIsImeShowing = hasAnyFlag(systemUiStateFlags, SYSUI_STATE_IME_SHOWING);
mIsImeSwitcherShowing = hasAnyFlag(systemUiStateFlags, SYSUI_STATE_IME_SWITCHER_SHOWING);
+
if (!mIsSystemGestureInProgress) {
updateStateForFlag(FLAG_STASHED_IN_APP_IME, shouldStashForIme());
animDuration = TASKBAR_STASH_DURATION_FOR_IME;
@@ -668,8 +784,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);
}
/**
@@ -724,6 +852,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:");
@@ -739,23 +915,25 @@
}
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_SYSUI_STATE, "FLAG_STASHED_IN_SYSUI_STATE");
+ 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 {
private final IntPredicate mStashCondition;
private boolean mIsStashed;
+ private boolean mIsHotseatIconOnTopWhenAligned;
private int mPrevFlags;
StatePropertyHolder(IntPredicate stashCondition) {
@@ -763,47 +941,42 @@
}
/**
- * @see #setState(int, long, long, boolean) with a default startDelay = 0.
- */
- public Animator setState(int flags, long duration, boolean start) {
- return setState(flags, duration, 0 /* startDelay */, start);
- }
-
- /**
- * Applies the latest state, potentially calling onStateChangeApplied() and creating a new
- * animation (stored in mAnimator) which is started if {@param start} is true.
+ * Creates an animator (stored in mAnimator) which applies the latest state, potentially
+ * creating a new animation (stored in mAnimator).
* @param flags The latest flags to apply (see the top of this file).
* @param duration The length of the animation.
- * @param startDelay How long to delay the animation after calling start().
- * @param start Whether to start mAnimator immediately.
- * @return mAnimator if mIsStashed changed, else null.
+ * @return mAnimator if mIsStashed changed or an empty animator.
*/
- public Animator setState(int flags, long duration, long startDelay, boolean start) {
+ @NonNull
+ public Animator createSetStateAnimator(int flags, long duration) {
int changedFlags = mPrevFlags ^ flags;
if (mPrevFlags != flags) {
onStateChangeApplied(changedFlags);
mPrevFlags = flags;
}
boolean isStashed = mStashCondition.test(flags);
- if (mIsStashed != isStashed) {
+ boolean isHotseatIconOnTopWhenAligned =
+ mControllers.uiController.isHotseatIconOnTopWhenAligned();
+ // If an animation has started and mIsHotseatIconOnTopWhenAligned is changed, we need
+ // to restart the animation with new parameters.
+ if (mIsStashed != isStashed
+ || (mIsHotseatIconOnTopWhenAligned != isHotseatIconOnTopWhenAligned
+ && mAnimator != null && mAnimator.isStarted())) {
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.TASKBAR_IN_APP_STATE, String.format(
- "setState: mIsStashed=%b, isStashed=%b, duration=%d, start=:%b",
+ "setState: mIsStashed=%b, isStashed=%b, duration=%d",
mIsStashed,
isStashed,
- duration,
- start));
+ duration));
}
mIsStashed = isStashed;
+ mIsHotseatIconOnTopWhenAligned = isHotseatIconOnTopWhenAligned;
// This sets mAnimator.
- createAnimToIsStashed(mIsStashed, duration, startDelay, /* animateBg= */ true);
- if (start) {
- mAnimator.start();
- }
+ createAnimToIsStashed(mIsStashed, duration, /* animateBg= */ true, changedFlags);
return mAnimator;
}
- return null;
+ return ValueAnimator.ofFloat(0, 1).setDuration(duration);
}
}
}
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..586115d
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java
@@ -0,0 +1,201 @@
+/*
+ * 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.AnimatedFloat.VALUE;
+import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
+
+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.AnimatedFloat;
+import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.anim.SpringAnimationBuilder;
+import com.android.launcher3.util.DisplayController;
+
+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) {
+ return;
+ }
+ reset();
+ if (mControllers.taskbarStashController.isInAppAndNotStashed()) {
+ mControllers.taskbarEduTooltipController.maybeShowFeaturesEdu();
+ }
+ }));
+ 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..ebb37a8 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -15,15 +15,23 @@
*/
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.launcher3.util.DisplayController;
+import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.TaskView;
+import com.android.systemui.shared.recents.model.Task;
import java.io.PrintWriter;
-import java.util.stream.Stream;
+import java.util.function.Consumer;
/**
* Base class for providing different taskbar UI
@@ -49,17 +57,17 @@
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() { }
- public Stream<ItemInfoWithIcon> getAppIconsForEdu() {
- return Stream.empty();
- }
-
/** Called when an icon is launched. */
public void onTaskbarIconLaunched(ItemInfo item) { }
@@ -76,10 +84,13 @@
}
/**
- * Manually closes the all apps window.
+ * Manually closes the overlay window.
*/
- public void hideAllApps() {
- mControllers.taskbarAllAppsController.hide();
+ public void hideOverlayWindow() {
+ if (!DisplayController.isTransientTaskbar(mControllers.taskbarActivityContext)
+ || mControllers.taskbarAllAppsController.isOpen()) {
+ mControllers.taskbarOverlayController.hideWindow();
+ }
}
/**
@@ -93,6 +104,50 @@
}
}
+ /**
+ * Returns {@code true} iff taskbar is stashed.
+ */
+ public boolean isTaskbarStashed() {
+ return mControllers.taskbarStashController.isStashed();
+ }
+
+ /**
+ * Returns {@code true} iff taskbar All Apps is open.
+ */
+ public boolean isTaskbarAllAppsOpen() {
+ return mControllers.taskbarAllAppsController.isOpen();
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Returns true if hotseat icons are on top of view hierarchy when aligned in the current state.
+ */
+ public boolean isHotseatIconOnTopWhenAligned() {
+ return true;
+ }
+
@CallSuper
protected void dumpLogs(String prefix, PrintWriter pw) {
pw.println(String.format(
@@ -100,4 +155,45 @@
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();
+ recents.findLastActiveTaskAndRunCallback(
+ info.getTargetComponent(),
+ (Consumer<Task>) foundTask -> {
+ if (foundTask != null) {
+ TaskView foundTaskView = recents.getTaskViewByTaskId(foundTask.key.id);
+ // There is already a running app of this type, use that as second app.
+ recents.confirmSplitSelect(
+ foundTaskView,
+ foundTaskView.getTask(),
+ foundTaskView.getIconView().getDrawable(),
+ foundTaskView.getThumbnail(),
+ foundTaskView.getThumbnail().getThumbnail(),
+ null /* intent */);
+ } else {
+ // No running app of that type, create a new instance as second app.
+ recents.confirmSplitSelect(
+ null /* containerTaskView */,
+ null /* task */,
+ new BitmapDrawable(info.bitmap.icon),
+ startingView,
+ null /* thumbnail */,
+ 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 ea15acb..2fcd64b 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,15 +53,17 @@
* 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;
+ private final boolean mIsRtl;
private final TaskbarActivityContext mActivityContext;
@@ -77,7 +79,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,13 +100,16 @@
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);
+ mIsRtl = Utilities.isRtl(resources);
int actualMargin = resources.getDimensionPixelSize(R.dimen.taskbar_icon_spacing);
int actualIconSize = mActivityContext.getDeviceProfile().iconSizePx;
+ mIconTouchSize = Math.max(actualIconSize,
+ resources.getDimensionPixelSize(R.dimen.taskbar_icon_min_touch_size));
+
// We layout the icons to be of mIconTouchSize in width and height
mItemMarginLeftRight = actualMargin - (mIconTouchSize - actualIconSize) / 2;
mItemPadding = (mIconTouchSize - actualIconSize) / 2;
@@ -115,14 +120,18 @@
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);
+ mAllAppsButton.setScaleX(mIsRtl ? -1 : 1);
+ 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 +183,7 @@
}
removeView(mQsb);
+
for (int i = 0; i < hotseatItemInfos.length; i++) {
ItemInfo hotseatItemInfo = hotseatItemInfos[i];
if (hotseatItemInfo == null) {
@@ -246,11 +256,11 @@
}
if (mAllAppsButton != null) {
- int index = Utilities.isRtl(getResources()) ? 0 : getChildCount();
+ int index = mIsRtl ? getChildCount() : 0;
addView(mAllAppsButton, index);
}
if (mActivityContext.getDeviceProfile().isQsbInline) {
- addView(mQsb, Utilities.isRtl(getResources()) ? getChildCount() : 0);
+ addView(mQsb, mIsRtl ? getChildCount() : 0);
// Always set QSB to invisible after re-adding.
mQsb.setVisibility(View.INVISIBLE);
}
@@ -285,12 +295,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;
@@ -314,10 +320,10 @@
int qsbEnd;
if (layoutRtl) {
qsbStart = iconEnd + mItemMarginLeftRight;
- qsbEnd = qsbStart + deviceProfile.qsbWidth;
+ qsbEnd = qsbStart + deviceProfile.hotseatQsbWidth;
} else {
qsbEnd = iconEnd - mItemMarginLeftRight;
- qsbStart = qsbEnd - deviceProfile.qsbWidth;
+ qsbStart = qsbEnd - deviceProfile.hotseatQsbWidth;
}
int qsbTop = (bottom - top - deviceProfile.hotseatQsbHeight) / 2;
int qsbBottom = qsbTop + deviceProfile.hotseatQsbHeight;
@@ -341,12 +347,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 +379,7 @@
public void setTouchesEnabled(boolean touchEnabled) {
this.mTouchEnabled = touchEnabled;
+ mControllerCallbacks.clearTouchInProgress();
}
/**
@@ -381,6 +398,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() {
@@ -395,6 +424,7 @@
/**
* Returns the all apps button in the taskbar.
*/
+ @Nullable
public View getAllAppsButtonView() {
return mAllAppsButton;
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index 077172d..168c353 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -17,10 +17,15 @@
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.AnimatedFloat.VALUE;
+import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_ALLAPPS_BUTTON_TAP;
-import static com.android.quickstep.AnimatedFloat.VALUE;
+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 android.annotation.NonNull;
import android.graphics.Rect;
@@ -28,15 +33,19 @@
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
+import android.view.animation.Interpolator;
+import androidx.annotation.Nullable;
import androidx.core.graphics.ColorUtils;
import androidx.core.view.OneShotPreDrawListener;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AlphaUpdateListener;
+import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.PendingAnimation;
@@ -44,11 +53,13 @@
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;
import java.io.PrintWriter;
import java.util.function.Predicate;
@@ -69,7 +80,8 @@
public static final int ALPHA_INDEX_NOTIFICATION_EXPANDED = 4;
public static final int ALPHA_INDEX_ASSISTANT_INVOKED = 5;
public static final int ALPHA_INDEX_IME_BUTTON_NAV = 6;
- private static final int NUM_ALPHA_CHANNELS = 7;
+ public static final int ALPHA_INDEX_SMALL_SCREEN = 7;
+ private static final int NUM_ALPHA_CHANNELS = 8;
private final TaskbarActivityContext mActivity;
private final TaskbarView mTaskbarView;
@@ -81,12 +93,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;
@@ -96,6 +114,10 @@
private Runnable mOnControllerPreCreateCallback = NO_OP;
private int mThemeIconsColor;
+ private boolean mIsHotseatIconOnTopWhenAligned;
+
+ private final DeviceProfile.OnDeviceProfileChangeListener mDeviceProfileChangeListener =
+ dp -> commitRunningAppsToUI();
public TaskbarViewController(TaskbarActivityContext activity, TaskbarView taskbarView) {
mActivity = activity;
@@ -103,12 +125,42 @@
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) {
mControllers = controllers;
mTaskbarView.init(new TaskbarViewCallbacks());
- mTaskbarView.getLayoutParams().height = mActivity.getDeviceProfile().taskbarSize;
+ mTaskbarView.getLayoutParams().height = isPhoneMode(mActivity.getDeviceProfile())
+ ? mActivity.getResources().getDimensionPixelSize(R.dimen.taskbar_size)
+ : mActivity.getDeviceProfile().taskbarSize;
mThemeIconsColor = ThemedIconDrawable.getColors(mTaskbarView.getContext())[0];
mTaskbarIconScaleForStash.updateValue(1f);
@@ -122,10 +174,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();
}
@@ -133,7 +188,7 @@
return mTaskbarView.areIconsVisible();
}
- public MultiValueAlpha getTaskbarIconAlpha() {
+ public MultiPropertyFactory<View> getTaskbarIconAlpha() {
return mTaskbarIconAlpha;
}
@@ -141,14 +196,15 @@
* Should be called when the IME visibility changes, so we can make Taskbar not steal touches.
*/
public void setImeIsVisible(boolean isImeVisible) {
- mTaskbarView.setTouchesEnabled(!isImeVisible);
+ mTaskbarView.setTouchesEnabled(!isImeVisible
+ || DisplayController.isTransientTaskbar(mActivity));
}
/**
* 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);
}
@@ -157,7 +213,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);
}
/**
@@ -180,10 +236,15 @@
return mTaskbarView.getIconLayoutBounds();
}
+ public int getIconLayoutWidth() {
+ return mTaskbarView.getIconLayoutWidth();
+ }
+
public View[] getIconViews() {
return mTaskbarView.getIconViews();
}
+ @Nullable
public View getAllAppsButtonView() {
return mTaskbarView.getAllAppsButtonView();
}
@@ -205,9 +266,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() {
@@ -225,10 +295,14 @@
* 0 => not aligned
* 1 => fully aligned
*/
- public void setLauncherIconAlignment(float alignmentRatio, Float endAlignment,
- DeviceProfile launcherDp) {
- if (mIconAlignControllerLazy == null) {
- mIconAlignControllerLazy = createIconAlignmentController(launcherDp, endAlignment);
+ public void setLauncherIconAlignment(float alignmentRatio, DeviceProfile launcherDp) {
+ boolean isHotseatIconOnTopWhenAligned =
+ mControllers.uiController.isHotseatIconOnTopWhenAligned();
+ // When mIsHotseatIconOnTopWhenAligned changes, animation needs to be re-created.
+ if (mIconAlignControllerLazy == null
+ || mIsHotseatIconOnTopWhenAligned != isHotseatIconOnTopWhenAligned) {
+ mIsHotseatIconOnTopWhenAligned = isHotseatIconOnTopWhenAligned;
+ mIconAlignControllerLazy = createIconAlignmentController(launcherDp);
}
mIconAlignControllerLazy.setPlayFraction(alignmentRatio);
if (alignmentRatio <= 0 || alignmentRatio >= 1) {
@@ -240,8 +314,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();
@@ -253,10 +326,15 @@
borderSpacing,
launcherDp.numShownHotseatIcons);
+ boolean isToHome = mControllers.uiController.isIconAlignedWithHotseat();
+ // If Hotseat is not the top element, Taskbar should maintain in-app state as it fades out,
+ // or fade in while already in in-app state.
+ Interpolator interpolator = mIsHotseatIconOnTopWhenAligned ? LINEAR : FINAL_FRAME;
+
int offsetY = launcherDp.getTaskbarOffsetY();
- setter.setFloat(mTaskbarIconTranslationYForHome, VALUE, -offsetY, LINEAR);
- setter.setFloat(mTaskbarNavButtonTranslationY, VALUE, -offsetY, LINEAR);
- setter.setFloat(mTaskbarNavButtonTranslationYForInAppDisplay, VALUE, offsetY, LINEAR);
+ setter.setFloat(mTaskbarIconTranslationYForHome, VALUE, -offsetY, interpolator);
+ setter.setFloat(mTaskbarNavButtonTranslationY, VALUE, -offsetY, interpolator);
+ setter.setFloat(mTaskbarNavButtonTranslationYForInAppDisplay, VALUE, offsetY, interpolator);
if (Utilities.isDarkTheme(mTaskbarView.getContext())) {
setter.addFloat(mThemeIconsBackground, VALUE, 0f, 1f, LINEAR);
@@ -267,54 +345,66 @@
setter.addOnFrameListener(anim -> mActivity.setTaskbarWindowHeight(
anim.getAnimatedFraction() > 0 ? expandedHeight : collapsedHeight));
- boolean isToHome = endAlignment != null && endAlignment == 1;
for (int i = 0; i < mTaskbarView.getChildCount(); i++) {
View child = mTaskbarView.getChildAt(i);
int positionInHotseat;
- if (FeatureFlags.ENABLE_ALL_APPS_IN_TASKBAR.get()
- && child == mTaskbarView.getAllAppsButtonView()) {
- // Note that there is no All Apps button in the hotseat, this position is only used
- // as its convenient for animation purposes.
- positionInHotseat = Utilities.isRtl(child.getResources())
- ? -1
- : taskbarDp.numShownHotseatIcons;
+ boolean isAllAppsButton = FeatureFlags.ENABLE_ALL_APPS_IN_TASKBAR.get()
+ && child == mTaskbarView.getAllAppsButtonView();
+ if (!mIsHotseatIconOnTopWhenAligned) {
+ // When going to home, the EMPHASIZED interpolator in TaskbarLauncherStateController
+ // plays iconAlignment to 1 really fast, therefore moving the fading towards the end
+ // to avoid icons disappearing rather than fading out visually.
+ setter.setViewAlpha(child, 0, Interpolators.clampToProgress(LINEAR, 0.8f, 1f));
+ } else if ((isAllAppsButton && !FeatureFlags.ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT.get())) {
+ setter.setViewAlpha(child, 0,
+ isToHome
+ ? Interpolators.clampToProgress(LINEAR, 0f, 0.17f)
+ : Interpolators.clampToProgress(LINEAR, 0.72f, 0.84f));
+ }
- if (!FeatureFlags.ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT.get()) {
- setter.setViewAlpha(child, 0,
- isToHome
- ? Interpolators.clampToProgress(LINEAR, 0f, 0.17f)
- : Interpolators.clampToProgress(LINEAR, 0.72f, 0.84f));
- }
- } else if (child.getTag() instanceof ItemInfo) {
- positionInHotseat = ((ItemInfo) child.getTag()).screenId;
- } else if (child == mTaskbarView.getQsb()) {
+ if (child == mTaskbarView.getQsb()) {
boolean isRtl = Utilities.isRtl(child.getResources());
float hotseatIconCenter = isRtl
? launcherDp.widthPx - hotseatPadding.right + borderSpacing
- + launcherDp.qsbWidth / 2f
- : hotseatPadding.left - borderSpacing - launcherDp.qsbWidth / 2f;
+ + launcherDp.hotseatQsbWidth / 2f
+ : hotseatPadding.left - borderSpacing - launcherDp.hotseatQsbWidth / 2f;
float childCenter = (child.getLeft() + child.getRight()) / 2f;
- float halfQsbIconWidthDiff = (launcherDp.qsbWidth - taskbarDp.iconSizePx) / 2f;
+ float halfQsbIconWidthDiff =
+ (launcherDp.hotseatQsbWidth - taskbarDp.iconSizePx) / 2f;
setter.addFloat(child, ICON_TRANSLATE_X,
isRtl ? -halfQsbIconWidthDiff : halfQsbIconWidthDiff,
- hotseatIconCenter - childCenter, LINEAR);
+ hotseatIconCenter - childCenter, interpolator);
float scale = ((float) taskbarDp.iconSizePx) / launcherDp.hotseatQsbVisualHeight;
- setter.addFloat(child, SCALE_PROPERTY, scale, 1f, LINEAR);
+ setter.addFloat(child, SCALE_PROPERTY, scale, 1f, interpolator);
- setter.addFloat(child, VIEW_ALPHA, 0f, 1f,
- isToHome
- ? Interpolators.clampToProgress(LINEAR, 0f, 0.35f)
- : Interpolators.clampToProgress(LINEAR, 0.84f, 1f));
+ setter.setFloat(child, VIEW_TRANSLATE_Y, mTaskbarBottomMargin, interpolator);
+
+ if (mIsHotseatIconOnTopWhenAligned) {
+ setter.addFloat(child, VIEW_ALPHA, 0f, 1f,
+ isToHome
+ ? Interpolators.clampToProgress(LINEAR, 0f, 0.35f)
+ : Interpolators.clampToProgress(LINEAR, 0.84f, 1f));
+ }
setter.addOnFrameListener(animator -> AlphaUpdateListener.updateVisibility(child));
- float qsbInsetFraction = halfQsbIconWidthDiff / launcherDp.qsbWidth;
- if (child instanceof HorizontalInsettableView) {
+ float qsbInsetFraction = halfQsbIconWidthDiff / launcherDp.hotseatQsbWidth;
+ if (child instanceof HorizontalInsettableView) {
setter.addFloat((HorizontalInsettableView) child,
HorizontalInsettableView.HORIZONTAL_INSETS, qsbInsetFraction, 0,
- LINEAR);
+ interpolator);
}
continue;
+ }
+
+ if (isAllAppsButton) {
+ // Note that there is no All Apps button in the hotseat, this position is only used
+ // as its convenient for animation purposes.
+ positionInHotseat = Utilities.isRtl(child.getResources())
+ ? taskbarDp.numShownHotseatIcons
+ : -1;
+ } else if (child.getTag() instanceof ItemInfo) {
+ positionInHotseat = ((ItemInfo) child.getTag()).screenId;
} else {
Log.w(TAG, "Unsupported view found in createIconAlignmentController, v=" + child);
continue;
@@ -325,9 +415,11 @@
+ hotseatCellSize / 2f;
float childCenter = (child.getLeft() + child.getRight()) / 2f;
- setter.setFloat(child, ICON_TRANSLATE_X, hotseatIconCenter - childCenter, LINEAR);
+ setter.setFloat(child, ICON_TRANSLATE_X, hotseatIconCenter - childCenter, interpolator);
- setter.setFloat(child, SCALE_PROPERTY, scaleUp, LINEAR);
+ setter.setFloat(child, VIEW_TRANSLATE_Y, mTaskbarBottomMargin, interpolator);
+
+ setter.setFloat(child, SCALE_PROPERTY, scaleUp, interpolator);
}
AnimatorPlaybackController controller = setter.createPlaybackController();
@@ -336,8 +428,8 @@
}
public void onRotationChanged(DeviceProfile deviceProfile) {
- if (mControllers.taskbarStashController.isInApp()) {
- // We only translate on rotation when on home
+ if (!mControllers.uiController.isIconAlignedWithHotseat()) {
+ // We only translate on rotation when icon is aligned with hotseat
return;
}
mActivity.setTaskbarWindowHeight(
@@ -385,7 +477,8 @@
"ALPHA_INDEX_RECENTS_DISABLED",
"ALPHA_INDEX_NOTIFICATION_EXPANDED",
"ALPHA_INDEX_ASSISTANT_INVOKED",
- "ALPHA_INDEX_IME_BUTTON_NAV");
+ "ALPHA_INDEX_IME_BUTTON_NAV",
+ "ALPHA_INDEX_SMALL_SCREEN");
mModelCallbacks.dumpLogs(prefix + "\t", pw);
}
@@ -409,6 +502,8 @@
private float mDownX, mDownY;
private boolean mCanceledStashHint;
+ private boolean mTouchInProgress;
+
public View.OnClickListener getIconOnClickListener() {
return mActivity.getItemOnClickListener();
}
@@ -430,37 +525,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 946873e..be34fb1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt
@@ -10,11 +10,9 @@
private const val TASKBAR_ICONS_FADE_DURATION = 300L
private const val STASHED_HANDLE_FADE_DURATION = 180L
-/**
- * Controls Taskbar behavior while Voice Interaction Window (assistant) is showing.
- */
-class VoiceInteractionWindowController(val context: TaskbarActivityContext)
- : TaskbarControllers.LoggableTaskbarController {
+/** Controls Taskbar behavior while Voice Interaction Window (assistant) is showing. */
+class VoiceInteractionWindowController(val context: TaskbarActivityContext) :
+ TaskbarControllers.LoggableTaskbarController, TaskbarControllers.BackgroundRendererController {
private val taskbarBackgroundRenderer = TaskbarBackgroundRenderer(context)
@@ -36,14 +34,19 @@
override fun draw(canvas: Canvas) {
super.draw(canvas)
- taskbarBackgroundRenderer.draw(canvas)
+ if (
+ this@VoiceInteractionWindowController.context.isGestureNav &&
+ controllers.taskbarStashController.isInAppAndNotStashed
+ ) {
+ taskbarBackgroundRenderer.draw(canvas)
+ }
}
}
separateWindowForTaskbarBackground.recreateControllers()
separateWindowForTaskbarBackground.setWillNotDraw(false)
- separateWindowLayoutParams = context.createDefaultWindowLayoutParams(
- TYPE_APPLICATION_OVERLAY)
+ separateWindowLayoutParams =
+ context.createDefaultWindowLayoutParams(TYPE_APPLICATION_OVERLAY)
separateWindowLayoutParams.isSystemApplicationOverlay = true
}
@@ -59,14 +62,16 @@
// 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)
- .animateToValue(taskbarIconAlpha)
- .setDuration(TASKBAR_ICONS_FADE_DURATION)
- val fadeStashedHandle = controllers.stashedHandleViewController.stashedHandleAlpha
- .getProperty(StashedHandleViewController.ALPHA_INDEX_ASSISTANT_INVOKED)
- .animateToValue(taskbarIconAlpha)
- .setDuration(STASHED_HANDLE_FADE_DURATION)
+ val fadeTaskbarIcons =
+ controllers.taskbarViewController.taskbarIconAlpha
+ .get(TaskbarViewController.ALPHA_INDEX_ASSISTANT_INVOKED)
+ .animateToValue(taskbarIconAlpha)
+ .setDuration(TASKBAR_ICONS_FADE_DURATION)
+ val fadeStashedHandle =
+ controllers.stashedHandleViewController.stashedHandleAlpha
+ .get(StashedHandleViewController.ALPHA_INDEX_ASSISTANT_INVOKED)
+ .animateToValue(taskbarIconAlpha)
+ .setDuration(STASHED_HANDLE_FADE_DURATION)
fadeTaskbarIcons.start()
fadeStashedHandle.start()
if (skipAnim) {
@@ -74,36 +79,54 @@
fadeStashedHandle.end()
}
- if (context.isGestureNav && controllers.taskbarStashController.isInAppAndNotStashed) {
- moveTaskbarBackgroundToLowerLayer()
- }
+ moveTaskbarBackgroundToAppropriateLayer(skipAnim)
}
/**
+ * Either:
+ *
* Hides the TaskbarDragLayer background and creates a new window to draw just that background.
+ *
+ * OR
+ *
+ * Removes the temporary window and show the TaskbarDragLayer background again.
*/
- private fun moveTaskbarBackgroundToLowerLayer() {
- val taskbarBackgroundOverride = controllers.taskbarDragLayerController
- .overrideBackgroundAlpha
- if (isVoiceInteractionWindowVisible) {
- // First add the temporary window, then hide the overlapping taskbar background.
- context.addWindowView(separateWindowForTaskbarBackground, separateWindowLayoutParams)
- ViewRootSync.synchronizeNextDraw(separateWindowForTaskbarBackground, context.dragLayer
- ) {
- taskbarBackgroundOverride.updateValue(0f)
+ private fun moveTaskbarBackgroundToAppropriateLayer(skipAnim: Boolean) {
+ val taskbarBackgroundOverride =
+ controllers.taskbarDragLayerController.overrideBackgroundAlpha
+ val moveToLowerLayer = isVoiceInteractionWindowVisible
+ val onWindowsSynchronized =
+ if (moveToLowerLayer) {
+ // First add the temporary window, then hide the overlapping taskbar background.
+ context.addWindowView(
+ separateWindowForTaskbarBackground,
+ separateWindowLayoutParams
+ );
+ { taskbarBackgroundOverride.updateValue(0f) }
+ } else {
+ // First reapply the original taskbar background, then remove the temporary window.
+ taskbarBackgroundOverride.updateValue(1f);
+ { context.removeWindowView(separateWindowForTaskbarBackground) }
}
+
+ if (skipAnim) {
+ onWindowsSynchronized()
} else {
- // First reapply the original taskbar background, then remove the temporary window.
- taskbarBackgroundOverride.updateValue(1f)
- ViewRootSync.synchronizeNextDraw(separateWindowForTaskbarBackground, context.dragLayer
- ) {
- context.removeWindowView(separateWindowForTaskbarBackground)
- }
+ ViewRootSync.synchronizeNextDraw(
+ separateWindowForTaskbarBackground,
+ context.dragLayer,
+ onWindowsSynchronized
+ )
}
}
+ override fun setCornerRoundness(cornerRoundness: Float) {
+ taskbarBackgroundRenderer.setCornerRoundness(cornerRoundness)
+ separateWindowForTaskbarBackground.invalidate()
+ }
+
override fun dumpLogs(prefix: String, pw: PrintWriter) {
pw.println(prefix + "VoiceInteractionWindowController:")
pw.println("$prefix\tisVoiceInteractionWindowVisible=$isVoiceInteractionWindowVisible")
}
-}
\ No newline at end of file
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java
index 51fa4d9..eeca329 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java
@@ -17,17 +17,17 @@
import android.content.Context;
import android.util.AttributeSet;
+import android.view.View;
import android.view.WindowInsets;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.R;
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);
@@ -44,10 +44,33 @@
}
@Override
- protected BaseAllAppsAdapter<TaskbarAllAppsContext> createAdapter(
- AlphabeticalAppsList<TaskbarAllAppsContext> appsList,
- BaseAdapterProvider[] adapterProviders) {
- return new AllAppsGridAdapter<>(mActivityContext, getLayoutInflater(), appsList,
- adapterProviders);
+ protected View inflateSearchBox() {
+ // Remove top padding of header, since we do not have any search
+ mHeader.setPadding(mHeader.getPaddingLeft(), 0,
+ mHeader.getPaddingRight(), mHeader.getPaddingBottom());
+
+ TaskbarAllAppsFallbackSearchContainer searchView =
+ new TaskbarAllAppsFallbackSearchContainer(getContext(), null);
+ searchView.setId(R.id.search_container_all_apps);
+ searchView.setVisibility(GONE);
+ return searchView;
+ }
+
+ @Override
+ protected boolean isSearchSupported() {
+ return false;
+ }
+
+ @Override
+ protected void updateBackground(DeviceProfile deviceProfile) {
+ super.updateBackground(deviceProfile);
+ // TODO(b/240670050): Remove this and add header protection for the taskbar entrypoint.
+ mBottomSheetBackground.setBackgroundResource(R.drawable.bg_rounded_corner_bottom_sheet);
+ }
+
+ @Override
+ public boolean isInAllApps() {
+ // All apps is always open
+ return true;
}
}
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..7a34869 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,16 @@
*/
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 TaskbarAllAppsSlideInView mSlideInView;
+ 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 +76,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 +96,8 @@
}
mPredictedApps = predictedApps;
- if (mAllAppsContext != null) {
- mAllAppsContext.getAppsView().getFloatingHeaderView()
+ if (mAppsView != null) {
+ mAppsView.getFloatingHeaderView()
.findFixedRowByType(PredictionRowView.class)
.setPredictedApps(mPredictedApps);
}
@@ -135,123 +108,49 @@
show(true);
}
+ /** Returns {@code true} if All Apps is open. */
+ public boolean isOpen() {
+ return mSlideInView != null && mSlideInView.isOpen();
+ }
+
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();
+ mSlideInView = (TaskbarAllAppsSlideInView) overlayContext.getLayoutInflater().inflate(
+ R.layout.taskbar_all_apps, overlayContext.getDragLayer(), false);
+ mSlideInView.addOnCloseListener(() -> {
+ mControllers.getSharedState().allAppsVisible = false;
+ mSlideInView = null;
+ mAppsView = null;
+ });
+ TaskbarAllAppsViewController viewController = new TaskbarAllAppsViewController(
+ overlayContext, mSlideInView, 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..8502752 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
@@ -15,9 +15,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,17 +27,19 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Insettable;
import com.android.launcher3.R;
+import com.android.launcher3.taskbar.allapps.TaskbarAllAppsViewController.TaskbarAllAppsCallbacks;
+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;
+ // Initialized in init.
+ private TaskbarAllAppsCallbacks mAllAppsCallbacks;
+
public TaskbarAllAppsSlideInView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
@@ -49,6 +49,10 @@
super(context, attrs, defStyleAttr);
}
+ void init(TaskbarAllAppsCallbacks callbacks) {
+ mAllAppsCallbacks = callbacks;
+ }
+
/** Opens the all apps view. */
void show(boolean animate) {
if (mIsOpen || mOpenCloseAnimator.isRunning()) {
@@ -60,9 +64,8 @@
if (animate) {
mOpenCloseAnimator.setValues(
PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
- mOpenCloseAnimator.setInterpolator(AGGRESSIVE_EASE);
- mOpenCloseAnimator.setDuration(
- ALL_APPS.getTransitionDuration(mActivityContext, true /* isToState */)).start();
+ mOpenCloseAnimator.setInterpolator(EMPHASIZED);
+ mOpenCloseAnimator.setDuration(mAllAppsCallbacks.getOpenDuration()).start();
} else {
mTranslationShift = TRANSLATION_SHIFT_OPENED;
}
@@ -73,21 +76,14 @@
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 */));
+ handleClose(animate, mAllAppsCallbacks.getCloseDuration());
}
@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..7a3b3e8 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java
@@ -15,14 +15,19 @@
*/
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.Executors.MAIN_EXECUTOR;
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;
+import com.android.launcher3.taskbar.overlay.TaskbarOverlayController;
+import com.android.launcher3.util.DisplayController;
/**
* Handles the {@link TaskbarAllAppsContainerView} behavior and synchronizes its transitions with
@@ -30,26 +35,29 @@
*/
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;
+ private final TaskbarOverlayController mOverlayController;
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;
+ mOverlayController = taskbarControllers.taskbarOverlayController;
+ mSlideInView.init(new TaskbarAllAppsCallbacks());
setUpIconLongClick();
setUpAppDivider();
setUpTaskbarStashing();
- mSlideInView.addOnCloseListener(windowController::maybeCloseWindow);
}
/** Starts the {@link TaskbarAllAppsSlideInView} enter transition. */
@@ -80,15 +88,34 @@
}
private void setUpTaskbarStashing() {
- mTaskbarStashController.updateStateForFlag(FLAG_STASHED_IN_APP_ALL_APPS, true);
- mTaskbarStashController.applyState(
- ALL_APPS.getTransitionDuration(mContext, true /* isToState */));
+ mTaskbarStashController.updateStateForFlag(FLAG_STASHED_IN_TASKBAR_ALL_APPS, true);
+ mTaskbarStashController.applyState(mOverlayController.getOpenDuration());
+
+ 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,
- // wait to unstash until after the gesture is finished.
- mSlideInView.post(mTaskbarStashController::maybeResetStashedInAppAllApps);
+
+ if (DisplayController.isTransientTaskbar(mContext)) {
+ mTaskbarStashController.updateStateForFlag(FLAG_STASHED_IN_TASKBAR_ALL_APPS, false);
+ mTaskbarStashController.applyState(mOverlayController.getCloseDuration());
+ } else {
+ // Post in case view is closing due to gesture navigation. If a gesture is in
+ // progress, wait to unstash until after the gesture is finished.
+ MAIN_EXECUTOR.post(() -> mTaskbarStashController.resetFlagIfNoGestureInProgress(
+ FLAG_STASHED_IN_TASKBAR_ALL_APPS));
+ }
});
}
+
+ class TaskbarAllAppsCallbacks {
+ int getOpenDuration() {
+ return mOverlayController.getOpenDuration();
+ }
+
+ int getCloseDuration() {
+ return mOverlayController.getCloseDuration();
+ }
+ }
}
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..a82902f
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt
@@ -0,0 +1,46 @@
+/*
+ * 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..468a1a7
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/KidsNavLayoutter.kt
@@ -0,0 +1,93 @@
+/*
+ * 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.*
+
+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
+ }
+}
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..0668da9
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt
@@ -0,0 +1,121 @@
+/*
+ * 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.*
+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)
+ }
+}
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..201895f
--- /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
+ }
+ }
+ }
+ }
+}
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..f7ac974
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt
@@ -0,0 +1,90 @@
+/*
+ * 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
+ }
+ }
+ }
+ }
+}
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..a24002c
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt
@@ -0,0 +1,50 @@
+/*
+ * 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()
+ }
+}
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..5ec7ca0
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt
@@ -0,0 +1,82 @@
+/*
+ * 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
+ }
+ }
+ }
+ }
+}
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..d7a0b03
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java
@@ -0,0 +1,128 @@
+/*
+ * 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.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;
+
+/**
+ * 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 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);
+ 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 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) {}
+}
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..476e0a8
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java
@@ -0,0 +1,213 @@
+/*
+ * 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 static com.android.launcher3.LauncherState.ALL_APPS;
+
+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);
+ }
+
+ @Override
+ public void onTaskMovedToFront(int taskId) {
+ 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();
+ });
+ }
+
+ /** The default open duration for overlays. */
+ public int getOpenDuration() {
+ return ALL_APPS.getTransitionDuration(mTaskbarContext, true);
+ }
+
+ /** The default close duration for overlays. */
+ public int getCloseDuration() {
+ return ALL_APPS.getTransitionDuration(mTaskbarContext, false);
+ }
+
+ @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..ca7ce74 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
@@ -21,6 +21,9 @@
import com.android.launcher3.Utilities;
+/**
+ * A wrapper for the hidden API calls
+ */
public class ApiWrapper {
public static final boolean TASKBAR_DRAWN_IN_PROCESS = true;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
index dc1046b..8fb7030 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
@@ -23,26 +23,32 @@
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_MODAL;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SPLIT_SELECT_FLOATING_TASK_TRANSLATE_OFFSCREEN;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SPLIT_SELECT_INSTRUCTIONS_FADE;
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.launcher3.testing.shared.TestProtocol.BAD_STATE;
+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.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_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.util.Log;
+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;
+import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.statemanager.StateManager.StateHandler;
import com.android.launcher3.states.StateAnimationConfig;
+import com.android.quickstep.views.FloatingTaskView;
import com.android.quickstep.views.RecentsView;
/**
@@ -54,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();
}
@@ -68,25 +74,25 @@
ADJACENT_PAGE_HORIZONTAL_OFFSET.set(mRecentsView, scaleAndOffset[1]);
TASK_SECONDARY_TRANSLATION.set(mRecentsView, 0f);
- float recentsAlpha = state.overviewUi ? 1f : 0;
- Log.d(BAD_STATE, "BaseRecentsViewStateController setState state=" + state
- + ", alpha=" + recentsAlpha);
- getContentAlphaProperty().set(mRecentsView, recentsAlpha);
+ getContentAlphaProperty().set(mRecentsView, state.overviewUi ? 1f : 0);
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
public void setStateWithAnimation(LauncherState toState, StateAnimationConfig config,
PendingAnimation builder) {
- Log.d(BAD_STATE, "BaseRecentsViewStateController setStateWithAnimation state=" + toState
- + ", config.skipOverview=" + config.hasAnimationFlag(SKIP_OVERVIEW));
if (config.hasAnimationFlag(SKIP_OVERVIEW)) {
return;
}
setStateWithAnimationInternal(toState, config, builder);
+ builder.addEndListener(success -> {
+ if (!success) {
+ mRecentsView.reset();
+ }
+ });
}
/**
@@ -106,23 +112,70 @@
setter.setFloat(mRecentsView, TASK_SECONDARY_TRANSLATION, 0f,
config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, LINEAR));
- float recentsAlpha = toState.overviewUi ? 1 : 0;
- Log.d(BAD_STATE, "BaseRecentsViewStateController setStateWithAnimationInternal toState="
- + toState + ", alpha=" + recentsAlpha);
- setter.setFloat(mRecentsView, getContentAlphaProperty(), recentsAlpha,
+ if (mRecentsView.isSplitSelectionActive()) {
+ // 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,
+ new Rect(0, 0, floatingTask.getWidth(), floatingTask.getHeight()),
+ false, null, onScreenRectF);
+ // Get the part of the floatingTask that intersects with the DragLayer (i.e. the
+ // on-screen portion)
+ onScreenRectF.intersect(
+ dragLayer.getLeft(),
+ dragLayer.getTop(),
+ dragLayer.getRight(),
+ dragLayer.getBottom()
+ );
+
+ setter.setFloat(
+ mRecentsView.getFirstFloatingTaskView(),
+ PRIMARY_TRANSLATE_OFFSCREEN,
+ mRecentsView.getPagedOrientationHandler()
+ .getFloatingTaskOffscreenTranslationTarget(
+ floatingTask,
+ onScreenRectF,
+ floatingTask.getStagePosition(),
+ mLauncher.getDeviceProfile()
+ ),
+ config.getInterpolator(
+ ANIM_OVERVIEW_SPLIT_SELECT_FLOATING_TASK_TRANSLATE_OFFSCREEN,
+ LINEAR
+ ));
+ setter.setViewAlpha(
+ mRecentsView.getSplitInstructionsView(),
+ 0,
+ config.getInterpolator(
+ ANIM_OVERVIEW_SPLIT_SELECT_INSTRUCTIONS_FADE,
+ LINEAR
+ )
+ );
+ }
+ }
+
+ setter.setFloat(mRecentsView, getContentAlphaProperty(), toState.overviewUi ? 1 : 0,
config.getInterpolator(ANIM_OVERVIEW_FADE, AGGRESSIVE_EASE_IN_OUT));
setter.setFloat(
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/QuickstepAppWidgetHost.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepAppWidgetHost.java
new file mode 100644
index 0000000..6659fa0
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepAppWidgetHost.java
@@ -0,0 +1,70 @@
+/**
+ * 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.uioverrides;
+
+import static com.android.launcher3.widget.LauncherWidgetHolder.APPWIDGET_HOST_ID;
+
+import android.appwidget.AppWidgetHost;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.Context;
+import android.os.Looper;
+
+import androidx.annotation.NonNull;
+
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.widget.LauncherWidgetHolder;
+
+import java.util.function.IntConsumer;
+
+/**
+ * {@link AppWidgetHost} that is used to receive the changes to the widgets without
+ * storing any {@code Activity} info like that of the launcher.
+ */
+final class QuickstepAppWidgetHost extends AppWidgetHost {
+ private final @NonNull Context mContext;
+ private final @NonNull IntConsumer mAppWidgetRemovedCallback;
+ private final @NonNull LauncherWidgetHolder.ProviderChangedListener mProvidersChangedListener;
+
+ QuickstepAppWidgetHost(@NonNull Context context, @NonNull IntConsumer appWidgetRemovedCallback,
+ @NonNull LauncherWidgetHolder.ProviderChangedListener listener,
+ @NonNull Looper looper) {
+ super(context, APPWIDGET_HOST_ID, null, looper);
+ mContext = context;
+ mAppWidgetRemovedCallback = appWidgetRemovedCallback;
+ mProvidersChangedListener = listener;
+ }
+
+ @Override
+ protected void onProvidersChanged() {
+ mProvidersChangedListener.notifyWidgetProvidersChanged();
+ }
+
+ @Override
+ public void onAppWidgetRemoved(int appWidgetId) {
+ mAppWidgetRemovedCallback.accept(appWidgetId);
+ }
+
+ @Override
+ protected void onProviderChanged(int appWidgetId, @NonNull AppWidgetProviderInfo appWidget) {
+ LauncherAppWidgetProviderInfo info = LauncherAppWidgetProviderInfo.fromProviderInfo(
+ mContext, appWidget);
+ super.onProviderChanged(appWidgetId, info);
+ // The super method updates the dimensions of the providerInfo. Update the
+ // launcher spans accordingly.
+ info.initSpans(mContext, LauncherAppState.getIDP(mContext));
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 21f03be..0673dc6 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.
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.uioverrides;
+import static android.os.Trace.TRACE_TAG_APP;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_OPTIMIZE_MEASURE;
import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
@@ -23,41 +25,94 @@
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.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.os.Trace;
+import android.view.Display;
import android.view.HapticFeedbackConstants;
+import android.view.RemoteAnimationTarget;
import android.view.View;
+import android.view.WindowManagerGlobal;
+import android.window.BackEvent;
+import android.window.OnBackAnimationCallback;
+import android.window.OnBackInvokedDispatcher;
+import android.window.SplashScreen;
-import com.android.launcher3.BaseQuickstepLauncher;
+import androidx.annotation.BinderThread;
+import androidx.annotation.NonNull;
+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.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.testing.TestLogging;
+import com.android.launcher3.testing.shared.TestProtocol;
+import com.android.launcher3.uioverrides.QuickstepWidgetHolder.QuickstepHolderFactory;
import com.android.launcher3.uioverrides.states.QuickstepAtomicAnimationFactory;
import com.android.launcher3.uioverrides.touchcontrollers.NavBarToHomeTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.NoButtonNavbarToOverviewTouchController;
@@ -68,42 +123,120 @@
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.DesktopTaskView;
+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 +317,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 +345,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 +381,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 +421,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 +523,530 @@
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() {
+ final QuickstepHolderFactory factory =
+ (QuickstepHolderFactory) LauncherWidgetHolder.HolderFactory.newFactory(this);
+ return factory.newInstance(this,
+ appWidgetId -> getWorkspace().removeWidget(appWidgetId),
+ new QuickstepInteractionHandler(this));
+ }
+
+ @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());
+ }
+ getWindow().addPrivateFlags(PRIVATE_FLAG_OPTIMIZE_MEASURE);
+ }
+
+ @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 onScreenOnChanged(boolean isOn) {
+ super.onScreenOnChanged(isOn);
+ if (!isOn) {
+ 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));
+ }
+ }
+
+ @Override
+ protected void registerBackDispatcher() {
+ getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
+ OnBackInvokedDispatcher.PRIORITY_DEFAULT,
+ new OnBackAnimationCallback() {
+ @Override
+ public void onBackInvoked() {
+ onBackPressed();
+ TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onBackInvoked");
+ }
+
+ @Override
+ public void onBackProgressed(@NonNull BackEvent backEvent) {
+ QuickstepLauncher.this.onBackProgressed(backEvent.getProgress());
+ }
+
+ @Override
+ public void onBackCancelled() {
+ QuickstepLauncher.this.onBackCancelled();
+ }
+ });
+ }
+
+ 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
+ public void setResumed() {
+ if (DesktopTaskView.DESKTOP_IS_PROTO2_ENABLED) {
+ DesktopVisibilityController controller = mDesktopVisibilityController;
+ if (controller != null && controller.areFreeformTasksVisible()
+ && !controller.isGestureInProgress()) {
+ // Return early to skip setting activity to appear as resumed
+ // TODO(b/255649902): shouldn't be needed when we have a separate launcher state
+ // for desktop that we can use to control other parts of launcher
+ return;
+ }
+ }
+ super.setResumed();
+ }
+
+ @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= */ UI_HELPER_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) {
+ // Do not animate moving to rest state, as it can clash with Launcher#onIdpChanged
+ // where reapplyUi calls StateManager's reapplyState during the state change animation,
+ // and cancel the state change unexpectedly. The screen will be off during screen
+ // transition, hiding the unanimated transition.
+ getStateManager().moveToRestState(/* isAnimated = */false);
+ }
+
+ 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;
+ }
+
+ @Override
+ public boolean areFreeformTasksVisible() {
+ if (mDesktopVisibilityController != null) {
+ return mDesktopVisibilityController.areFreeformTasksVisible();
+ }
+ return false;
+ }
+
+ @Override
+ public void dispatchDeviceProfileChanged() {
+ super.dispatchDeviceProfileChanged();
+ Trace.instantForTrack(TRACE_TAG_APP, "QuickstepLauncher#DeviceProfileChanged",
+ getDeviceProfile().toSmallString());
}
private static final class LauncherTaskViewController extends
@@ -371,6 +1075,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/QuickstepWidgetHolder.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java
new file mode 100644
index 0000000..a8edd51
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java
@@ -0,0 +1,270 @@
+/**
+ * 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.uioverrides;
+
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+
+import android.appwidget.AppWidgetHost;
+import android.appwidget.AppWidgetHostView;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.Context;
+import android.util.SparseArray;
+import android.widget.RemoteViews;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.UiThread;
+import androidx.annotation.WorkerThread;
+
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.model.WidgetsModel;
+import com.android.launcher3.util.Thunk;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.widget.LauncherWidgetHolder;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.WeakHashMap;
+import java.util.function.Consumer;
+import java.util.function.IntConsumer;
+
+/**
+ * {@link LauncherWidgetHolder} that puts the app widget host in the background
+ */
+public final class QuickstepWidgetHolder extends LauncherWidgetHolder {
+ private static final List<QuickstepWidgetHolder> sHolders = new ArrayList<>();
+ private static final SparseArray<QuickstepWidgetHolderListener> sListeners =
+ new SparseArray<>();
+
+ private static AppWidgetHost sWidgetHost = null;
+
+ private final @Nullable RemoteViews.InteractionHandler mInteractionHandler;
+
+ private final @NonNull IntConsumer mAppWidgetRemovedCallback;
+
+ private final ArrayList<ProviderChangedListener> mProviderChangedListeners = new ArrayList<>();
+
+ @Thunk
+ QuickstepWidgetHolder(@NonNull Context context,
+ @Nullable IntConsumer appWidgetRemovedCallback,
+ @Nullable RemoteViews.InteractionHandler interactionHandler) {
+ super(context, appWidgetRemovedCallback);
+ mAppWidgetRemovedCallback = appWidgetRemovedCallback != null ? appWidgetRemovedCallback
+ : i -> {};
+ mInteractionHandler = interactionHandler;
+ sHolders.add(this);
+ }
+
+ @Override
+ @NonNull
+ protected AppWidgetHost createHost(@NonNull Context context,
+ @Nullable IntConsumer appWidgetRemovedCallback) {
+ if (sWidgetHost == null) {
+ sWidgetHost = new QuickstepAppWidgetHost(context.getApplicationContext(),
+ i -> MAIN_EXECUTOR.execute(() ->
+ sHolders.forEach(h -> h.mAppWidgetRemovedCallback.accept(i))),
+ () -> MAIN_EXECUTOR.execute(() ->
+ sHolders.forEach(h -> h.mProviderChangedListeners.forEach(
+ ProviderChangedListener::notifyWidgetProvidersChanged))),
+ UI_HELPER_EXECUTOR.getLooper());
+ if (!WidgetsModel.GO_DISABLE_WIDGETS) {
+ sWidgetHost.startListening();
+ }
+ }
+ return sWidgetHost;
+ }
+
+ /**
+ * Delete the specified app widget from the host
+ * @param appWidgetId The ID of the app widget to be deleted
+ */
+ @Override
+ public void deleteAppWidgetId(int appWidgetId) {
+ super.deleteAppWidgetId(appWidgetId);
+ sListeners.remove(appWidgetId);
+ }
+
+ /**
+ * Called when the launcher is destroyed
+ */
+ @Override
+ public void destroy() {
+ sHolders.remove(this);
+ }
+
+ /**
+ * Add a listener that is triggered when the providers of the widgets are changed
+ * @param listener The listener that notifies when the providers changed
+ */
+ @Override
+ public void addProviderChangeListener(
+ @NonNull LauncherWidgetHolder.ProviderChangedListener listener) {
+ mProviderChangedListeners.add(listener);
+ }
+
+ /**
+ * Remove the specified listener from the host
+ * @param listener The listener that is to be removed from the host
+ */
+ @Override
+ public void removeProviderChangeListener(
+ LauncherWidgetHolder.ProviderChangedListener listener) {
+ mProviderChangedListeners.remove(listener);
+ }
+
+ /**
+ * Stop the host from updating the widget views
+ */
+ @Override
+ public void stopListening() {
+ if (WidgetsModel.GO_DISABLE_WIDGETS) {
+ return;
+ }
+
+ sWidgetHost.setAppWidgetHidden();
+ setListeningFlag(false);
+ }
+
+ /**
+ * 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
+ @Override
+ public LauncherAppWidgetHostView createView(@NonNull Context context, int appWidgetId,
+ @NonNull LauncherAppWidgetProviderInfo appWidget) {
+ LauncherAppWidgetHostView widgetView = getPendingView(appWidgetId);
+ if (widgetView != null) {
+ removePendingView(appWidgetId);
+ } else {
+ widgetView = new LauncherAppWidgetHostView(context);
+ }
+ widgetView.setInteractionHandler(mInteractionHandler);
+ widgetView.setAppWidget(appWidgetId, appWidget);
+
+ QuickstepWidgetHolderListener listener = sListeners.get(appWidgetId);
+ if (listener == null) {
+ listener = new QuickstepWidgetHolderListener(this, widgetView);
+ sWidgetHost.setListener(appWidgetId, listener);
+ sListeners.put(appWidgetId, listener);
+ } else {
+ listener.resetView(this, widgetView);
+ }
+
+ return widgetView;
+ }
+
+ /**
+ * Clears all the views from the host
+ */
+ @Override
+ public void clearViews() {
+ for (int i = sListeners.size() - 1; i >= 0; i--) {
+ sListeners.valueAt(i).mView.remove(this);
+ }
+ }
+
+ private static class QuickstepWidgetHolderListener
+ implements AppWidgetHost.AppWidgetHostListener {
+ @NonNull
+ private final Map<QuickstepWidgetHolder, AppWidgetHostView> mView = new WeakHashMap<>();
+
+ @Nullable
+ private RemoteViews mRemoteViews = null;
+
+ QuickstepWidgetHolderListener(@NonNull QuickstepWidgetHolder holder,
+ @NonNull LauncherAppWidgetHostView view) {
+ mView.put(holder, view);
+ }
+
+ @UiThread
+ public void resetView(@NonNull QuickstepWidgetHolder holder,
+ @NonNull AppWidgetHostView view) {
+ mView.put(holder, view);
+ view.updateAppWidget(mRemoteViews);
+ }
+
+ @Override
+ @WorkerThread
+ public void onUpdateProviderInfo(@Nullable AppWidgetProviderInfo info) {
+ mRemoteViews = null;
+ executeOnMainExecutor(v -> v.onUpdateProviderInfo(info));
+ }
+
+ @Override
+ @WorkerThread
+ public void updateAppWidget(@Nullable RemoteViews views) {
+ mRemoteViews = views;
+ executeOnMainExecutor(v -> v.updateAppWidget(mRemoteViews));
+ }
+
+ @Override
+ @WorkerThread
+ public void onViewDataChanged(int viewId) {
+ executeOnMainExecutor(v -> v.onViewDataChanged(viewId));
+ }
+
+ private void executeOnMainExecutor(Consumer<AppWidgetHostView> consumer) {
+ MAIN_EXECUTOR.execute(() -> mView.values().forEach(consumer));
+ }
+ }
+
+ /**
+ * {@code HolderFactory} subclass that takes an interaction handler as one of the parameters
+ * when creating a new instance.
+ */
+ public static class QuickstepHolderFactory extends HolderFactory {
+
+ @SuppressWarnings("unused")
+ public QuickstepHolderFactory(Context context) { }
+
+ @Override
+ public LauncherWidgetHolder newInstance(@NonNull Context context,
+ @Nullable IntConsumer appWidgetRemovedCallback) {
+ return newInstance(context, appWidgetRemovedCallback, null);
+ }
+
+ /**
+ * @param context The context of the caller
+ * @param appWidgetRemovedCallback The callback that is called when widgets are removed
+ * @param interactionHandler The interaction handler when the widgets are clicked
+ * @return A new {@link LauncherWidgetHolder} instance
+ */
+ public LauncherWidgetHolder newInstance(@NonNull Context context,
+ @Nullable IntConsumer appWidgetRemovedCallback,
+ @Nullable RemoteViews.InteractionHandler interactionHandler) {
+
+ if (!FeatureFlags.ENABLE_WIDGET_HOST_IN_BACKGROUND.get()) {
+ return new LauncherWidgetHolder(context, appWidgetRemovedCallback) {
+ @Override
+ protected AppWidgetHost createHost(Context context,
+ @Nullable IntConsumer appWidgetRemovedCallback) {
+ AppWidgetHost host = super.createHost(context, appWidgetRemovedCallback);
+ host.setInteractionHandler(interactionHandler);
+ return host;
+ }
+ };
+ }
+ return new QuickstepWidgetHolder(context, appWidgetRemovedCallback, interactionHandler);
+ }
+ }
+}
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/plugins/PluginEnablerImpl.java b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginEnablerImpl.java
index 5afeca7..faa900b 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginEnablerImpl.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginEnablerImpl.java
@@ -18,11 +18,11 @@
import android.content.Context;
import android.content.SharedPreferences;
-import com.android.launcher3.Utilities;
-import com.android.systemui.shared.plugins.PluginEnabler;
-
import androidx.preference.PreferenceDataStore;
+import com.android.launcher3.LauncherPrefs;
+import com.android.systemui.shared.plugins.PluginEnabler;
+
public class PluginEnablerImpl extends PreferenceDataStore implements PluginEnabler {
private static final String PREFIX_PLUGIN_ENABLED = "PLUGIN_ENABLED_";
@@ -30,7 +30,7 @@
final private SharedPreferences mSharedPrefs;
public PluginEnablerImpl(Context context) {
- mSharedPrefs = Utilities.getDevicePrefs(context);
+ mSharedPrefs = LauncherPrefs.getDevicePrefs(context);
}
@Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java
index fe0bca6..e3d386c 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java
@@ -28,9 +28,9 @@
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.systemui.plugins.Plugin;
import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.plugins.PluginManager;
import com.android.systemui.shared.plugins.PluginActionManager;
import com.android.systemui.shared.plugins.PluginInstance;
-import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.shared.plugins.PluginManagerImpl;
import com.android.systemui.shared.plugins.PluginPrefs;
import com.android.systemui.shared.system.UncaughtExceptionPreHandlerManager;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
index 9f2efc4..2a42175 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -20,11 +20,11 @@
import android.content.Context;
-import com.android.launcher3.DeviceProfile.DeviceProfileListenable;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
import com.android.launcher3.util.Themes;
+import com.android.launcher3.views.ActivityContext;
/**
* Definition for AllApps state
@@ -39,11 +39,11 @@
}
@Override
- public <DEVICE_PROFILE_CONTEXT extends Context & DeviceProfileListenable>
+ public <DEVICE_PROFILE_CONTEXT extends Context & ActivityContext>
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 +77,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 & ActivityContext>
+ 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..95eb128 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,7 +26,9 @@
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.DesktopTaskView;
import com.android.quickstep.views.RecentsView;
/**
@@ -83,19 +86,36 @@
}
@Override
+ public boolean showTaskThumbnailSplash() {
+ return true;
+ }
+
+ @Override
protected float getDepthUnchecked(Context context) {
+ if (DesktopTaskView.DESKTOP_MODE_SUPPORTED) {
+ if (Launcher.getLauncher(context).areFreeformTasksVisible()) {
+ // Don't blur the background while freeform tasks are visible
+ return 0;
+ }
+ }
return 1;
}
@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 c4c9038..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;
@@ -37,6 +38,10 @@
*/
public class OverviewState extends LauncherState {
+ private static final int OVERVIEW_SLIDE_IN_DURATION = 380;
+ private static final int OVERVIEW_POP_IN_DURATION = 250;
+ private static final int OVERVIEW_EXIT_DURATION = 250;
+
protected static final Rect sTempRect = new Rect();
private static final int STATE_FLAGS = FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED
@@ -57,8 +62,15 @@
@Override
public int getTransitionDuration(Context context, boolean isToState) {
- // In gesture modes, overview comes in all the way from the side, so give it more time.
- return DisplayController.getNavigationMode(context).hasGestures ? 380 : 250;
+ if (isToState) {
+ // In gesture modes, overview comes in all the way from the side, so give it more time.
+ return DisplayController.getNavigationMode(context).hasGestures
+ ? OVERVIEW_SLIDE_IN_DURATION
+ : OVERVIEW_POP_IN_DURATION;
+ } else {
+ // When exiting Overview, exit quickly.
+ return OVERVIEW_EXIT_DURATION;
+ }
}
@Override
@@ -93,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/QuickSwitchState.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
index 969abc2..7392469 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
@@ -17,10 +17,13 @@
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
+import android.graphics.Color;
+
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.util.Themes;
+import com.android.quickstep.views.DesktopTaskView;
/**
* State to indicate we are about to launch a recent task. Note that this state is only used when
@@ -43,6 +46,12 @@
@Override
public int getWorkspaceScrimColor(Launcher launcher) {
+ if (DesktopTaskView.DESKTOP_MODE_SUPPORTED) {
+ if (launcher.areFreeformTasksVisible()) {
+ // No scrim while freeform tasks are visible
+ return Color.TRANSPARENT;
+ }
+ }
DeviceProfile dp = launcher.getDeviceProfile();
if (dp.isTaskbarPresentInApps) {
return launcher.getColor(R.color.taskbar_background);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
index 4d2f965..5eeeb36 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
@@ -22,16 +22,19 @@
import static com.android.launcher3.LauncherState.HINT_STATE_TWO_BUTTON;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
import static com.android.launcher3.WorkspaceStateTransitionAnimation.getWorkspaceSpringScaleAnimator;
import static com.android.launcher3.anim.Interpolators.ACCEL;
import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
import static com.android.launcher3.anim.Interpolators.DEACCEL;
import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7;
import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
-import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
+import static com.android.launcher3.anim.Interpolators.EMPHASIZED_ACCELERATE;
+import static com.android.launcher3.anim.Interpolators.EMPHASIZED_DECELERATE;
import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
import static com.android.launcher3.anim.Interpolators.INSTANT;
import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.anim.Interpolators.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;
@@ -39,6 +42,8 @@
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_ACTIONS_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SPLIT_SELECT_FLOATING_TASK_TRANSLATE_OFFSCREEN;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SPLIT_SELECT_INSTRUCTIONS_FADE;
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.ANIM_SCRIM_FADE;
@@ -58,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;
/**
@@ -87,9 +93,16 @@
public void prepareForAtomicAnimation(LauncherState fromState, LauncherState toState,
StateAnimationConfig config) {
RecentsView overview = mActivity.getOverviewPanel();
- if (toState == NORMAL && fromState == OVERVIEW) {
+ if ((fromState == OVERVIEW || fromState == OVERVIEW_SPLIT_SELECT) && toState == NORMAL) {
+ if (fromState == OVERVIEW_SPLIT_SELECT) {
+ config.setInterpolator(ANIM_OVERVIEW_SPLIT_SELECT_FLOATING_TASK_TRANSLATE_OFFSCREEN,
+ clampToProgress(EMPHASIZED_ACCELERATE, 0, 0.4f));
+ config.setInterpolator(ANIM_OVERVIEW_SPLIT_SELECT_INSTRUCTIONS_FADE,
+ clampToProgress(LINEAR, 0, 0.33f));
+ }
+
config.setInterpolator(ANIM_OVERVIEW_ACTIONS_FADE, clampToProgress(LINEAR, 0, 0.25f));
- config.setInterpolator(ANIM_SCRIM_FADE, LINEAR);
+ config.setInterpolator(ANIM_SCRIM_FADE, clampToProgress(LINEAR, 0.33f, 1));
config.setInterpolator(ANIM_WORKSPACE_SCALE, DEACCEL);
config.setInterpolator(ANIM_WORKSPACE_FADE, ACCEL);
@@ -98,22 +111,21 @@
// Overview is going offscreen, so keep it at its current scale and opacity.
config.setInterpolator(ANIM_OVERVIEW_SCALE, FINAL_FRAME);
config.setInterpolator(ANIM_OVERVIEW_FADE, FINAL_FRAME);
- config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X,
- clampToProgress(FAST_OUT_SLOW_IN, 0, 0.75f));
+ 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;
@@ -178,6 +190,22 @@
AllAppsSwipeController.applyAllAppsToNormalConfig(mActivity, config);
} else if (fromState == NORMAL && toState == ALL_APPS) {
AllAppsSwipeController.applyNormalToAllAppsAnimConfig(mActivity, config);
+ } else if (fromState == OVERVIEW && toState == OVERVIEW_SPLIT_SELECT) {
+ 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 e79d56b..8babd34 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java
@@ -16,7 +16,10 @@
package com.android.launcher3.uioverrides.states;
+import android.content.Context;
+
import com.android.launcher3.Launcher;
+import com.android.quickstep.util.SplitAnimationTimings;
import com.android.quickstep.views.RecentsView;
/**
@@ -38,4 +41,16 @@
RecentsView recentsView = launcher.getOverviewPanel();
return recentsView.getSplitSelectTranslation();
}
+
+ @Override
+ public int getTransitionDuration(Context context, boolean isToState) {
+ 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..b5afda3 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
@@ -25,7 +25,8 @@
import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
import static com.android.launcher3.anim.AnimatorListeners.forSuccessCallback;
import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
-import static com.android.quickstep.util.VibratorWrapper.OVERVIEW_HAPTIC;
+import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ONE_HANDED_ACTIVE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
import android.animation.ObjectAnimator;
@@ -34,18 +35,18 @@
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.launcher3.util.VibratorWrapper;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.MotionPauseDetector;
import com.android.quickstep.util.OverviewToHomeAnim;
-import com.android.quickstep.util.VibratorWrapper;
import com.android.quickstep.views.RecentsView;
/**
@@ -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 4092a73..e0cb0b4 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
@@ -39,16 +39,16 @@
import static com.android.launcher3.states.StateAnimationConfig.SKIP_ALL_ANIMATIONS;
import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
import static com.android.launcher3.states.StateAnimationConfig.SKIP_SCRIM;
-import static com.android.launcher3.testing.shared.TestProtocol.BAD_STATE;
import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_RIGHT;
import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_UP;
+import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
-import static com.android.quickstep.util.VibratorWrapper.OVERVIEW_HAPTIC;
import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
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.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;
@@ -56,26 +56,25 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.graphics.PointF;
-import android.util.Log;
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;
+import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
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.launcher3.util.VibratorWrapper;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.util.MotionPauseDetector;
-import com.android.quickstep.util.VibratorWrapper;
import com.android.quickstep.util.WorkspaceRevealAnim;
import com.android.quickstep.views.LauncherRecentsView;
import com.android.quickstep.views.RecentsView;
@@ -93,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;
@@ -115,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();
@@ -227,7 +226,7 @@
// Set RecentView's initial properties.
RECENTS_SCALE_PROPERTY.set(mRecentsView, fromState.getOverviewScaleAndOffset(mLauncher)[0]);
ADJACENT_PAGE_HORIZONTAL_OFFSET.set(mRecentsView, 1f);
- Log.d(BAD_STATE, "NBQSTC setupOverviewAnimators setContentAlpha=1");
+ TASK_THUMBNAIL_SPLASH_ALPHA.set(mRecentsView, fromState.showTaskThumbnailSplash() ? 1f : 0);
mRecentsView.setContentAlpha(1);
mRecentsView.setFullscreenProgress(fromState.getOverviewFullscreenProgress());
mLauncher.getActionsView().getVisibilityAlpha().setValue(
@@ -247,24 +246,6 @@
QUICK_SWITCH.getWorkspaceScrimColor(mLauncher), LINEAR);
if (mRecentsView.getTaskViewCount() == 0) {
xAnim.addFloat(mRecentsView, CONTENT_ALPHA, 0f, 1f, LINEAR);
- Log.d(BAD_STATE, "NBQSTC setupOverviewAnimators from: 0 to: 1");
- xAnim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- Log.d(BAD_STATE, "NBQSTC setupOverviewAnimators onStart");
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- float alpha = mRecentsView == null ? -1 : CONTENT_ALPHA.get(mRecentsView);
- Log.d(BAD_STATE, "NBQSTC setupOverviewAnimators onCancel, alpha=" + alpha);
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- Log.d(BAD_STATE, "NBQSTC setupOverviewAnimators onEnd");
- }
- });
}
mXOverviewAnim = xAnim.createPlaybackController();
mXOverviewAnim.dispatchOnStart();
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
index 9efbc34..8368f9c 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
@@ -186,12 +186,17 @@
case MotionEvent.ACTION_DOWN:
InteractionJankMonitorWrapper.begin(
mLauncher.getRootView(), InteractionJankMonitorWrapper.CUJ_OPEN_ALL_APPS);
+ InteractionJankMonitorWrapper.begin(
+ mLauncher.getRootView(),
+ InteractionJankMonitorWrapper.CUJ_CLOSE_ALL_APPS_SWIPE);
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
InteractionJankMonitorWrapper.cancel(
InteractionJankMonitorWrapper.CUJ_OPEN_ALL_APPS);
+ InteractionJankMonitorWrapper.cancel(
+ InteractionJankMonitorWrapper.CUJ_CLOSE_ALL_APPS_SWIPE);
break;
}
return super.onControllerInterceptTouchEvent(ev);
@@ -204,6 +209,10 @@
if (newToState != ALL_APPS) {
InteractionJankMonitorWrapper.cancel(InteractionJankMonitorWrapper.CUJ_OPEN_ALL_APPS);
}
+ if (newToState != NORMAL) {
+ InteractionJankMonitorWrapper.cancel(
+ InteractionJankMonitorWrapper.CUJ_CLOSE_ALL_APPS_SWIPE);
+ }
}
@Override
@@ -211,6 +220,9 @@
super.onReachedFinalState(toState);
if (toState == ALL_APPS) {
InteractionJankMonitorWrapper.end(InteractionJankMonitorWrapper.CUJ_OPEN_ALL_APPS);
+ } else if (toState == NORMAL) {
+ InteractionJankMonitorWrapper.end(
+ InteractionJankMonitorWrapper.CUJ_CLOSE_ALL_APPS_SWIPE);
}
}
@@ -218,5 +230,7 @@
protected void clearState() {
super.clearState();
InteractionJankMonitorWrapper.cancel(InteractionJankMonitorWrapper.CUJ_OPEN_ALL_APPS);
+ InteractionJankMonitorWrapper.cancel(
+ InteractionJankMonitorWrapper.CUJ_CLOSE_ALL_APPS_SWIPE);
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
index f607aa3..56ac4c5 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
@@ -29,7 +29,6 @@
import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_TRANSLATE;
-import static com.android.launcher3.testing.shared.TestProtocol.BAD_STATE;
import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK;
import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
@@ -37,7 +36,6 @@
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
-import android.util.Log;
import android.view.MotionEvent;
import com.android.launcher3.Launcher;
@@ -47,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;
@@ -114,7 +112,6 @@
RECENTS_SCALE_PROPERTY.set(mOverviewPanel,
QUICK_SWITCH.getOverviewScaleAndOffset(mLauncher)[0] * 0.85f);
ADJACENT_PAGE_HORIZONTAL_OFFSET.set(mOverviewPanel, 1f);
- Log.d(BAD_STATE, "QuickSwitchTouchController initCurrentAnimation setContentAlpha=1");
mOverviewPanel.setContentAlpha(1);
mCurrentAnimation = mLauncher.getStateManager()
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java
index 1bc789b..af63a07 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java
@@ -21,6 +21,7 @@
import static android.view.MotionEvent.ACTION_UP;
import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY;
+import static com.android.launcher3.Utilities.isTrackpadMotionEvent;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SWIPE_DOWN_WORKSPACE_NOTISHADE_OPEN;
import android.graphics.PointF;
@@ -104,7 +105,8 @@
// Currently input dispatcher will not do touch transfer if there are more than
// one touch pointer. Hence, even if slope passed, only set the slippery flag
// when there is single touch event. (context: InputDispatcher.cpp line 1445)
- if (dy > mTouchSlop && dy > Math.abs(dx) && ev.getPointerCount() == 1) {
+ if (dy > mTouchSlop && dy > Math.abs(dx) && (isTrackpadMotionEvent(ev)
+ || ev.getPointerCount() == 1)) {
ev.setAction(ACTION_DOWN);
dispatchTouchEvent(ev);
setWindowSlippery(true);
@@ -158,7 +160,8 @@
} else {
// For NORMAL state, only listen if the event originated above the navbar height
DeviceProfile dp = mLauncher.getDeviceProfile();
- if (ev.getY() > (mLauncher.getDragLayer().getHeight() - dp.getInsets().bottom)) {
+ if (!isTrackpadMotionEvent(ev) && ev.getY() > (mLauncher.getDragLayer().getHeight()
+ - dp.getInsets().bottom)) {
return false;
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
index ca7f633..eddc50c 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
@@ -41,8 +41,9 @@
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.FlingBlockCheck;
import com.android.launcher3.util.TouchController;
+import com.android.launcher3.util.VibratorWrapper;
import com.android.launcher3.views.BaseDragLayer;
-import com.android.quickstep.util.VibratorWrapper;
+import com.android.quickstep.util.VibrationConstants;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
@@ -61,7 +62,7 @@
Utilities.ATLEAST_R ? VibrationEffect.Composition.PRIMITIVE_TICK : -1;
public static final float TASK_DISMISS_VIBRATION_PRIMITIVE_SCALE = 1f;
public static final VibrationEffect TASK_DISMISS_VIBRATION_FALLBACK =
- VibratorWrapper.EFFECT_TEXTURE_TICK;
+ VibrationConstants.EFFECT_TEXTURE_TICK;
protected final T mActivity;
private final SingleAxisSwipeDetector mDetector;
@@ -236,7 +237,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 8f1872b..ddc14b8 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,16 +27,18 @@
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.config.FeatureFlags.ENABLE_TASKBAR_REVISED_THRESHOLDS;
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;
+import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
import static com.android.quickstep.GestureState.GestureEndTarget.HOME;
import static com.android.quickstep.GestureState.GestureEndTarget.LAST_TASK;
@@ -46,10 +49,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.VibratorWrapper.OVERVIEW_HAPTIC;
+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.LAUNCHER_DESTROYED;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.ON_SETTLED_ON_END_TARGET;
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 +66,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 +76,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,24 +90,30 @@
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;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.logging.StatsLogManager;
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.VibratorWrapper;
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,10 +124,11 @@
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;
-import com.android.quickstep.util.VibratorWrapper;
+import com.android.quickstep.views.DesktopTaskView;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.Task;
@@ -121,14 +136,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;
/**
@@ -167,6 +183,7 @@
if (mActivity != activity) {
return;
}
+ handleActivityDestroyed();
mRecentsView = null;
mActivity = null;
}
@@ -244,6 +261,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.
*/
@@ -272,15 +296,15 @@
private boolean mWasLauncherAlreadyVisible;
- private boolean mPassedOverviewThreshold;
private boolean mGestureStarted;
private boolean mLogDirectionUpOrLeft = true;
- private PointF mDownPos;
private boolean mIsLikelyToStartNewTask;
private final long mTouchTimeMs;
private long mLauncherFrameDrawnTime;
+ private final int mSplashMainWindowShiftLength;
+
private final Runnable mOnDeferredActivityLaunch = this::onDeferredActivityLaunch;
private SwipePipToHomeAnimator mSwipePipToHomeAnimator;
@@ -293,6 +317,20 @@
// Interpolate RecentsView scale from start of quick switch scroll until this scroll threshold
private final float mQuickSwitchScaleScrollThreshold;
+ private final int mTaskbarAppWindowThreshold;
+ private final int mTaskbarHomeOverviewThreshold;
+ private final int mTaskbarCatchUpThreshold;
+ private final boolean mTaskbarAlreadyOpen;
+ private final boolean mIsTaskbarAllAppsOpen;
+ private final boolean mIsTransientTaskbar;
+ // May be set to false when mIsTransientTaskbar is true.
+ private boolean mCanSlowSwipeGoHome = true;
+ private boolean mHasReachedOverviewThreshold = false;
+ private boolean mDividerHiddenBeforeAnimation = false;
+
+ @Nullable
+ private RemoteAnimationTargets.ReleaseCheck mSwipePipToHomeReleaseCheck = null;
+
public AbsSwipeUpHandler(Context context, RecentsAnimationDeviceState deviceState,
TaskAnimationManager taskAnimationManager, GestureState gestureState,
long touchTimeMs, boolean continuingLastGesture,
@@ -313,15 +351,60 @@
mTaskAnimationManager = taskAnimationManager;
mTouchTimeMs = touchTimeMs;
mContinuingLastGesture = continuingLastGesture;
- mQuickSwitchScaleScrollThreshold = context.getResources().getDimension(
- R.dimen.quick_switch_scaling_scroll_threshold);
- initAfterSubclassConstructor();
+ Resources res = context.getResources();
+ mQuickSwitchScaleScrollThreshold = res
+ .getDimension(R.dimen.quick_switch_scaling_scroll_threshold);
+
+ mSplashMainWindowShiftLength = -res
+ .getDimensionPixelSize(R.dimen.starting_surface_exit_animation_window_shift_length);
+
+ initTransitionEndpoints(mRemoteTargetHandles[0].getTaskViewSimulator()
+ .getOrientationState().getLauncherDeviceProfile());
initStateCallbacks();
+
+ mIsTransientTaskbar = mDp.isTaskbarPresent
+ && DisplayController.isTransientTaskbar(mActivity);
+ TaskbarUIController controller = mActivityInterface.getTaskbarController();
+ mTaskbarAlreadyOpen = controller != null && !controller.isTaskbarStashed();
+ mIsTaskbarAllAppsOpen = controller != null && controller.isTaskbarAllAppsOpen();
+ mTaskbarAppWindowThreshold = res
+ .getDimensionPixelSize(ENABLE_TASKBAR_REVISED_THRESHOLDS.get()
+ ? R.dimen.taskbar_app_window_threshold_v2
+ : R.dimen.taskbar_app_window_threshold);
+ boolean swipeWillNotShowTaskbar = mTaskbarAlreadyOpen;
+ mTaskbarHomeOverviewThreshold = swipeWillNotShowTaskbar
+ ? 0
+ : res.getDimensionPixelSize(
+ ENABLE_TASKBAR_REVISED_THRESHOLDS.get()
+ ? R.dimen.taskbar_home_overview_threshold_v2
+ : R.dimen.taskbar_home_overview_threshold);
+ mTaskbarCatchUpThreshold = res.getDimensionPixelSize(R.dimen.taskbar_catch_up_threshold);
+ }
+
+ @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,18 +457,13 @@
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) {
if (mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)) {
return false;
}
+ mStateCallback.resumeCallbacks();
T createdActivity = mActivityInterface.getCreatedActivity();
if (createdActivity != null) {
@@ -455,6 +533,15 @@
return true;
}
+ private void handleActivityDestroyed() {
+ ActiveGestureLog.INSTANCE.addLog("Launcher activity destroyed", LAUNCHER_DESTROYED);
+ if (mActivityInterface.shouldCancelGestureOnDestroy()) {
+ onGestureCancelled();
+ } else {
+ mStateCallback.pauseCallbacks();
+ }
+ }
+
/**
* Return true if the window should be translated horizontally if the recents view scrolls
*/
@@ -557,14 +644,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() {
@@ -595,8 +678,8 @@
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
@@ -611,6 +694,8 @@
public void onMotionPauseDetected() {
mHasMotionEverBeenPaused = true;
maybeUpdateRecentsAttachedState(true/* animate */, true/* moveFocusedTask */);
+ Optional.ofNullable(mActivityInterface.getTaskbarController())
+ .ifPresent(TaskbarUIController::startTranslationSpring);
performHapticFeedback();
}
@@ -642,7 +727,7 @@
if (!mDeviceState.isFullyGesturalNavMode() || mRecentsView == null) {
return;
}
- RemoteAnimationTargetCompat runningTaskTarget = mRecentsAnimationTargets != null
+ RemoteAnimationTarget runningTaskTarget = mRecentsAnimationTargets != null
? mRecentsAnimationTargets.findTask(mGestureState.getRunningTaskId())
: null;
final boolean recentsAttachedToAppWindow;
@@ -682,12 +767,25 @@
}
}
+ /**
+ * Returns threshold that needs to be met in order for motion pause to be allowed.
+ */
+ public float getThresholdToAllowMotionPause() {
+ return mIsTransientTaskbar
+ ? mTaskbarHomeOverviewThreshold
+ : 0;
+ }
+
public void setIsLikelyToStartNewTask(boolean isLikelyToStartNewTask) {
setIsLikelyToStartNewTask(isLikelyToStartNewTask, true /* animate */);
}
private void setIsLikelyToStartNewTask(boolean isLikelyToStartNewTask, boolean animate) {
if (mIsLikelyToStartNewTask != isLikelyToStartNewTask) {
+ if (isLikelyToStartNewTask && mIsTransientTaskbar) {
+ setDividerShown(false /* shown */, true /* immediate */);
+ }
+
mIsLikelyToStartNewTask = isLikelyToStartNewTask;
maybeUpdateRecentsAttachedState(animate);
}
@@ -744,14 +842,6 @@
@UiThread
@Override
public void updateFinalShift() {
- final boolean passed = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW;
- if (passed != mPassedOverviewThreshold) {
- mPassedOverviewThreshold = passed;
- if (mDeviceState.isTwoButtonNavMode() && !mGestureState.isHandlingAtomicEvent()) {
- performHapticFeedback();
- }
- }
-
updateSysUiFlags(mCurrentShift.value);
applyScrollAndTransform();
@@ -800,15 +890,21 @@
public void onRecentsAnimationStart(RecentsAnimationController controller,
RecentsAnimationTargets targets) {
super.onRecentsAnimationStart(controller, targets);
- ActiveGestureLog.INSTANCE.addLog("startRecentsAnimationCallback", targets.apps.length);
- mRemoteTargetHandles = mTargetGluer.assignTargetsForSplitScreen(mContext, targets);
+ if (DesktopTaskView.DESKTOP_MODE_SUPPORTED && targets.hasDesktopTasks()) {
+ mRemoteTargetHandles = mTargetGluer.assignTargetsForDesktop(targets);
+ } else {
+ mRemoteTargetHandles = mTargetGluer.assignTargetsForSplitScreen(mContext, targets);
+ }
mRecentsAnimationController = controller;
mRecentsAnimationTargets = targets;
+ mSwipePipToHomeReleaseCheck = new RemoteAnimationTargets.ReleaseCheck();
+ mSwipePipToHomeReleaseCheck.setCanRelease(true);
+ mRecentsAnimationTargets.addReleaseCheck(mSwipePipToHomeReleaseCheck);
// 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
@@ -837,23 +933,18 @@
mStateCallback.runOnceAtState(STATE_APP_CONTROLLER_RECEIVED | STATE_GESTURE_STARTED,
this::startInterceptingTouchesForGesture);
mStateCallback.setStateOnUiThread(STATE_APP_CONTROLLER_RECEIVED);
-
- mPassedOverviewThreshold = false;
}
@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,6 +974,8 @@
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));
}
@@ -890,12 +983,34 @@
}
notifyGestureStartedAsync();
setIsLikelyToStartNewTask(isLikelyToStartNewTask, false /* animate */);
+
+ if (mIsTransientTaskbar && !mTaskbarAlreadyOpen && !isLikelyToStartNewTask) {
+ setClampScrollOffset(true);
+ }
mStateCallback.setStateOnUiThread(STATE_GESTURE_STARTED);
mGestureStarted = true;
- SystemUiProxy.INSTANCE.get(mContext).notifySwipeUpGestureStarted();
}
/**
+ * Sets whether or not we should clamp the scroll offset.
+ * This is used to avoid x-axis movement when swiping up transient taskbar.
+ * @param clampScrollOffset When true, we clamp the scroll to 0 before the clamp threshold is
+ * met.
+ */
+ private void setClampScrollOffset(boolean clampScrollOffset) {
+ if (!mIsTransientTaskbar) {
+ return;
+ }
+ if (mRecentsView == null) {
+ mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT,
+ () -> mRecentsView.setClampScrollOffset(clampScrollOffset));
+ return;
+ }
+ mRecentsView.setClampScrollOffset(clampScrollOffset);
+ }
+
+
+ /**
* Notifies the launcher that the swipe gesture has started. This can be called multiple times.
*/
@UiThread
@@ -919,26 +1034,25 @@
}
/**
- * @param endVelocity The velocity in the direction of the nav bar to the middle of the screen.
- * @param velocity The x and y components of the velocity when the gesture ends.
- * @param downPos The x and y value of where the gesture started.
+ * @param endVelocityPxPerMs The velocity in the direction of the nav bar to the middle of the
+ * screen.
+ * @param velocityPxPerMs The x and y components of the velocity when the gesture ends.
*/
@UiThread
- public void onGestureEnded(float endVelocity, PointF velocity, PointF downPos) {
+ public void onGestureEnded(float endVelocityPxPerMs, PointF velocityPxPerMs) {
float flingThreshold = mContext.getResources()
.getDimension(R.dimen.quickstep_fling_threshold_speed);
boolean isFling = mGestureStarted && !mIsMotionPaused
- && Math.abs(endVelocity) > flingThreshold;
+ && Math.abs(endVelocityPxPerMs) > flingThreshold;
mStateCallback.setStateOnUiThread(STATE_GESTURE_COMPLETED);
- boolean isVelocityVertical = Math.abs(velocity.y) > Math.abs(velocity.x);
+ boolean isVelocityVertical = Math.abs(velocityPxPerMs.y) > Math.abs(velocityPxPerMs.x);
if (isVelocityVertical) {
- mLogDirectionUpOrLeft = velocity.y < 0;
+ mLogDirectionUpOrLeft = velocityPxPerMs.y < 0;
} else {
- mLogDirectionUpOrLeft = velocity.x < 0;
+ mLogDirectionUpOrLeft = velocityPxPerMs.x < 0;
}
- mDownPos = downPos;
- Runnable handleNormalGestureEndCallback = () ->
- handleNormalGestureEnd(endVelocity, isFling, velocity, /* isCancel= */ false);
+ Runnable handleNormalGestureEndCallback = () -> handleNormalGestureEnd(
+ endVelocityPxPerMs, isFling, velocityPxPerMs, /* isCancel= */ false);
if (mRecentsView != null) {
mRecentsView.runOnPageScrollsInitialized(handleNormalGestureEndCallback);
} else {
@@ -984,12 +1098,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 +1128,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,84 +1147,106 @@
return false;
}
- private GestureEndTarget calculateEndTarget(PointF velocity, float endVelocity,
- boolean isFlingY, boolean isCancel) {
+ private float dpiFromPx(float pixels) {
+ return Utilities.dpiFromPx(pixels, mContext.getResources().getDisplayMetrics().densityDpi);
+ }
+
+ private GestureEndTarget calculateEndTarget(
+ PointF velocityPxPerMs, float endVelocityPxPerMs, boolean isFlingY, boolean isCancel) {
+ ActiveGestureLog.INSTANCE.addLog(
+ new ActiveGestureLog.CompoundString("calculateEndTarget: velocities=(x=")
+ .append(Float.toString(dpiFromPx(velocityPxPerMs.x)))
+ .append("dp/ms, y=")
+ .append(Float.toString(dpiFromPx(velocityPxPerMs.y)))
+ .append("dp/ms), angle=")
+ .append(Double.toString(Math.toDegrees(Math.atan2(
+ -velocityPxPerMs.y, velocityPxPerMs.x)))));
+
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(velocityPxPerMs, endVelocityPxPerMs);
+ } else {
+ endTarget = calculateEndTargetForNonFling(velocityPxPerMs);
}
- 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;
+ }
+
+ return willGoToNewTask ? NEW_TASK : HOME;
+ }
+
+ private GestureEndTarget calculateEndTargetForNonFling(PointF velocity) {
+ final boolean isScrollingToNewTask = isScrollingToNewTask();
+
+ // 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;
+ }
+ return velocity.y < 0 && mCanSlowSwipeGoHome ? HOME : 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;
+ }
+
+ /**
+ * Sets whether a slow swipe can go to the HOME end target when the user lets go. A slow swipe
+ * for this purpose must meet two criteria:
+ * 1) y-velocity is less than quickstep_fling_threshold_speed
+ * AND
+ * 2) motion pause has not been detected (possibly because
+ * {@link MotionPauseDetector#setDisallowPause} has been called with disallowPause == true)
+ */
+ public void setCanSlowSwipeGoHome(boolean canSlowSwipeGoHome) {
+ mCanSlowSwipeGoHome = canSlowSwipeGoHome;
+ }
+
@UiThread
- private void handleNormalGestureEnd(float endVelocity, boolean isFling, PointF velocity,
- boolean isCancel) {
+ private void handleNormalGestureEnd(
+ float endVelocityPxPerMs, boolean isFling, PointF velocityPxPerMs, boolean isCancel) {
long duration = MAX_SWIPE_DURATION;
float currentShift = mCurrentShift.value;
- final GestureEndTarget endTarget = calculateEndTarget(velocity, endVelocity,
- isFling, isCancel);
+ final GestureEndTarget endTarget = calculateEndTarget(
+ velocityPxPerMs, endVelocityPxPerMs, isFling, isCancel);
// Set the state, but don't notify until the animation completes
mGestureState.setEndTarget(endTarget, false /* isAtomic */);
mAnimationFactory.setEndTarget(endTarget);
@@ -1117,16 +1259,16 @@
duration = Math.min(MAX_SWIPE_DURATION, expectedDuration);
startShift = currentShift;
} else {
- startShift = Utilities.boundToRange(currentShift - velocity.y
+ startShift = Utilities.boundToRange(currentShift - velocityPxPerMs.y
* getSingleFrameMs(mContext) / mTransitionDragLength, 0, mDragLengthFactor);
if (mTransitionDragLength > 0) {
- float distanceToTravel = (endShift - currentShift) * mTransitionDragLength;
+ float distanceToTravel = (endShift - currentShift) * mTransitionDragLength;
- // we want the page's snap velocity to approximately match the velocity at
- // which the user flings, so we scale the duration by a value near to the
- // derivative of the scroll interpolator at zero, ie. 2.
- long baseDuration = Math.round(Math.abs(distanceToTravel / velocity.y));
- duration = Math.min(MAX_SWIPE_DURATION, 2 * baseDuration);
+ // we want the page's snap velocity to approximately match the velocity at
+ // which the user flings, so we scale the duration by a value near to the
+ // derivative of the scroll interpolator at zero, ie. 2.
+ long baseDuration = Math.round(Math.abs(distanceToTravel / velocityPxPerMs.y));
+ duration = Math.min(MAX_SWIPE_DURATION, 2 * baseDuration);
}
}
Interpolator interpolator;
@@ -1175,22 +1317,30 @@
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) {
- mRecentsView.setOnPageTransitionEndCallback(
- () -> mGestureState.setState(STATE_RECENTS_SCROLLING_FINISHED));
- } else {
+ Runnable onPageTransitionEnd = () -> {
mGestureState.setState(STATE_RECENTS_SCROLLING_FINISHED);
+ setClampScrollOffset(false);
+ };
+ if (mRecentsView != null) {
+ ActiveGestureLog.INSTANCE.trackEvent(ActiveGestureErrorDetector.GestureEvent
+ .SET_ON_PAGE_TRANSITION_END_CALLBACK);
+ mRecentsView.setOnPageTransitionEndCallback(onPageTransitionEnd);
+ } else {
+ onPageTransitionEnd.run();
}
- animateToProgress(startShift, endShift, duration, interpolator, endTarget, velocity);
+ animateToProgress(startShift, endShift, duration, interpolator, endTarget, velocityPxPerMs);
}
private void doLogGesture(GestureEndTarget endTarget, @Nullable TaskView targetTask) {
- if (mDp == null || !mDp.isGestureMode || mDownPos == null) {
+ if (mDp == null || !mDp.isGestureMode) {
// We probably never received an animation controller, skip logging.
return;
}
@@ -1235,7 +1385,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
@@ -1262,6 +1412,10 @@
// If we are transitioning to launcher, then listen for the activity to be restarted while
// the transition is in progress
if (mGestureState.getEndTarget().isLauncher) {
+ // This is also called when the launcher is resumed, in order to clear the pending
+ // widgets that have yet to be configured.
+ DragView.removeAllViews(mActivity);
+
TaskStackChangeListeners.getInstance().registerTaskStackListener(
mActivityRestartListener);
@@ -1281,7 +1435,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
@@ -1305,9 +1459,16 @@
mSwipePipToHomeAnimator = createWindowAnimationToPip(
homeAnimFactory, runningTaskTarget, start);
mSwipePipToHomeAnimators[0] = mSwipePipToHomeAnimator;
+ if (mSwipePipToHomeReleaseCheck != null) {
+ mSwipePipToHomeReleaseCheck.setCanRelease(false);
+ }
windowAnim = mSwipePipToHomeAnimators;
} else {
mSwipePipToHomeAnimator = null;
+ if (mSwipePipToHomeReleaseCheck != null) {
+ mSwipePipToHomeReleaseCheck.setCanRelease(true);
+ mSwipePipToHomeReleaseCheck = null;
+ }
windowAnim = createWindowAnimationToHome(start, homeAnimFactory);
windowAnim[0].addAnimatorListener(new AnimationSuccessListener() {
@@ -1330,7 +1491,8 @@
if (windowAnimation == null) {
continue;
}
- windowAnimation.start(mContext, velocityPxPerMs);
+ DeviceProfile dp = mActivity == null ? null : mActivity.getDeviceProfile();
+ windowAnimation.start(mContext, dp, velocityPxPerMs);
mRunningWindowAnim[i] = RunningWindowAnim.wrap(windowAnimation);
}
homeAnimFactory.setSwipeVelocity(velocityPxPerMs.y);
@@ -1390,7 +1552,7 @@
}
}
- private int calculateWindowRotation(RemoteAnimationTargetCompat runningTaskTarget,
+ private int calculateWindowRotation(RemoteAnimationTarget runningTaskTarget,
RecentsOrientedState orientationState) {
if (runningTaskTarget.rotationChange != 0
&& TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) {
@@ -1401,8 +1563,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()
@@ -1419,12 +1582,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
@@ -1487,6 +1655,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;
@@ -1495,7 +1692,10 @@
mRecentsAnimationController.enableInputConsumer();
// Start hiding the divider
- setDividerShown(false /* shown */, true /* immediate */);
+ if (!mIsTransientTaskbar || mTaskbarAlreadyOpen || mIsTaskbarAllAppsOpen
+ || mDividerHiddenBeforeAnimation) {
+ setDividerShown(false /* shown */, true /* immediate */);
+ }
}
private void computeRecentsScrollIfInvisible() {
@@ -1574,7 +1774,6 @@
private void resumeLastTask() {
if (mRecentsAnimationController != null) {
mRecentsAnimationController.finish(false /* toRecents */, null);
- ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", false);
}
doLogGesture(LAST_TASK, null);
reset();
@@ -1623,6 +1822,9 @@
* handler (in case of quick switch).
*/
private void cancelCurrentAnimation() {
+ ActiveGestureLog.INSTANCE.addLog(
+ "AbsSwipeUpHandler.cancelCurrentAnimation",
+ ActiveGestureErrorDetector.GestureEvent.CANCEL_CURRENT_ANIMATION);
mCanceled = true;
mCurrentShift.cancelAnimation();
@@ -1635,8 +1837,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);
}
@@ -1681,10 +1882,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);
@@ -1694,10 +1891,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);
@@ -1710,7 +1903,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
@@ -1719,16 +1911,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();
@@ -1766,19 +1969,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() {
@@ -1790,7 +1993,10 @@
finishRecentsControllerToHome(
() -> mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED));
}
- ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", true);
+ if (mSwipePipToHomeReleaseCheck != null) {
+ mSwipePipToHomeReleaseCheck.setCanRelease(true);
+ mSwipePipToHomeReleaseCheck = null;
+ }
doLogGesture(HOME, mRecentsView == null ? null : mRecentsView.getCurrentPageTaskView());
}
@@ -1834,31 +2040,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;
- }
-
- /**
- * To be called at the end of constructor of subclasses. This calls various methods which can
- * depend on proper class initialization.
- */
- protected void initAfterSubclassConstructor() {
- initTransitionEndpoints(mRemoteTargetHandles[0].getTaskViewSimulator()
- .getOrientationState().getLauncherDeviceProfile());
+ || app.windowConfiguration.getActivityType() == ACTIVITY_TYPE_HOME;
}
protected void performHapticFeedback() {
@@ -1903,6 +2098,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) {
@@ -1972,17 +2170,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.
@@ -2069,7 +2309,46 @@
return scaleProgress;
}
+ /**
+ * Overrides the gesture displacement 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.
+ */
+ @Override
+ protected float overrideDisplacementForTransientTaskbar(float displacement) {
+ if (!mIsTransientTaskbar) {
+ return displacement;
+ }
+
+ if (mTaskbarAlreadyOpen || mIsTaskbarAllAppsOpen) {
+ return displacement;
+ }
+
+ if (displacement < mTaskbarAppWindowThreshold) {
+ return 0;
+ }
+
+ // "Catch up" with the displacement at mTaskbarCatchUpThreshold.
+ if (displacement < mTaskbarCatchUpThreshold) {
+ if (!mHasReachedOverviewThreshold) {
+ setDividerShown(false /* shown */, true /* immediate */);
+ mHasReachedOverviewThreshold = true;
+ }
+ return Utilities.mapToRange(displacement, mTaskbarAppWindowThreshold,
+ mTaskbarCatchUpThreshold, 0, mTaskbarCatchUpThreshold, ACCEL_DEACCEL);
+ }
+
+ return displacement;
+ }
+
private void setDividerShown(boolean shown, boolean immediate) {
+ if (mRecentsAnimationTargets == null) {
+ if (!shown) {
+ mDividerHiddenBeforeAnimation = true;
+ }
+ return;
+ }
if (mDividerAnimator != null) {
mDividerAnimator.cancel();
}
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index 6354282..22eaa97 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,18 @@
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.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,12 +120,17 @@
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);
+ /**
+ * Returns {@code true} iff an ongoing navigational gesture should be cancelled on activity
+ * destroy. Otherwise, the MultiStateCallbacks will be paused until the activity is recreated.
+ */
+ public boolean shouldCancelGestureOnDestroy() {
+ return true;
+ }
+
public abstract ActivityInitListener createActivityInitListener(
Predicate<Boolean> onInitListener);
@@ -141,6 +148,11 @@
}
@Nullable
+ public DesktopVisibilityController getDesktopVisibilityController() {
+ return null;
+ }
+
+ @Nullable
public abstract TaskbarUIController getTaskbarController();
public final boolean isResumed() {
@@ -161,7 +173,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 +195,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,
@@ -482,33 +487,38 @@
if (mIsAttachedToWindow == attached && animate) {
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;
+
+ 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..4cb4665 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,
@@ -94,6 +88,11 @@
}
@Override
+ public boolean shouldCancelGestureOnDestroy() {
+ return false;
+ }
+
+ @Override
public ActivityInitListener createActivityInitListener(
Predicate<Boolean> onInitListener) {
return new ActivityInitListener<>((activity, alreadyOnHome) ->
@@ -120,8 +119,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 +132,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..d4bebea 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;
@@ -57,6 +58,7 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.SpringAnimationBuilder;
@@ -65,12 +67,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 +126,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 +155,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 +165,7 @@
}
@Override
- protected boolean handleTaskAppeared(RemoteAnimationTargetCompat[] appearedTaskTarget) {
+ protected boolean handleTaskAppeared(RemoteAnimationTarget[] appearedTaskTarget) {
if (mActiveAnimationFactory != null
&& mActiveAnimationFactory.handleHomeTaskAppeared(appearedTaskTarget)) {
mActiveAnimationFactory = null;
@@ -279,13 +280,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 +305,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..9d6d68e 100644
--- a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
+++ b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
@@ -16,6 +16,8 @@
package com.android.quickstep;
+import static android.view.RemoteAnimationTarget.MODE_CLOSING;
+
import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE;
import static com.android.launcher3.BaseActivity.INVISIBLE_ALL;
import static com.android.launcher3.BaseActivity.INVISIBLE_BY_PENDING_FLAGS;
@@ -30,23 +32,28 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Handler;
+import android.os.RemoteException;
+import android.util.Log;
import android.util.Pair;
+import android.view.IRemoteAnimationFinishedCallback;
+import android.view.IRemoteAnimationRunner;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
+import android.view.ViewRootImpl;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.window.BackEvent;
+import android.window.BackMotionEvent;
+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.
@@ -66,7 +73,10 @@
*/
public class LauncherBackAnimationController {
private static final int CANCEL_TRANSITION_DURATION = 233;
- private static final float MIN_WINDOW_SCALE = 0.7f;
+ private static final int SCRIM_FADE_DURATION = 233;
+ private static final float MIN_WINDOW_SCALE = 0.85f;
+ private static final float MAX_SCRIM_ALPHA_DARK = 0.8f;
+ private static final float MAX_SCRIM_ALPHA_LIGHT = 0.2f;
private final QuickstepTransitionManager mQuickstepTransitionManager;
private final Matrix mTransformMatrix = new Matrix();
/** The window position at the beginning of the back animation. */
@@ -75,7 +85,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 +94,21 @@
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 IRemoteAnimationFinishedCallback mAnimationFinishedCallback;
+ private BackProgressAnimator mProgressAnimator = new BackProgressAnimator();
+ private SurfaceControl mScrimLayer;
+ private ValueAnimator mScrimAlphaAnimator;
+ private float mScrimAlpha;
public LauncherBackAnimationController(
- BaseQuickstepLauncher launcher,
+ QuickstepLauncher launcher,
QuickstepTransitionManager quickstepTransitionManager) {
mLauncher = launcher;
mQuickstepTransitionManager = quickstepTransitionManager;
@@ -119,32 +134,62 @@
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) {
+ public void onBackProgressed(BackMotionEvent backEvent) {
+ handler.post(() -> {
+ mProgressAnimator.onBackProgressed(backEvent);
+ });
+ }
+
+ @Override
+ public void onBackStarted(BackMotionEvent backEvent) {
+ handler.post(() -> {
startBack(backEvent);
- } else {
- updateBackProgress(mBackProgress, 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);
+ });
+ });
+ }
+ };
+
+ final IRemoteAnimationRunner runner = new IRemoteAnimationRunner.Stub() {
+ @Override
+ public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
+ IRemoteAnimationFinishedCallback finishedCallback) {
+ for (final RemoteAnimationTarget target : apps) {
+ if (MODE_CLOSING == target.mode) {
+ mBackTarget = target;
+ break;
+ }
}
+ mAnimationFinishedCallback = finishedCallback;
}
@Override
- public void onBackStarted() { }
+ public void onAnimationCancelled(boolean isKeyguardOccluded) {}
};
- SystemUiProxy.INSTANCE.get(mLauncher).setBackToLauncherCallback(mBackCallback);
+
+ SystemUiProxy.INSTANCE.get(mLauncher).setBackToLauncherCallback(mBackCallback, runner);
}
private void resetPositionAnimated() {
@@ -170,10 +215,11 @@
if (mBackCallback != null) {
SystemUiProxy.INSTANCE.get(mLauncher).clearBackToLauncherCallback(mBackCallback);
}
+ mProgressAnimator.reset();
mBackCallback = null;
}
- private void startBack(BackEvent backEvent) {
+ private void startBack(BackMotionEvent backEvent) {
mBackInProgress = true;
RemoteAnimationTarget appTarget = backEvent.getDepartingAnimationTarget();
@@ -181,35 +227,63 @@
return;
}
- mTransaction.show(appTarget.leash).apply();
- mTransaction.setAnimationTransaction();
- mBackTarget = new RemoteAnimationTargetCompat(appTarget);
+ mTransaction
+ .show(appTarget.leash)
+ .setAnimationTransaction();
+ mBackTarget = appTarget;
mInitialTouchPos.set(backEvent.getTouchX(), backEvent.getTouchY());
// TODO(b/218916755): Offset start rectangle in multiwindow mode.
- mStartRect.set(mBackTarget.windowConfiguration.getMaxBounds());
+ mStartRect.set(appTarget.windowConfiguration.getMaxBounds());
+ mCurrentRect.set(mStartRect);
+ addScrimLayer();
+ mTransaction.apply();
+ }
+
+ void addScrimLayer() {
+ ViewRootImpl viewRootImpl = mLauncher.getDragLayer().getViewRootImpl();
+ SurfaceControl parent = viewRootImpl != null
+ ? viewRootImpl.getSurfaceControl()
+ : null;
+ boolean isDarkTheme = Utilities.isDarkTheme(mLauncher);
+ mScrimLayer = new SurfaceControl.Builder()
+ .setName("Back to launcher background scrim")
+ .setCallsite("LauncherBackAnimationController")
+ .setColorLayer()
+ .setParent(parent)
+ .setOpaque(false)
+ .setHidden(false)
+ .build();
+ final float[] colorComponents = new float[] { 0f, 0f, 0f };
+ mScrimAlpha = (isDarkTheme)
+ ? MAX_SCRIM_ALPHA_DARK : MAX_SCRIM_ALPHA_LIGHT;
+ mTransaction
+ .setColor(mScrimLayer, colorComponents)
+ .setAlpha(mScrimLayer, mScrimAlpha)
+ .show(mScrimLayer);
+ }
+
+ void removeScrimLayer() {
+ if (mScrimLayer == null) {
+ return;
+ }
+ if (mScrimLayer.isValid()) {
+ mTransaction.remove(mScrimLayer).apply();
+ }
+ mScrimLayer = null;
}
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 +316,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,11 +355,12 @@
mBackProgress, mWindowScaleStartCornerRadius, mWindowScaleEndCornerRadius);
Pair<RectFSpringAnim, AnimatorSet> pair =
mQuickstepTransitionManager.createWallpaperOpenAnimations(
- new RemoteAnimationTargetCompat[]{mBackTarget},
- new RemoteAnimationTargetCompat[]{},
+ new RemoteAnimationTarget[]{mBackTarget},
+ new RemoteAnimationTarget[0],
false /* fromUnlock */,
mCurrentRect,
- cornerRadius);
+ cornerRadius,
+ mBackInProgress /* fromPredictiveBack */);
startTransitionAnimations(pair.first, pair.second);
mLauncher.clearForceInvisibleFlag(INVISIBLE_ALL);
}
@@ -304,7 +376,18 @@
mInitialTouchPos.set(0, 0);
mAnimatorSetInProgress = false;
mSpringAnimationInProgress = false;
- SystemUiProxy.INSTANCE.get(mLauncher).onBackToLauncherAnimationFinished();
+ if (mAnimationFinishedCallback != null) {
+ try {
+ mAnimationFinishedCallback.onAnimationFinished();
+ } catch (RemoteException e) {
+ Log.w("ShellBackPreview", "Failed call onBackAnimationFinished", e);
+ }
+ mAnimationFinishedCallback = null;
+ }
+ if (mScrimAlphaAnimator != null && mScrimAlphaAnimator.isRunning()) {
+ mScrimAlphaAnimator.cancel();
+ mScrimAlphaAnimator = null;
+ }
}
private void startTransitionAnimations(RectFSpringAnim springAnim, AnimatorSet anim) {
@@ -328,9 +411,29 @@
tryFinishBackAnimation();
}
});
+ mScrimAlphaAnimator = new ValueAnimator().ofFloat(1, 0);
+ mScrimAlphaAnimator.addUpdateListener(animation -> {
+ float value = (Float) animation.getAnimatedValue();
+ if (mScrimLayer.isValid()) {
+ mTransaction.setAlpha(mScrimLayer, value * mScrimAlpha);
+ mTransaction.apply();
+ }
+ });
+ mScrimAlphaAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ resetScrim();
+ }
+ });
+ mScrimAlphaAnimator.setDuration(SCRIM_FADE_DURATION).start();
anim.start();
}
+ private void resetScrim() {
+ removeScrimLayer();
+ mScrimAlpha = 0;
+ }
+
private void tryFinishBackAnimation() {
if (!mSpringAnimationInProgress && !mAnimatorSetInProgress) {
finishAnimation();
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..6d767ed 100644
--- a/quickstep/src/com/android/quickstep/MultiStateCallback.java
+++ b/quickstep/src/com/android/quickstep/MultiStateCallback.java
@@ -22,10 +22,16 @@
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;
+import java.util.List;
import java.util.StringJoiner;
import java.util.function.Consumer;
@@ -41,17 +47,32 @@
private final SparseArray<ArrayList<Consumer<Boolean>>> mStateChangeListeners =
new SparseArray<>();
+ @NonNull private final TrackedEventsMapper mTrackedEventsMapper;
+
private final String[] mStateNames;
private int mState = 0;
+ private boolean mCallbacksPaused = false;
+ private final List<Runnable> mPendingCallbacks = new ArrayList<>();
+
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()) {
@@ -61,6 +82,24 @@
}
}
+ /** Pauses callbacks. */
+ public void pauseCallbacks() {
+ mCallbacksPaused = true;
+ }
+
+ /** Immediately queues any callbacks that were pending paused. */
+ public void resumeCallbacks() {
+ if (!mCallbacksPaused) {
+ return;
+ }
+ mCallbacksPaused = false;
+ List<Runnable> queuedCallbacks = new ArrayList<>(mPendingCallbacks);
+ mPendingCallbacks.clear();
+ for (Runnable runnable : queuedCallbacks) {
+ runnable.run();
+ }
+ }
+
/**
* Adds the provided state flags to the global state and executes any callbacks as a result.
*/
@@ -69,7 +108,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;
@@ -80,13 +121,38 @@
if ((mState & state) == state) {
LinkedList<Runnable> callbacks = mCallbacks.valueAt(i);
while (!callbacks.isEmpty()) {
- callbacks.pollFirst().run();
+ Runnable cb = callbacks.pollFirst();
+ if (mCallbacksPaused) {
+ mPendingCallbacks.add(cb);
+ } else {
+ cb.run();
+ }
}
}
}
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.
@@ -112,7 +178,11 @@
if (wasOn != isOn) {
ArrayList<Consumer<Boolean>> listeners = mStateChangeListeners.valueAt(i);
for (Consumer<Boolean> listener : listeners) {
- listener.accept(isOn);
+ if (mCallbacksPaused) {
+ mPendingCallbacks.add(() -> listener.accept(isOn));
+ } else {
+ listener.accept(isOn);
+ }
}
}
}
@@ -174,4 +244,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..99af052 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;
}
@@ -218,7 +231,7 @@
public void onRecentsAnimationStart(RecentsAnimationController controller,
RecentsAnimationTargets targets) {
activityInterface.runOnInitBackgroundStateUI(() ->
- interactionHandler.onGestureEnded(0, new PointF(), new PointF()));
+ interactionHandler.onGestureEnded(0, new PointF()));
cmd.removeListener(this);
}
@@ -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..589459f 100644
--- a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
+++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
@@ -21,19 +21,17 @@
import static android.content.Intent.ACTION_PACKAGE_REMOVED;
import static com.android.launcher3.config.FeatureFlags.SEPARATE_RECENTS_ACTIVITY;
-import static com.android.launcher3.util.PackageManagerHelper.getPackageFilter;
import static com.android.systemui.shared.system.PackageManagerWrapper.ACTION_PREFERRED_ACTIVITY_CHANGED;
import android.content.ActivityNotFoundException;
-import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
+import android.util.Log;
import android.util.SparseIntArray;
import androidx.annotation.NonNull;
@@ -54,9 +52,11 @@
* and provide callers the relevant classes.
*/
public final class OverviewComponentObserver {
- private final BroadcastReceiver mUserPreferenceChangeReceiver =
+ private static final String TAG = "OverviewComponentObserver";
+
+ private final SimpleBroadcastReceiver mUserPreferenceChangeReceiver =
new SimpleBroadcastReceiver(this::updateOverviewTargets);
- private final BroadcastReceiver mOtherHomeAppUpdateReceiver =
+ private final SimpleBroadcastReceiver mOtherHomeAppUpdateReceiver =
new SimpleBroadcastReceiver(this::updateOverviewTargets);
private final Context mContext;
@@ -99,8 +99,7 @@
mConfigChangesMap.append(fallbackComponent.hashCode(), fallbackInfo.configChanges);
} catch (PackageManager.NameNotFoundException ignored) { /* Impossible */ }
- mContext.registerReceiver(mUserPreferenceChangeReceiver,
- new IntentFilter(ACTION_PREFERRED_ACTIVITY_CHANGED));
+ mUserPreferenceChangeReceiver.register(mContext, ACTION_PREFERRED_ACTIVITY_CHANGED);
updateOverviewTargets();
}
@@ -115,11 +114,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) {
@@ -151,7 +145,12 @@
}
}
- if (!mDeviceState.isHomeDisabled() && (defaultHome == null || mIsDefaultHome)) {
+ // TODO(b/258022658): Remove temporary logging.
+ Log.i(TAG, "updateOverviewTargets: mIsHomeDisabled=" + mIsHomeDisabled
+ + ", isDefaultHomeNull=" + (defaultHome == null)
+ + ", mIsDefaultHome=" + mIsDefaultHome);
+
+ if (!mIsHomeDisabled && (defaultHome == null || mIsDefaultHome)) {
// User default home is same as out home app. Use Overview integrated in Launcher.
mActivityInterface = LauncherActivityInterface.INSTANCE;
mIsHomeAndOverviewSame = true;
@@ -178,9 +177,8 @@
unregisterOtherHomeAppUpdateReceiver();
mUpdateRegisteredPackage = defaultHome.getPackageName();
- mContext.registerReceiver(mOtherHomeAppUpdateReceiver, getPackageFilter(
- mUpdateRegisteredPackage, ACTION_PACKAGE_ADDED, ACTION_PACKAGE_CHANGED,
- ACTION_PACKAGE_REMOVED));
+ mOtherHomeAppUpdateReceiver.registerPkgActions(mContext, mUpdateRegisteredPackage,
+ ACTION_PACKAGE_ADDED, ACTION_PACKAGE_CHANGED, ACTION_PACKAGE_REMOVED);
}
}
mOverviewChangeListener.accept(mIsHomeAndOverviewSame);
diff --git a/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java b/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
index 5521020..5f589bf 100644
--- a/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
+++ b/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
@@ -16,11 +16,9 @@
package com.android.quickstep;
import android.annotation.TargetApi;
-import android.app.ActivityManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;
-import android.os.RemoteException;
import android.os.UserManager;
import android.util.Log;
import android.view.ThreadedRenderer;
@@ -62,12 +60,5 @@
// Elevate GPU priority for Quickstep and Remote animations.
ThreadedRenderer.setContextPriority(
ThreadedRenderer.EGL_CONTEXT_PRIORITY_HIGH_IMG);
-
- // Enable binder tracing on system server for calls originating from Launcher
- try {
- ActivityManager.getService().enableBinderTracing();
- } catch (RemoteException e) {
- Log.e(TAG, "Unable to enable binder tracing", e);
- }
}
}
diff --git a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
index 9bd0cb9..54e4a0d 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,64 @@
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:
+ enableTransientTaskbar(true);
+ return response;
+
+ case TestProtocol.REQUEST_DISABLE_TRANSIENT_TASKBAR:
+ enableTransientTaskbar(false);
+ return response;
}
return super.call(method, arg, extras);
@@ -93,4 +165,59 @@
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) {
+ TaskbarActivityContext context = tisBinder.getTaskbarManager().getCurrentActivityContext();
+ if (context == null) {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(NPE_TRANSIENT_TASKBAR, "enableBlockingTimeout: enable=" + enable,
+ new Exception());
+ }
+ } else {
+ context.enableBlockingTimeoutDuringTests(enable);
+ }
+ }
+
+ private void enableTransientTaskbar(boolean enable) {
+ DisplayController.INSTANCE.get(mContext).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..b33ceca 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;
@@ -40,6 +45,8 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.function.Consumer;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
/**
* Manages the recent task list from the system, caching it as necessary.
@@ -49,7 +56,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 +73,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;
@@ -124,14 +131,18 @@
* @return The change id of the current task list
*/
public synchronized int getTasks(boolean loadKeysOnly,
- Consumer<ArrayList<GroupTask>> callback) {
+ Consumer<ArrayList<GroupTask>> callback, Predicate<GroupTask> filter) {
final int requestLoadId = mChangeId;
if (mResultsUi.isValidForRequest(requestLoadId, loadKeysOnly)) {
// The list is up to date, send the callback on the next frame,
// so that requestID can be returned first.
if (callback != null) {
// Copy synchronously as the changeId might change by next frame
- ArrayList<GroupTask> result = copyOf(mResultsUi);
+ // and filter GroupTasks
+ ArrayList<GroupTask> result = mResultsUi.stream().filter(filter)
+ .map(GroupTask::copy)
+ .collect(Collectors.toCollection(ArrayList<GroupTask>::new));
+
mMainThreadExecutor.post(() -> {
callback.accept(result);
});
@@ -151,7 +162,11 @@
mLoadingTasksInBackground = false;
mResultsUi = loadResult;
if (callback != null) {
- ArrayList<GroupTask> result = copyOf(mResultsUi);
+ // filter the tasks if needed before passing them into the callback
+ ArrayList<GroupTask> result = mResultsUi.stream().filter(filter)
+ .map(GroupTask::copy)
+ .collect(Collectors.toCollection(ArrayList<GroupTask>::new));
+
callback.accept(result);
}
});
@@ -253,9 +268,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 +293,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 +326,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 +336,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 +351,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 98824eb..f26189c 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -15,34 +15,35 @@
*/
package com.android.quickstep;
-import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
-import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
+import static android.os.Trace.TRACE_TAG_APP;
+import static android.view.RemoteAnimationTarget.MODE_CLOSING;
+import static android.view.RemoteAnimationTarget.MODE_OPENING;
import static com.android.launcher3.QuickstepTransitionManager.RECENTS_LAUNCH_DURATION;
import static com.android.launcher3.QuickstepTransitionManager.STATUS_BAR_TRANSITION_DURATION;
import static com.android.launcher3.QuickstepTransitionManager.STATUS_BAR_TRANSITION_PRE_DELAY;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.graphics.SysUiScrim.SYSUI_PROGRESS;
-import static com.android.launcher3.testing.shared.TestProtocol.BAD_STATE;
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.util.Log;
+import android.os.Trace;
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;
@@ -82,9 +83,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;
@@ -112,8 +110,6 @@
private @Nullable TaskbarManager mTaskbarManager;
private @Nullable FallbackTaskbarUIController mTaskbarUIController;
- private Configuration mOldConfig;
-
private StateManager<RecentsState> mStateManager;
// Strong refs to runners which are cleared when the activity is destroyed
@@ -138,7 +134,7 @@
SplitSelectStateController controller =
new SplitSelectStateController(this, mHandler, getStateManager(),
- null /* depthController */);
+ /* depthController */ null, getStatsLogManager());
mDragLayer.recreateControllers();
mFallbackRecentsView.init(mActionsView, controller);
@@ -165,7 +161,7 @@
@Override
public void onMultiWindowModeChanged(boolean isInMultiWindowMode, Configuration newConfig) {
- onHandleConfigChanged();
+ onHandleConfigurationChanged();
super.onMultiWindowModeChanged(isInMultiWindowMode, newConfig);
}
@@ -175,11 +171,8 @@
ACTIVITY_TRACKER.handleNewIntent(this);
}
- /**
- * Logic for when device configuration changes (rotation, screen size change, multi-window,
- * etc.)
- */
- protected void onHandleConfigChanged() {
+ @Override
+ protected void onHandleConfigurationChanged() {
initDeviceProfile();
AbstractFloatingView.closeOpenViews(this, true,
@@ -249,9 +242,9 @@
mActivityLaunchAnimationRunner = new RemoteAnimationFactory() {
@Override
- public void onCreateAnimation(int transit, RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets,
- RemoteAnimationTargetCompat[] nonAppTargets, AnimationResult result) {
+ public void onAnimationStart(int transit, RemoteAnimationTarget[] appTargets,
+ RemoteAnimationTarget[] wallpaperTargets,
+ RemoteAnimationTarget[] nonAppTargets, AnimationResult result) {
mHandler.removeCallbacks(mAnimationStartTimeoutRunnable);
AnimatorSet anim = composeRecentsLaunchAnimator(taskView, appTargets,
wallpaperTargets, nonAppTargets);
@@ -269,12 +262,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(
@@ -288,9 +281,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);
@@ -314,7 +307,6 @@
protected void onStart() {
// Set the alpha to 1 before calling super, as it may get set back to 0 due to
// onActivityStart callback.
- Log.d(BAD_STATE, "RecentsActivity onStart mFallbackRecentsView.setContentAlpha(1)");
mFallbackRecentsView.setContentAlpha(1);
super.onStart();
mFallbackRecentsView.updateLocusId();
@@ -341,7 +333,6 @@
mStateManager = new StateManager<>(this, RecentsState.BG_LAUNCHER);
- mOldConfig = new Configuration(getResources().getConfiguration());
initDeviceProfile();
setupViews();
@@ -351,16 +342,6 @@
}
@Override
- public void onConfigurationChanged(Configuration newConfig) {
- int diff = newConfig.diff(mOldConfig);
- if ((diff & (CONFIG_ORIENTATION | CONFIG_SCREEN_SIZE)) != 0) {
- onHandleConfigChanged();
- }
- mOldConfig.setTo(newConfig);
- super.onConfigurationChanged(newConfig);
- }
-
- @Override
public void onStateSetEnd(RecentsState state) {
super.onStateSetEnd(state);
@@ -413,48 +394,39 @@
}
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) {
- AnimatorPlaybackController controller = getStateManager()
- .createAnimationToNewWorkspace(RecentsState.BG_LAUNCHER, HOME_APPEAR_DURATION);
- controller.dispatchOnStart();
+ (transit, appTargets, wallpaperTargets, nonAppTargets, 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) {
- new Transaction().setAlpha(app.leash, 1).apply();
- }
- AnimatorSet anim = new AnimatorSet();
- anim.play(controller.getAnimationPlayer());
- anim.setDuration(HOME_APPEAR_DURATION);
- result.setAnimation(anim, RecentsActivity.this,
- () -> getStateManager().goToState(RecentsState.HOME, false),
- true /* skipFirstFrame */);
- }
- };
+ RemoteAnimationTargets targets = new RemoteAnimationTargets(
+ appTargets, wallpaperTargets, nonAppTargets, MODE_OPENING);
+ for (RemoteAnimationTarget app : targets.apps) {
+ new Transaction().setAlpha(app.leash, 1).apply();
+ }
+ AnimatorSet anim = new AnimatorSet();
+ anim.play(controller.getAnimationPlayer());
+ anim.setDuration(HOME_APPEAR_DURATION);
+ result.setAnimation(anim, RecentsActivity.this,
+ () -> getStateManager().goToState(RecentsState.HOME, false),
+ true /* skipFirstFrame */);
+ };
@Override
protected void collectStateHandlers(List<StateHandler> out) {
@@ -478,6 +450,13 @@
return new RecentsAtomicAnimationFactory<>(this);
}
+ @Override
+ public void dispatchDeviceProfileChanged() {
+ super.dispatchDeviceProfileChanged();
+ Trace.instantForTrack(TRACE_TAG_APP, "RecentsActivity#DeviceProfileChanged",
+ getDeviceProfile().toSmallString());
+ }
+
private AnimatorListenerAdapter resetStateListener() {
return new AnimatorListenerAdapter() {
@Override
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
index 51ae56b..b82ff03 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
@@ -16,7 +16,8 @@
package com.android.quickstep;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
+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;
@@ -28,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;
@@ -85,30 +86,18 @@
@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) {
- // Convert appTargets to type RemoteAnimationTarget for all apps except Home app
- RemoteAnimationTarget[] nonHomeApps = Arrays.stream(appTargets)
- .filter(remoteAnimationTarget ->
- remoteAnimationTarget.activityType != ACTIVITY_TYPE_HOME)
- .map(RemoteAnimationTargetCompat::unwrap)
- .toArray(RemoteAnimationTarget[]::new);
-
- RemoteAnimationTarget[] nonAppTargets = mSystemUiProxy.onGoingToRecentsLegacy(nonHomeApps);
-
- RecentsAnimationTargets targets = new RecentsAnimationTargets(appTargets,
- wallpaperTargets, RemoteAnimationTargetCompat.wrap(nonAppTargets),
- homeContentInsets, minimizedHomeBounds);
mController = new RecentsAnimationController(animationController,
mAllowMinimizeSplitScreen, this::onAnimationFinished);
@@ -116,7 +105,19 @@
Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(),
mController::finishAnimationToApp);
} else {
+ RemoteAnimationTarget[] nonAppTargets =
+ mSystemUiProxy.onGoingToRecentsLegacy(appTargets);
+ if (nonAppTargets == null) {
+ nonAppTargets = new RemoteAnimationTarget[0];
+ }
+ final RecentsAnimationTargets targets = new RecentsAnimationTargets(appTargets,
+ 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);
}
@@ -128,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);
}
@@ -136,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);
}
@@ -158,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);
}
@@ -190,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 4fb7e6b..92d86ba 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -17,15 +17,14 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.content.Intent.ACTION_USER_UNLOCKED;
import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.launcher3.util.DisplayController.CHANGE_ALL;
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;
@@ -43,30 +42,27 @@
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
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_STATUS_BAR_KEYGUARD_SHOWING;
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;
-import android.os.Process;
import android.os.RemoteException;
import android.os.SystemProperties;
-import android.os.UserManager;
import android.provider.Settings;
import android.view.MotionEvent;
import 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.LockedUserState;
+import com.android.launcher3.util.NavigationMode;
import com.android.launcher3.util.SettingsCache;
import com.android.quickstep.TopTaskTracker.CachedTaskInfo;
import com.android.quickstep.util.NavBarPosition;
@@ -111,20 +107,8 @@
private final boolean mIsOneHandedModeSupported;
private boolean mPipIsActive;
- 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 int mGestureBlockingTaskId = -1;
- private Region mExclusionRegion;
+ private @NonNull Region mExclusionRegion = new Region();
private SystemGestureExclusionListenerCompat mExclusionListener;
public RecentsAnimationDeviceState(Context context) {
@@ -148,20 +132,15 @@
runOnDestroy(mRotationTouchHelper::destroy);
}
- // Register for user unlocked if necessary
- mIsUserUnlocked = context.getSystemService(UserManager.class)
- .isUserUnlocked(Process.myUserHandle());
- if (!mIsUserUnlocked) {
- mContext.registerReceiver(mUserUnlockedReceiver,
- new IntentFilter(ACTION_USER_UNLOCKED));
- }
- runOnDestroy(() -> Utilities.unregisterReceiverSafely(mContext, mUserUnlockedReceiver));
-
// Register for exclusion updates
mExclusionListener = new SystemGestureExclusionListenerCompat(mDisplayId) {
@Override
@BinderThread
public void onExclusionChanged(Region region) {
+ if (region == null) {
+ // Don't think this is possible but just in case, don't let it be null.
+ region = new Region();
+ }
// Assignments are atomic, it should be safe on binder thread
mExclusionRegion = region;
}
@@ -312,39 +291,12 @@
}
/**
- * Adds a callback for when a user is unlocked. If the user is already unlocked, this listener
- * will be called back immediately.
- */
- public void runOnUserUnlocked(Runnable action) {
- if (mIsUserUnlocked) {
- action.run();
- } else {
- mUserUnlockedActions.add(action);
- }
- }
-
- /**
- * @return whether the user is unlocked.
- */
- public boolean isUserUnlocked() {
- return mIsUserUnlocked;
- }
-
- /**
* @return whether the user has completed setup wizard
*/
public boolean isUserSetupComplete() {
return mIsUserSetupComplete;
}
- private void notifyUserUnlocked() {
- for (Runnable action : mUserUnlockedActions) {
- action.run();
- }
- mUserUnlockedActions.clear();
- Utilities.unregisterReceiverSafely(mContext, mUserUnlockedReceiver);
- }
-
/**
* Sets the task id where gestures should be blocked
*/
@@ -383,6 +335,7 @@
|| mRotationTouchHelper.isTaskListFrozen();
return canStartWithNavHidden
&& (mSystemUiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) == 0
+ && (mSystemUiStateFlags & SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING) == 0
&& (mSystemUiStateFlags & SYSUI_STATE_QUICK_SETTINGS_EXPANDED) == 0
&& (mSystemUiStateFlags & SYSUI_STATE_MAGNIFICATION_OVERLAP) == 0
&& ((mSystemUiStateFlags & SYSUI_STATE_HOME_DISABLED) == 0
@@ -498,7 +451,7 @@
public boolean isInExclusionRegion(MotionEvent event) {
// mExclusionRegion can change on binder thread, use a local instance here.
Region exclusionRegion = mExclusionRegion;
- return mMode == NO_BUTTON && exclusionRegion != null
+ return mMode == NO_BUTTON
&& exclusionRegion.contains((int) event.getX(), (int) event.getY());
}
@@ -575,19 +528,23 @@
&& ((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));
- pw.println(" isUserUnlocked=" + mIsUserUnlocked);
+ pw.println(" isUserUnlocked=" + LockedUserState.get(mContext).isUserUnlocked());
pw.println(" isOneHandedModeEnabled=" + mIsOneHandedModeEnabled);
pw.println(" isSwipeToNotificationEnabled=" + mIsSwipeToNotificationEnabled);
- pw.println(" deferredGestureRegion=" + mDeferredGestureRegion);
+ pw.println(" deferredGestureRegion=" + mDeferredGestureRegion.getBounds());
+ pw.println(" exclusionRegion=" + mExclusionRegion.getBounds());
pw.println(" pipIsActive=" + mPipIsActive);
mRotationTouchHelper.dump(pw);
}
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
index b6d9016..15e1365 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
@@ -15,11 +15,14 @@
*/
package com.android.quickstep;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.view.RemoteAnimationTarget.MODE_CLOSING;
+import android.app.WindowConfiguration;
import android.graphics.Rect;
+import android.view.RemoteAnimationTarget;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.quickstep.views.DesktopTaskView;
/**
* Extension of {@link RemoteAnimationTargets} with additional information about swipe
@@ -30,8 +33,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;
@@ -41,4 +44,22 @@
public boolean hasTargets() {
return unfilteredApps.length != 0;
}
+
+ /**
+ * Check if target apps contain desktop tasks which have windowing mode set to {@link
+ * WindowConfiguration#WINDOWING_MODE_FREEFORM}
+ *
+ * @return {@code true} if at least one target app is a desktop task
+ */
+ public boolean hasDesktopTasks() {
+ if (!DesktopTaskView.DESKTOP_MODE_SUPPORTED) {
+ return false;
+ }
+ for (RemoteAnimationTarget target : apps) {
+ if (target.windowConfiguration.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/quickstep/src/com/android/quickstep/RecentsFilterState.java b/quickstep/src/com/android/quickstep/RecentsFilterState.java
new file mode 100644
index 0000000..ff6951d
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/RecentsFilterState.java
@@ -0,0 +1,176 @@
+/*
+ * 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.annotation.Nullable;
+
+import com.android.quickstep.util.GroupTask;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.Predicate;
+
+/**
+ * Keeps track of the state of {@code RecentsView}.
+ *
+ * <p> More specifically, used for keeping track of the state of filters applied on tasks
+ * in {@code RecentsView} for multi-instance management.
+ */
+public class RecentsFilterState {
+ // the minimum number of tasks per package present to allow filtering
+ public static final int MIN_FILTERING_TASK_COUNT = 2;
+
+ // default filter that returns true for any input
+ public static final Predicate<GroupTask> DEFAULT_FILTER = (groupTask -> true);
+
+ // the package name to filter recent tasks by
+ @Nullable
+ private String mPackageNameToFilter = null;
+
+ // the callback that gets executed upon filter change
+ @Nullable
+ private Runnable mOnFilterUpdatedListener = null;
+
+ // map maintaining the count for each unique base activity package name currently in the recents
+ @Nullable
+ private Map<String, Integer> mInstanceCountMap;
+
+ /**
+ * Returns {@code true} if {@code RecentsView} filters tasks by some package name.
+ */
+ public boolean isFiltered() {
+ return mPackageNameToFilter != null;
+ }
+
+ /**
+ * Returns the package name that tasks are filtered by.
+ */
+ @Nullable
+ public String getPackageNameToFilter() {
+ return mPackageNameToFilter;
+ }
+
+
+ /**
+ * Sets a listener on any changes to the filter.
+ *
+ * @param callback listener to be executed upon filter updates
+ */
+ public void setOnFilterUpdatedListener(@Nullable Runnable callback) {
+ mOnFilterUpdatedListener = callback;
+ }
+
+ /**
+ * Updates the filter such that tasks are filtered by a certain package name.
+ *
+ * @param packageName package name of the base activity to filter tasks by;
+ * if null, filter is turned off
+ */
+ public void setFilterBy(@Nullable String packageName) {
+ if (Objects.equals(packageName, mPackageNameToFilter)) {
+ return;
+ }
+
+ mPackageNameToFilter = packageName;
+
+ if (mOnFilterUpdatedListener != null) {
+ mOnFilterUpdatedListener.run();
+ }
+ }
+
+ /**
+ * Updates the map of package names to their count in the most recent list of tasks.
+ *
+ * @param groupTaskList the list of tasks that map update is be based on
+ */
+ public void updateInstanceCountMap(List<GroupTask> groupTaskList) {
+ mInstanceCountMap = getInstanceCountMap(groupTaskList);
+ }
+
+ /**
+ * Returns the map of package names to their count in the most recent list of tasks.
+ */
+ @Nullable
+ public Map<String, Integer> getInstanceCountMap() {
+ return mInstanceCountMap;
+ }
+
+ /**
+ * Returns a predicate for filtering out GroupTasks by package name.
+ *
+ * @param packageName package name to filter GroupTasks by
+ * if null, Predicate always returns true.
+ */
+ public static Predicate<GroupTask> getFilter(@Nullable String packageName) {
+ if (packageName == null) {
+ return DEFAULT_FILTER;
+ }
+
+ return (groupTask) -> (groupTask.task2 != null
+ && groupTask.task2.key.getPackageName().equals(packageName))
+ || groupTask.task1.key.getPackageName().equals(packageName);
+ }
+
+ /**
+ * Returns a map of package names to their frequencies in a list of GroupTasks.
+ *
+ * @param groupTasks the list to go through to create the map
+ */
+ public static Map<String, Integer> getInstanceCountMap(List<GroupTask> groupTasks) {
+ Map<String, Integer> instanceCountMap = new HashMap<>();
+
+ for (GroupTask groupTask : groupTasks) {
+ final String firstTaskPkgName = groupTask.task1.key.getPackageName();
+ final String secondTaskPkgName =
+ groupTask.task2 == null ? null : groupTask.task2.key.getPackageName();
+
+ // increment the instance count for the first task's base activity package name
+ incrementOrAddIfNotExists(instanceCountMap, firstTaskPkgName);
+
+ // check if second task is non existent
+ if (secondTaskPkgName != null) {
+ // increment the instance count for the second task's base activity package name
+ incrementOrAddIfNotExists(instanceCountMap, secondTaskPkgName);
+ }
+ }
+
+ return instanceCountMap;
+ }
+
+ /**
+ * Returns true if tasks of provided package name should show filter UI.
+ *
+ * @param taskPackageName package name of the task in question
+ */
+ public boolean shouldShowFilterUI(String taskPackageName) {
+ // number of occurrences in recents overview with the package name of this task
+ int instanceCount = getInstanceCountMap().get(taskPackageName);
+
+ // if the number of occurrences isn't enough make sure tasks can't be filtered by
+ // the package name of this task
+ return !(isFiltered() || instanceCount < MIN_FILTERING_TASK_COUNT);
+ }
+
+ private static void incrementOrAddIfNotExists(Map<String, Integer> map, String pkgName) {
+ if (!map.containsKey(pkgName)) {
+ map.put(pkgName, 0);
+ }
+ map.put(pkgName, map.get(pkgName) + 1);
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index 3074dbb..913f08f 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;
@@ -50,6 +50,7 @@
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.function.Consumer;
+import java.util.function.Predicate;
/**
* Singleton class to load and manage recents model.
@@ -75,7 +76,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);
@@ -95,14 +97,30 @@
}
/**
- * Fetches the list of recent tasks.
+ * Fetches the list of recent tasks. Tasks are ordered by recency, with the latest active tasks
+ * at the end of the list.
*
* @param callback The callback to receive the task plan once its complete or null. This is
* always called on the UI thread.
* @return the request id associated with this call.
*/
public int getTasks(Consumer<ArrayList<GroupTask>> callback) {
- return mTaskList.getTasks(false /* loadKeysOnly */, callback);
+ return mTaskList.getTasks(false /* loadKeysOnly */, callback,
+ RecentsFilterState.DEFAULT_FILTER);
+ }
+
+
+ /**
+ * Fetches the list of recent tasks, based on a filter
+ *
+ * @param callback The callback to receive the task plan once its complete or null. This is
+ * always called on the UI thread.
+ * @param filter Returns true if a GroupTask should be included into the list passed into
+ * callback.
+ * @return the request id associated with this call.
+ */
+ public int getTasks(Consumer<ArrayList<GroupTask>> callback, Predicate<GroupTask> filter) {
+ return mTaskList.getTasks(false /* loadKeysOnly */, callback, filter);
}
/**
@@ -124,8 +142,9 @@
* Checks if a task has been removed or not.
*
* @param callback Receives true if task is removed, false otherwise
+ * @param filter Returns true if GroupTask should be in the list of considerations
*/
- public void isTaskRemoved(int taskId, Consumer<Boolean> callback) {
+ public void isTaskRemoved(int taskId, Consumer<Boolean> callback, Predicate<GroupTask> filter) {
mTaskList.getTasks(true /* loadKeysOnly */, (taskGroups) -> {
for (GroupTask group : taskGroups) {
if (group.containsTask(taskId)) {
@@ -134,7 +153,7 @@
}
}
callback.accept(true);
- });
+ }, filter);
}
@Override
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..9b00dcf 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,7 @@
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 com.android.quickstep.views.DesktopTaskView;
import java.util.ArrayList;
@@ -40,8 +42,8 @@
* Use this constructor if remote targets are split-screen independent
*/
public RemoteTargetGluer(Context context, BaseActivityInterface sizingStrategy,
- RemoteAnimationTargets targets) {
- mRemoteTargetHandles = createHandles(context, sizingStrategy, targets.apps.length);
+ RemoteAnimationTargets targets, boolean forDesktop) {
+ init(context, sizingStrategy, targets.apps.length, forDesktop);
}
/**
@@ -49,15 +51,31 @@
* running tasks
*/
public RemoteTargetGluer(Context context, BaseActivityInterface sizingStrategy) {
+ if (DesktopTaskView.DESKTOP_MODE_SUPPORTED) {
+ // TODO: binder call, only for prototyping. Creating the gluer should be postponed so
+ // we can create it when we have the remote animation targets ready.
+ int desktopTasks = SystemUiProxy.INSTANCE.get(context).getVisibleDesktopTaskCount();
+ if (desktopTasks > 0) {
+ init(context, sizingStrategy, desktopTasks, true /* forDesktop */);
+ return;
+ }
+ }
+
int[] splitIds = TopTaskTracker.INSTANCE.get(context).getRunningSplitTaskIds();
- mRemoteTargetHandles = createHandles(context, sizingStrategy, splitIds.length == 2 ? 2 : 1);
+ init(context, sizingStrategy, splitIds.length == 2 ? 2 : 1, false /* forDesktop */);
+ }
+
+ private void init(Context context, BaseActivityInterface sizingStrategy, int numHandles,
+ boolean forDesktop) {
+ mRemoteTargetHandles = createHandles(context, sizingStrategy, numHandles, forDesktop);
}
private RemoteTargetHandle[] createHandles(Context context,
- BaseActivityInterface sizingStrategy, int numHandles) {
+ BaseActivityInterface sizingStrategy, int numHandles, boolean forDesktop) {
RemoteTargetHandle[] handles = new RemoteTargetHandle[numHandles];
for (int i = 0; i < numHandles; i++) {
TaskViewSimulator tvs = new TaskViewSimulator(context, sizingStrategy);
+ tvs.setIsDesktopTask(forDesktop);
TransformParams transformParams = new TransformParams();
handles[i] = new RemoteTargetHandle(tvs, transformParams);
}
@@ -75,7 +93,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 +118,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 +137,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,
@@ -135,6 +153,24 @@
}
/**
+ * Similar to {@link #assignTargets(RemoteAnimationTargets)}, except this creates distinct
+ * transform params per app in {@code targets.apps} list.
+ */
+ public RemoteTargetHandle[] assignTargetsForDesktop(RemoteAnimationTargets targets) {
+ for (int i = 0; i < mRemoteTargetHandles.length; i++) {
+ RemoteAnimationTarget primaryTaskTarget = targets.apps[i];
+ mRemoteTargetHandles[i].mTransformParams.setTargetSet(
+ createRemoteAnimationTargetsForTaskId(targets, primaryTaskTarget.taskId));
+ mRemoteTargetHandles[i].mTaskViewSimulator.setPreview(primaryTaskTarget, null);
+ }
+ 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
*
* @param targetToExclude Will be excluded from the resulting return value.
@@ -144,11 +180,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,13 +197,39 @@
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);
}
+ /**
+ * Ensures that we only animate one specific app target. Includes ancillary targets such as
+ * home/recents
+ *
+ * @param targets remote animation targets to filter
+ * @param taskId id for a task that we want this remote animation to apply to
+ * @return {@link RemoteAnimationTargets} where app target only includes the app that has the
+ * {@code taskId} that was passed in
+ */
+ private RemoteAnimationTargets createRemoteAnimationTargetsForTaskId(
+ RemoteAnimationTargets targets, int taskId) {
+ RemoteAnimationTarget[] targetApp = null;
+ for (RemoteAnimationTarget targetCompat : targets.unfilteredApps) {
+ if (targetCompat.taskId == taskId) {
+ targetApp = new RemoteAnimationTarget[]{targetCompat};
+ break;
+ }
+ }
+
+ if (targetApp == null) {
+ targetApp = new RemoteAnimationTarget[0];
+ }
+
+ return new RemoteAnimationTargets(targetApp, targets.wallpapers, targets.nonApps,
+ targets.targetMode);
+ }
+
public RemoteTargetHandle[] getRemoteTargetHandles() {
return mRemoteTargetHandles;
}
diff --git a/quickstep/src/com/android/quickstep/RotationTouchHelper.java b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
index 2186a3b..c041201 100644
--- a/quickstep/src/com/android/quickstep/RotationTouchHelper.java
+++ b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
@@ -18,13 +18,14 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Surface.ROTATION_0;
+import static com.android.launcher3.Utilities.isTrackpadMotionEvent;
import static com.android.launcher3.util.DisplayController.CHANGE_ACTIVE_SCREEN;
import static com.android.launcher3.util.DisplayController.CHANGE_ALL;
import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
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 +36,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;
@@ -231,17 +232,33 @@
/**
* @return whether the coordinates of the {@param event} is in the swipe up gesture region.
*/
- public boolean isInSwipeUpTouchRegion(MotionEvent event) {
- return mOrientationTouchTransformer.touchInValidSwipeRegions(event.getX(), event.getY());
+ public boolean isInSwipeUpTouchRegion(MotionEvent event, BaseActivityInterface activity) {
+ boolean inBarArea = mOrientationTouchTransformer.touchInValidSwipeRegions(event.getX(),
+ event.getY());
+ if (inBarArea) {
+ return true;
+ }
+ if (isTrackpadMotionEvent(event)) {
+ return !activity.isResumed();
+ }
+ return false;
}
/**
* @return whether the coordinates of the {@param event} with the given {@param pointerIndex}
* is in the swipe up gesture region.
*/
- public boolean isInSwipeUpTouchRegion(MotionEvent event, int pointerIndex) {
- return mOrientationTouchTransformer.touchInValidSwipeRegions(event.getX(pointerIndex),
- event.getY(pointerIndex));
+ public boolean isInSwipeUpTouchRegion(MotionEvent event, int pointerIndex,
+ BaseActivityInterface activity) {
+ boolean inBarArea = mOrientationTouchTransformer.touchInValidSwipeRegions(
+ event.getX(pointerIndex), event.getY(pointerIndex));
+ if (inBarArea) {
+ return true;
+ }
+ if (isTrackpadMotionEvent(event)) {
+ return !activity.isResumed();
+ }
+ return false;
}
@Override
@@ -321,9 +338,9 @@
if (enable && !mInOverview && !TestProtocol.sDisableSensorRotation) {
// Clear any previous state from sensor manager
mSensorRotation = mCurrentAppRotation;
- mOrientationListener.enable();
+ UI_HELPER_EXECUTOR.execute(mOrientationListener::enable);
} else {
- mOrientationListener.disable();
+ UI_HELPER_EXECUTOR.execute(mOrientationListener::disable);
}
}
diff --git a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
index baeb514..291f835 100644
--- a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
+++ b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
@@ -24,12 +24,14 @@
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;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
@@ -37,11 +39,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 +66,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;
@@ -115,7 +117,9 @@
@UiThread
public void updateDisplacement(float displacement) {
// We are moving in the negative x/y direction
- displacement = -displacement;
+ displacement = overrideDisplacementForTransientTaskbar(-displacement);
+ mCurrentDisplacement = displacement;
+
float shift;
if (displacement > mTransitionDragLength * mDragLengthFactor && mTransitionDragLength > 0) {
shift = mDragLengthFactor;
@@ -128,6 +132,17 @@
}
/**
+ * When Transient Taskbar is enabled, subclasses can override the displacement to keep the app
+ * window at the bottom of the screen while taskbar is being swiped in.
+ * @param displacement The distance the user has swiped up from the bottom of the screen. This
+ * value will be positive unless the user swipe downwards.
+ * @return the overridden displacement.
+ */
+ protected float overrideDisplacementForTransientTaskbar(float displacement) {
+ return displacement;
+ }
+
+ /**
* Called when the value of {@link #mCurrentShift} changes
*/
@UiThread
@@ -335,11 +350,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 c3ed6d1..cf5082f 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,32 +28,38 @@
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
-import android.graphics.Bitmap;
-import android.graphics.Insets;
+import android.content.pm.ShortcutInfo;
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;
+import android.view.IRemoteAnimationRunner;
import android.view.MotionEvent;
import android.view.RemoteAnimationAdapter;
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.internal.util.ScreenshotRequest;
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());
};
@@ -98,34 +108,27 @@
private IPipAnimationListener mPipAnimationListener;
private ISplitScreenListener mSplitScreenListener;
private IStartingWindowListener mStartingWindowListener;
- private ILauncherUnlockAnimationController mPendingLauncherUnlockAnimationController;
+ private ILauncherUnlockAnimationController mLauncherUnlockAnimationController;
private IRecentTasksListener mRecentTasksListener;
- private final ArrayList<RemoteTransitionCompat> mRemoteTransitions = new ArrayList<>();
+ private final LinkedHashMap<RemoteTransition, TransitionFilter> mRemoteTransitions =
+ new LinkedHashMap<>();
+ private IBinder mOriginalTransactionToken = null;
private IOnBackInvokedCallback mBackToLauncherCallback;
+ private IRemoteAnimationRunner mBackToLauncherRunner;
// 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 +174,7 @@
IOneHanded oneHanded, IShellTransitions shellTransitions,
IStartingWindow startingWindow, IRecentTasks recentTasks,
ISysuiUnlockAnimationController sysuiUnlockAnimationController,
- IBackAnimation backAnimation) {
+ IBackAnimation backAnimation, IDesktopMode desktopMode) {
unlinkToDeath();
mSystemUiProxy = proxy;
mPip = pip;
@@ -182,10 +185,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);
@@ -193,29 +197,21 @@
if (mStartingWindowListener != null && mStartingWindow != null) {
setStartingWindowListener(mStartingWindowListener);
}
- if (mPendingLauncherUnlockAnimationController != null
- && mSysuiUnlockAnimationController != null) {
- setLauncherUnlockAnimationController(mPendingLauncherUnlockAnimationController);
- mPendingLauncherUnlockAnimationController = null;
+ 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);
+ setupTransactionQueue();
if (mRecentTasksListener != null && mRecentTasks != null) {
registerRecentTasksListener(mRecentTasksListener);
}
if (mBackAnimation != null && mBackToLauncherCallback != null) {
- setBackToLauncherCallback(mBackToLauncherCallback);
- }
-
- if (mPendingSetNavButtonAlpha != null) {
- mPendingSetNavButtonAlpha.run();
- mPendingSetNavButtonAlpha = null;
+ setBackToLauncherCallback(mBackToLauncherCallback, mBackToLauncherRunner);
}
}
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
@@ -274,31 +270,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) {
@@ -377,31 +348,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 {
@@ -441,14 +387,12 @@
}
@Override
- public void handleImageBundleAsScreenshot(Bundle screenImageBundle, Rect locationInScreen,
- Insets visibleInsets, Task.TaskKey task) {
+ public void takeScreenshot(ScreenshotRequest request) {
if (mSystemUiProxy != null) {
try {
- mSystemUiProxy.handleImageBundleAsScreenshot(screenImageBundle, locationInScreen,
- visibleInsets, task);
+ mSystemUiProxy.takeScreenshot(request);
} catch (RemoteException e) {
- Log.w(TAG, "Failed call handleImageBundleAsScreenshot");
+ Log.w(TAG, "Failed call takeScreenshot");
}
}
}
@@ -483,12 +427,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);
@@ -497,12 +449,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);
}
@@ -510,12 +462,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);
}
@@ -539,6 +496,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
//
@@ -566,15 +536,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");
}
}
}
@@ -582,13 +592,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");
}
@@ -596,25 +606,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");
}
@@ -622,10 +659,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");
}
@@ -648,6 +685,7 @@
*
* @return RemoteAnimationTargets of windows that need to animate but only exist in shell.
*/
+ @Nullable
public RemoteAnimationTarget[] onGoingToRecentsLegacy(RemoteAnimationTarget[] apps) {
if (mSplitScreen != null) {
try {
@@ -659,6 +697,7 @@
return null;
}
+ @Nullable
public RemoteAnimationTarget[] onStartingSplitLegacy(RemoteAnimationTarget[] apps) {
if (mSplitScreen != null) {
try {
@@ -698,24 +737,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");
}
@@ -723,6 +762,50 @@
mRemoteTransitions.remove(remoteTransition);
}
+ /**
+ * Use SystemUI's transaction-queue instead of Launcher's independent one. This is necessary
+ * if Launcher and SystemUI need to coordinate transactions (eg. for shell transitions).
+ */
+ public void shareTransactionQueue() {
+ if (mOriginalTransactionToken == null) {
+ mOriginalTransactionToken = SurfaceControl.Transaction.getDefaultApplyToken();
+ }
+ setupTransactionQueue();
+ }
+
+ /**
+ * Switch back to using Launcher's independent transaction queue.
+ */
+ public void unshareTransactionQueue() {
+ if (mOriginalTransactionToken == null) {
+ return;
+ }
+ SurfaceControl.Transaction.setDefaultApplyToken(mOriginalTransactionToken);
+ mOriginalTransactionToken = null;
+ }
+
+ private void setupTransactionQueue() {
+ if (mOriginalTransactionToken == null) {
+ return;
+ }
+ if (mShellTransitions == null) {
+ SurfaceControl.Transaction.setDefaultApplyToken(mOriginalTransactionToken);
+ return;
+ }
+ final IBinder shellApplyToken;
+ try {
+ shellApplyToken = mShellTransitions.getShellApplyToken();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error getting Shell's apply token", e);
+ return;
+ }
+ if (shellApplyToken == null) {
+ Log.e(TAG, "Didn't receive apply token from Shell");
+ return;
+ }
+ SurfaceControl.Transaction.setDefaultApplyToken(shellApplyToken);
+ }
+
//
// Starting window
//
@@ -761,11 +844,11 @@
controller.dispatchSmartspaceStateToSysui();
}
} catch (RemoteException e) {
- Log.w(TAG, "Failed call setStartingWindowListener", e);
+ Log.w(TAG, "Failed call setLauncherUnlockAnimationController", e);
}
- } else {
- mPendingLauncherUnlockAnimationController = controller;
}
+
+ mLauncherUnlockAnimationController = controller;
}
/**
@@ -814,13 +897,15 @@
//
/** Sets the launcher {@link android.window.IOnBackInvokedCallback} to shell */
- public void setBackToLauncherCallback(IOnBackInvokedCallback callback) {
+ public void setBackToLauncherCallback(IOnBackInvokedCallback callback,
+ IRemoteAnimationRunner runner) {
mBackToLauncherCallback = callback;
+ mBackToLauncherRunner = runner;
if (mBackAnimation == null) {
return;
}
try {
- mBackAnimation.setBackToLauncherCallback(callback);
+ mBackAnimation.setBackToLauncherCallback(callback, runner);
} catch (RemoteException e) {
Log.e(TAG, "Failed call setBackToLauncherCallback", e);
}
@@ -835,6 +920,7 @@
return;
}
mBackToLauncherCallback = null;
+ mBackToLauncherRunner = null;
if (mBackAnimation == null) {
return;
}
@@ -845,20 +931,6 @@
}
}
- /**
- * Notifies shell that all back to launcher animations have finished (including the transition
- * that plays after the gesture is committed and before the app is closed.
- */
- public void onBackToLauncherAnimationFinished() {
- if (mBackAnimation != null) {
- try {
- mBackAnimation.onBackToLauncherAnimationFinished();
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call onBackAnimationFinished", e);
- }
- }
- }
-
public ArrayList<GroupedRecentTaskInfo> getRecentTasks(int numTasks, int userId) {
if (mRecentTasks != null) {
try {
@@ -882,12 +954,48 @@
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);
+ }
+ }
+ }
+
+ /** Call shell to get number of visible freeform tasks */
+ public int getVisibleDesktopTaskCount() {
+ if (mDesktopMode != null) {
+ try {
+ return mDesktopMode.getVisibleTaskCount();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call getVisibleDesktopTaskCount", e);
+ }
+ }
+ return 0;
+ }
}
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index 4c0bde7..0e5aa81 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.ArrayList;
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,48 +156,39 @@
}
@Override
- public void onTasksAppeared(RemoteAnimationTargetCompat[] appearedTaskTargets) {
- RemoteAnimationTargetCompat appearedTaskTarget = appearedTaskTargets[0];
+ public void onTasksAppeared(RemoteAnimationTarget[] appearedTaskTargets) {
+ RemoteAnimationTarget appearedTaskTarget = appearedTaskTargets[0];
BaseActivityInterface activityInterface = mLastGestureState.getActivityInterface();
- // Convert appTargets to type RemoteAnimationTarget for all apps except Home app
- final ArrayList<RemoteAnimationTargetCompat> tmpNonHomeApps = new ArrayList<>();
- final ArrayList<RemoteAnimationTargetCompat> tmpHomeApps = new ArrayList<>();
- for (RemoteAnimationTargetCompat compat : appearedTaskTargets) {
- if (compat.activityType != ACTIVITY_TYPE_HOME) {
- tmpNonHomeApps.add(compat);
- } else {
- tmpHomeApps.add(compat);
+
+ 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.
+ ((RecentsActivity) activityInterface.getCreatedActivity()).startHome();
+ return;
}
}
- RemoteAnimationTarget[] nonHomeApps = tmpNonHomeApps.stream()
- .map(RemoteAnimationTargetCompat::unwrap)
- .toArray(RemoteAnimationTarget[]::new);
- RemoteAnimationTarget[] homeApps = tmpHomeApps.stream()
- .map(RemoteAnimationTargetCompat::unwrap)
- .toArray(RemoteAnimationTarget[]::new);
- if (homeApps.length > 0
- && activityInterface.getCreatedActivity() instanceof RecentsActivity) {
- ((RecentsActivity) activityInterface.getCreatedActivity()).startHome();
- return;
+
+ RemoteAnimationTarget[] nonAppTargets = SystemUiProxy.INSTANCE.get(mCtx)
+ .onStartingSplitLegacy(appearedTaskTargets);
+ if (nonAppTargets == null) {
+ nonAppTargets = new RemoteAnimationTarget[0];
}
-
- RemoteAnimationTarget[] nonAppTargets =
- SystemUiProxy.INSTANCE.getNoCreate().onStartingSplitLegacy(nonHomeApps);
-
- if (ENABLE_QUICKSTEP_LIVE_TILE.get() && activityInterface.isInLiveTileMode()
+ 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();
@@ -214,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();
@@ -235,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.
@@ -261,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);
@@ -275,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) {
@@ -299,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
@@ -331,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/TaskIconCache.java b/quickstep/src/com/android/quickstep/TaskIconCache.java
index dc60875..7c05a10 100644
--- a/quickstep/src/com/android/quickstep/TaskIconCache.java
+++ b/quickstep/src/com/android/quickstep/TaskIconCache.java
@@ -249,12 +249,13 @@
private BitmapInfo getBitmapInfo(Drawable drawable, int userId,
int primaryColor, boolean isInstantApp) {
try (BaseIconFactory bif = getIconFactory()) {
- bif.disableColorExtraction();
bif.setWrapperBackgroundColor(primaryColor);
// User version code O, so that the icon is always wrapped in an adaptive icon container
return bif.createBadgedIconBitmap(drawable,
- new IconOptions().setUser(UserHandle.of(userId)).setInstantApp(isInstantApp));
+ new IconOptions().setUser(UserHandle.of(userId))
+ .setInstantApp(isInstantApp)
+ .setExtractedColor(0));
}
}
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..663525d 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,9 @@
}
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)
+ && !SystemProperties.getBoolean("persist.wm.debug.desktop_mode_2", 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 c28fc9d..1a72e3f 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -15,12 +15,15 @@
*/
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;
import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
+import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
+import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.QuickstepTransitionManager.ANIMATION_DELAY_NAV_FADE_IN;
@@ -35,19 +38,14 @@
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.launcher3.testing.shared.TestProtocol.BAD_STATE;
-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 static com.android.quickstep.views.DesktopTaskView.DESKTOP_MODE_SUPPORTED;
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;
@@ -55,7 +53,7 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Build;
-import android.util.Log;
+import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.view.View;
import android.window.TransitionInfo;
@@ -65,6 +63,7 @@
import com.android.launcher3.BaseActivity;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
@@ -75,9 +74,12 @@
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;
+import com.android.quickstep.views.DesktopTaskView;
import com.android.quickstep.views.GroupedTaskView;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskThumbnailView;
@@ -85,7 +87,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;
@@ -107,7 +108,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;
@@ -137,7 +138,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;
@@ -160,9 +161,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();
@@ -172,7 +173,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);
@@ -183,9 +184,12 @@
// Re-use existing handles
remoteTargetHandles = recentsViewHandles;
} else {
+ boolean forDesktop = DESKTOP_MODE_SUPPORTED && v instanceof DesktopTaskView;
RemoteTargetGluer gluer = new RemoteTargetGluer(v.getContext(),
- recentsView.getSizeStrategy(), targets);
- if (v.containsMultipleTasks()) {
+ recentsView.getSizeStrategy(), targets, forDesktop);
+ if (forDesktop) {
+ remoteTargetHandles = gluer.assignTargetsForDesktop(targets);
+ } else if (v.containsMultipleTasks()) {
remoteTargetHandles = gluer.assignTargetsForSplitScreen(targets, v.getTaskIds());
} else {
remoteTargetHandles = gluer.assignTargets(targets);
@@ -197,10 +201,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;
@@ -255,21 +259,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);
}
}
});
@@ -307,9 +314,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,16 +337,27 @@
Matrix localMti = new Matrix();
localMt.invert(localMti);
mti[i] = localMti;
+
+ // Translations for child thumbnails also get scaled as the parent taskView scales
+ // Add inverse scaling to keep translations the same
+ float translationY = ttv.getTranslationY();
+ float translationX = ttv.getTranslationX();
+ float fullScreenScale =
+ topMostSimulators[i].getTaskViewSimulator().getFullScreenScale();
+ out.addFloat(ttv, VIEW_TRANSLATE_Y, translationY,
+ translationY / fullScreenScale, TOUCH_RESPONSE_INTERPOLATOR);
+ out.addFloat(ttv, VIEW_TRANSLATE_X, translationX,
+ translationX / fullScreenScale, TOUCH_RESPONSE_INTERPOLATOR);
}
- 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]
@@ -370,8 +394,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,17 +415,47 @@
* device is considered in multiWindowMode and things like insets and stuff change
* and calculations have to be adjusted in the animations for that
*/
- public static void composeRecentsSplitLaunchAnimator(int initialTaskId,
- @Nullable PendingIntent initialTaskPendingIntent, int secondTaskId,
- @NonNull TransitionInfo transitionInfo, SurfaceControl.Transaction t,
- @NonNull Runnable finishCallback) {
+ public static void composeRecentsSplitLaunchAnimator(GroupedTaskView launchingTaskView,
+ @NonNull StateManager stateManager, @Nullable DepthController depthController,
+ int initialTaskId, int secondTaskId, @NonNull TransitionInfo transitionInfo,
+ SurfaceControl.Transaction t, @NonNull Runnable finishCallback) {
+ if (launchingTaskView != null) {
+ AnimatorSet animatorSet = new AnimatorSet();
+ animatorSet.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ finishCallback.run();
+ }
+ });
+
+ final RemoteAnimationTarget[] appTargets =
+ RemoteAnimationTargetCompat.wrapApps(transitionInfo, t, null /* leashMap */);
+ final RemoteAnimationTarget[] wallpaperTargets =
+ RemoteAnimationTargetCompat.wrapNonApps(
+ transitionInfo, true /* wallpapers */, t, null /* leashMap */);
+ final RemoteAnimationTarget[] nonAppTargets =
+ RemoteAnimationTargetCompat.wrapNonApps(
+ transitionInfo, false /* wallpapers */, t, null /* leashMap */);
+ final RecentsView recentsView = launchingTaskView.getRecentsView();
+ composeRecentsLaunchAnimator(animatorSet, launchingTaskView,
+ appTargets, wallpaperTargets, nonAppTargets,
+ true, stateManager,
+ recentsView, depthController);
+
+ t.apply();
+ animatorSet.start();
+ return;
+ }
+
// TODO: consider initialTaskPendingIntent
TransitionInfo.Change splitRoot1 = null;
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) {
@@ -414,7 +468,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) {
@@ -448,17 +502,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) {
@@ -481,7 +534,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;
@@ -536,9 +589,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;
@@ -566,34 +619,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);
- if (dp.isTablet) {
- Log.d(BAD_STATE, "TVU composeRecentsLaunchAnimator alpha=" + 0);
- launcherAnim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- Log.d(BAD_STATE, "TVU composeRecentsLaunchAnimator onStart");
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- float alpha = recentsView == null
- ? -1
- : RecentsView.CONTENT_ALPHA.get(recentsView);
- Log.d(BAD_STATE, "TVU composeRecentsLaunchAnimator onCancel, alpha="
- + alpha);
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- Log.d(BAD_STATE, "TVU composeRecentsLaunchAnimator onEnd");
- }
- });
+ // 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);
@@ -629,7 +664,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());
@@ -648,7 +683,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;
@@ -658,9 +693,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;
}
@@ -673,7 +708,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();
});
@@ -682,6 +719,7 @@
public void onAnimationStart(Animator animation) {
if (shown) {
for (SurfaceControl leash : auxiliarySurfaces) {
+ t.setLayer(leash, Integer.MAX_VALUE);
t.setAlpha(leash, 0);
t.show(leash);
}
@@ -693,7 +731,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 42fa86d..d4bf5c7 100644
--- a/quickstep/src/com/android/quickstep/TopTaskTracker.java
+++ b/quickstep/src/com/android/quickstep/TopTaskTracker.java
@@ -20,9 +20,11 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.content.Intent.ACTION_CHOOSER;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
+import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
+import android.annotation.UserIdInt;
import android.app.ActivityManager.RunningTaskInfo;
import android.content.Context;
@@ -31,9 +33,9 @@
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.SplitConfigurationOptions;
+import com.android.launcher3.util.SplitConfigurationOptions.SplitStageInfo;
import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
import com.android.launcher3.util.SplitConfigurationOptions.StageType;
-import com.android.launcher3.util.SplitConfigurationOptions.SplitStageInfo;
import com.android.launcher3.util.TraceHelper;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.Task.TaskKey;
@@ -85,6 +87,19 @@
public void onTaskMovedToFront(RunningTaskInfo taskInfo) {
mOrderedTaskList.removeIf(rto -> rto.taskId == taskInfo.taskId);
mOrderedTaskList.addFirst(taskInfo);
+
+ // Keep the home display's top running task in the first while adding a non-home
+ // display's task to the list, to avoid showing non-home display's task upon going to
+ // Recents animation.
+ if (taskInfo.displayId != DEFAULT_DISPLAY) {
+ final RunningTaskInfo topTaskOnHomeDisplay = mOrderedTaskList.stream()
+ .filter(rto -> rto.displayId == DEFAULT_DISPLAY).findFirst().orElse(null);
+ if (topTaskOnHomeDisplay != null) {
+ mOrderedTaskList.removeIf(rto -> rto.taskId == topTaskOnHomeDisplay.taskId);
+ mOrderedTaskList.addFirst(topTaskOnHomeDisplay);
+ }
+ }
+
if (mOrderedTaskList.size() >= HISTORY_SIZE) {
// If we grow in size, remove the last taskInfo which is not part of the split task.
Iterator<RunningTaskInfo> itr = mOrderedTaskList.descendingIterator();
@@ -268,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 2df9b9c..88df733 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,33 @@
*/
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.Utilities.isTrackpadMotionEvent;
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_QUICK_SETTINGS_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 +50,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 +57,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,24 +69,28 @@
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
+import com.android.app.viewcapture.ViewCapture;
import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.R;
-import com.android.launcher3.testing.shared.ResourceUtils;
import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.provider.RestoreDbTask;
+import com.android.launcher3.statehandlers.DesktopVisibilityController;
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.launcher3.taskbar.TaskbarManager;
import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.shared.ResourceUtils;
import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.tracing.LauncherTraceProto;
import com.android.launcher3.tracing.TouchInteractionServiceProto;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.LockedUserState;
import com.android.launcher3.util.OnboardingPrefs;
import com.android.launcher3.util.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 +104,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 +116,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 +137,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 +170,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,16 +218,12 @@
@BinderThread
@Override
- public void onTip(int actionType, int viewType) {
- // Please delete this method from the interface
- }
-
- @BinderThread
- @Override
- public void onAssistantAvailable(boolean available) {
+ public void onAssistantAvailable(boolean available, boolean longPressHomeEnabled) {
MAIN_EXECUTOR.execute(() -> {
mDeviceState.setAssistantAvailable(available);
TouchInteractionService.this.onAssistantVisibilityChanged();
+ executeForTaskbarManager(() -> mTaskbarManager
+ .onLongPressHomeEnabled(longPressHomeEnabled));
});
}
@@ -238,10 +236,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,18 +255,34 @@
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() {
MAIN_EXECUTOR.execute(ProxyScreenStatusProvider.INSTANCE::onScreenTurnedOn);
}
+ @BinderThread
+ @Override
+ public void onScreenTurningOn() {
+ MAIN_EXECUTOR.execute(ProxyScreenStatusProvider.INSTANCE::onScreenTurningOn);
+ }
+
+ @BinderThread
+ @Override
+ public void onScreenTurningOff() {
+ 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.
*
@@ -395,8 +408,8 @@
mRotationTouchHelper = mDeviceState.getRotationTouchHelper();
// Call runOnUserUnlocked() before any other callbacks to ensure everything is initialized.
- mDeviceState.runOnUserUnlocked(this::onUserUnlocked);
- mDeviceState.runOnUserUnlocked(mTaskbarManager::onUserUnlocked);
+ LockedUserState.get(this).runOnUserUnlocked(this::onUserUnlocked);
+ LockedUserState.get(this).runOnUserUnlocked(mTaskbarManager::onUserUnlocked);
mDeviceState.addNavigationModeChangedCallback(this::onNavigationModeChanged);
ProtoTracer.INSTANCE.get(this).add(this);
@@ -443,7 +456,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());
@@ -454,8 +468,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);
@@ -467,14 +479,14 @@
}
private void resetHomeBounceSeenOnQuickstepEnabledFirstTime() {
- if (!mDeviceState.isUserUnlocked() || mDeviceState.isButtonNavMode()) {
+ if (!LockedUserState.get(this).isUserUnlocked() || mDeviceState.isButtonNavMode()) {
// Skip if not yet unlocked (can't read user shared prefs) or if the current navigation
// mode doesn't have gestures
return;
}
// Reset home bounce seen on quick step enabled for first time
- SharedPreferences sharedPrefs = Utilities.getPrefs(this);
+ SharedPreferences sharedPrefs = LauncherPrefs.getPrefs(this);
if (!sharedPrefs.getBoolean(HAS_ENABLED_QUICKSTEP_ONCE, true)) {
sharedPrefs.edit()
.putBoolean(HAS_ENABLED_QUICKSTEP_ONCE, true)
@@ -493,11 +505,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()
@@ -510,15 +522,28 @@
@UiThread
private void onSystemUiFlagsChanged(int lastSysUIFlags) {
- if (mDeviceState.isUserUnlocked()) {
+ if (LockedUserState.get(this).isUserUnlocked()) {
int systemUiStateFlags = mDeviceState.getSystemUiStateFlags();
SystemUiProxy.INSTANCE.get(this).setLastSystemUiStateFlags(systemUiStateFlags);
mOverviewComponentObserver.onSystemUiStateChanged();
mTaskbarManager.onSystemUiFlagsChanged(systemUiStateFlags);
- boolean wasExpanded = (lastSysUIFlags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) != 0;
- boolean isExpanded =
- (systemUiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) != 0;
+ 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);
+ }
+ }
+
+ int isShadeExpandedFlag =
+ SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED | SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
+ boolean wasExpanded = (lastSysUIFlags & isShadeExpandedFlag) != 0;
+ boolean isExpanded = (systemUiStateFlags & isShadeExpandedFlag) != 0;
if (wasExpanded != isExpanded && isExpanded) {
// End live tile when expanding the notification panel for the first time from
// overview.
@@ -542,7 +567,7 @@
@UiThread
private void onAssistantVisibilityChanged() {
- if (mDeviceState.isUserUnlocked()) {
+ if (LockedUserState.get(this).isUserUnlocked()) {
mOverviewComponentObserver.getActivityInterface().onAssistantVisibilityChanged(
mDeviceState.getAssistantVisibility());
}
@@ -552,7 +577,7 @@
public void onDestroy() {
Log.d(TAG, "Touch service destroyed: user=" + getUserId());
sIsInitialized = false;
- if (mDeviceState.isUserUnlocked()) {
+ if (LockedUserState.get(this).isUserUnlocked()) {
mInputConsumer.unregisterInputConsumer();
mOverviewComponentObserver.onDestroy();
}
@@ -563,7 +588,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;
@@ -586,7 +611,7 @@
TestLogging.recordMotionEvent(
TestProtocol.SEQUENCE_TIS, "TouchInteractionService.onInputEvent", event);
- if (!mDeviceState.isUserUnlocked()) {
+ if (!LockedUserState.get(this).isUserUnlocked()) {
return;
}
@@ -598,7 +623,8 @@
mRotationTouchHelper.setOrientationTransformIfNeeded(event);
if (!mDeviceState.isOneHandedModeActive()
- && mRotationTouchHelper.isInSwipeUpTouchRegion(event)) {
+ && mRotationTouchHelper.isInSwipeUpTouchRegion(event,
+ mOverviewComponentObserver.getActivityInterface())) {
// Clone the previous gesture state since onConsumerAboutToBeSwitched might trigger
// onConsumerInactive and wipe the previous gesture state
GestureState prevGestureState = new GestureState(mGestureState);
@@ -607,17 +633,15 @@
mConsumer.onConsumerAboutToBeSwitched();
mGestureState = newGestureState;
mConsumer = newConsumer(prevGestureState, mGestureState, event);
-
- ActiveGestureLog.INSTANCE.addLog("setInputConsumer: " + mConsumer.getName());
mUncheckedConsumer = mConsumer;
- } else if (mDeviceState.isUserUnlocked() && mDeviceState.isFullyGesturalNavMode()
+ } else if (LockedUserState.get(this).isUserUnlocked()
+ && mDeviceState.isFullyGesturalNavMode()
&& mDeviceState.canTriggerAssistantAction(event)) {
mGestureState = createGestureState(mGestureState);
// 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,
@@ -637,12 +661,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;
}
}
@@ -664,87 +693,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()) {
+ if (!LockedUserState.get(this).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) {
+ if (tac != null && canStartSystemGesture) {
+ 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);
}
@@ -752,44 +831,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, mGestureState, 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
@@ -803,23 +932,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);
}
}
@@ -841,32 +992,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);
@@ -894,13 +1076,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;
@@ -912,7 +1102,7 @@
}
private void preloadOverview(boolean fromInit, boolean forSUWAllSet) {
- if (!mDeviceState.isUserUnlocked()) {
+ if (!LockedUserState.get(this).isUserUnlocked()) {
return;
}
@@ -944,7 +1134,7 @@
@Override
public void onConfigurationChanged(Configuration newConfig) {
- if (!mDeviceState.isUserUnlocked()) {
+ if (!LockedUserState.get(this).isUserUnlocked()) {
return;
}
final BaseActivityInterface activityInterface =
@@ -985,7 +1175,7 @@
} else {
// Dump everything
FeatureFlags.dump(pw);
- if (mDeviceState.isUserUnlocked()) {
+ if (LockedUserState.get(this).isUserUnlocked()) {
PluginManagerWrapper.INSTANCE.get(getBaseContext()).dump(pw);
}
mDeviceState.dump(pw);
@@ -1015,22 +1205,39 @@
pw.println("ProtoTrace:");
pw.println(" file=" + ProtoTracer.INSTANCE.get(this).getTraceFile());
if (createdOverviewActivity != null) {
- createdOverviewActivity.getDeviceProfile().dump("", pw);
+ 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 1fef544..b132067 100644
--- a/quickstep/src/com/android/quickstep/ViewUtils.java
+++ b/quickstep/src/com/android/quickstep/ViewUtils.java
@@ -17,6 +17,7 @@
import android.graphics.HardwareRenderer;
import android.os.Handler;
+import android.view.SurfaceControl;
import android.view.View;
import android.view.ViewRootImpl;
@@ -45,12 +46,15 @@
return new FrameHandler(view, onFinishRunnable, canceled).schedule();
}
- private static class FrameHandler implements HardwareRenderer.FrameDrawingCallback {
+ private static class FrameHandler implements HardwareRenderer.FrameDrawingCallback,
+ ViewRootImpl.SurfaceChangedCallback {
final ViewRootImpl mViewRoot;
final Runnable mFinishCallback;
final BooleanSupplier mCancelled;
final Handler mHandler;
+ boolean mSurfaceCallbackRegistered = false;
+ boolean mFinished;
int mDeferFrameCount = 1;
@@ -62,6 +66,23 @@
}
@Override
+ public void surfaceCreated(SurfaceControl.Transaction t) {
+ // Do nothing
+ }
+
+ @Override
+ public void surfaceReplaced(SurfaceControl.Transaction t) {
+ // Do nothing
+ }
+
+ @Override
+ public void surfaceDestroyed() {
+ // If the root view is detached, then the app won't get any scheduled frames so we need
+ // to force-run any pending callbacks
+ finish();
+ }
+
+ @Override
public void onFrameDraw(long frame) {
Utilities.postAsyncCallback(mHandler, this::onFrame);
}
@@ -77,18 +98,35 @@
return;
}
- if (mFinishCallback != null) {
- mFinishCallback.run();
- }
+ finish();
}
private boolean schedule() {
if (mViewRoot != null && mViewRoot.getView() != null) {
+ if (!mSurfaceCallbackRegistered) {
+ mSurfaceCallbackRegistered = true;
+ mViewRoot.addSurfaceChangedCallback(this);
+ }
mViewRoot.registerRtFrameCallback(this);
mViewRoot.getView().invalidate();
return true;
}
return false;
}
+
+ private void finish() {
+ if (mFinished) {
+ return;
+ }
+ mFinished = true;
+ mDeferFrameCount = 0;
+ if (mFinishCallback != null) {
+ mFinishCallback.run();
+ }
+ 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 3734f11..bcaae99 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -15,7 +15,6 @@
*/
package com.android.quickstep.fallback;
-import static com.android.launcher3.testing.shared.TestProtocol.BAD_STATE;
import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
import static com.android.quickstep.fallback.RecentsState.DEFAULT;
import static com.android.quickstep.fallback.RecentsState.HOME;
@@ -27,7 +26,6 @@
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.MotionEvent;
import androidx.annotation.Nullable;
@@ -35,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;
@@ -56,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;
@@ -107,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();
@@ -198,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();
}
}
@@ -212,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);
}
@@ -225,7 +227,6 @@
if (toState == MODAL_TASK) {
setOverviewSelectEnabled(true);
}
- Log.d(BAD_STATE, "FRV onStateTransitionStart setFreezeVisibility=true, toState=" + toState);
setFreezeViewVisibility(true);
}
@@ -237,12 +238,13 @@
}
boolean isOverlayEnabled = finalState == DEFAULT || finalState == MODAL_TASK;
setOverlayEnabled(isOverlayEnabled);
- Log.d(BAD_STATE, "FRV onStateTransitionComplete setFreezeVisibility=false, finalState="
- + finalState);
setFreezeViewVisibility(false);
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/AccessibilityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java
index 0c2c92c..6a36d9f 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java
@@ -29,6 +29,7 @@
import android.view.ViewConfiguration;
import com.android.launcher3.R;
+import com.android.quickstep.GestureState;
import com.android.quickstep.InputConsumer;
import com.android.quickstep.RecentsAnimationDeviceState;
import com.android.quickstep.SystemUiProxy;
@@ -46,6 +47,7 @@
private final VelocityTracker mVelocityTracker;
private final MotionPauseDetector mMotionPauseDetector;
private final RecentsAnimationDeviceState mDeviceState;
+ private final GestureState mGestureState;
private final float mMinGestureDistance;
private final float mMinFlingVelocity;
@@ -55,7 +57,7 @@
private float mTotalY;
public AccessibilityInputConsumer(Context context, RecentsAnimationDeviceState deviceState,
- InputConsumer delegate, InputMonitorCompat inputMonitor) {
+ GestureState gestureState, InputConsumer delegate, InputMonitorCompat inputMonitor) {
super(delegate, inputMonitor);
mContext = context;
mVelocityTracker = VelocityTracker.obtain();
@@ -63,6 +65,7 @@
.getDimension(R.dimen.accessibility_gesture_min_swipe_distance);
mMinFlingVelocity = ViewConfiguration.get(context).getScaledMinimumFlingVelocity();
mDeviceState = deviceState;
+ mGestureState = gestureState;
mMotionPauseDetector = new MotionPauseDetector(context);
}
@@ -99,8 +102,8 @@
case ACTION_POINTER_DOWN: {
if (mState == STATE_INACTIVE) {
int pointerIndex = ev.getActionIndex();
- if (mDeviceState.getRotationTouchHelper()
- .isInSwipeUpTouchRegion(ev, pointerIndex)
+ if (mDeviceState.getRotationTouchHelper().isInSwipeUpTouchRegion(ev,
+ pointerIndex, mGestureState.getActivityInterface())
&& mDelegate.allowInterceptByParent()) {
setActive(ev);
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
index 6bc24f2..42a74d9 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
@@ -37,14 +37,15 @@
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;
+import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.util.DisplayController;
-import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.GestureState;
import com.android.quickstep.InputConsumer;
import com.android.quickstep.MultiStateCallback;
@@ -52,14 +53,14 @@
import com.android.quickstep.RecentsAnimationController;
import com.android.quickstep.RecentsAnimationDeviceState;
import com.android.quickstep.RecentsAnimationTargets;
+import com.android.quickstep.RemoteAnimationTargets;
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;
@@ -153,7 +154,8 @@
if (!mThresholdCrossed) {
// Cancel interaction in case of multi-touch interaction
int ptrIdx = ev.getActionIndex();
- if (!mDeviceState.getRotationTouchHelper().isInSwipeUpTouchRegion(ev, ptrIdx)) {
+ if (!mDeviceState.getRotationTouchHelper().isInSwipeUpTouchRegion(ev, ptrIdx,
+ mGestureState.getActivityInterface())) {
int action = ev.getAction();
ev.setAction(ACTION_CANCEL);
finishTouchTracking(ev);
@@ -226,6 +228,10 @@
mStateCallback.setState(STATE_HANDLER_INVALIDATED);
}
});
+ RemoteAnimationTargets targets = mTransformParams.getTargetSet();
+ if (targets != null) {
+ targets.addReleaseCheck(new DeviceLockedReleaseCheck(animator));
+ }
animator.start();
} else {
mStateCallback.setState(STATE_HANDLER_INVALIDATED);
@@ -290,9 +296,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
@@ -304,4 +310,27 @@
public boolean allowInterceptByParent() {
return !mThresholdCrossed;
}
+
+ private static final class DeviceLockedReleaseCheck extends
+ RemoteAnimationTargets.ReleaseCheck {
+
+ private DeviceLockedReleaseCheck(Animator animator) {
+ setCanRelease(true);
+
+ animator.addListener(new AnimatorListenerAdapter() {
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ super.onAnimationStart(animation);
+ setCanRelease(false);
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ setCanRelease(true);
+ }
+ });
+ }
+ }
}
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/MotionEventsHandler.java b/quickstep/src/com/android/quickstep/inputconsumers/MotionEventsHandler.java
new file mode 100644
index 0000000..54edde6
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/inputconsumers/MotionEventsHandler.java
@@ -0,0 +1,116 @@
+/*
+ * 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.inputconsumers;
+
+import static android.view.MotionEvent.AXIS_GESTURE_X_OFFSET;
+import static android.view.MotionEvent.AXIS_GESTURE_Y_OFFSET;
+import static android.view.MotionEvent.INVALID_POINTER_ID;
+
+import static com.android.launcher3.Utilities.getTrackpadMotionEventScale;
+import static com.android.launcher3.Utilities.isTrackpadMotionEvent;
+
+import android.content.Context;
+import android.graphics.PointF;
+import android.view.MotionEvent;
+
+import com.android.quickstep.util.NavBarPosition;
+
+/**
+ * A motion event handler that can tracks the states of a gesture, whether it's from on-screen
+ * touch or trackpad gesture.
+ */
+public class MotionEventsHandler {
+
+ private final PointF mDownPos = new PointF();
+ private final PointF mLastPos = new PointF();
+ private final int mScale;
+
+ private int mActivePointerId = INVALID_POINTER_ID;
+
+ private float mCurrentTrackpadOffsetX = 0;
+ private float mCurrentTrackpadOffsetY = 0;
+
+ public MotionEventsHandler(Context context) {
+ mScale = getTrackpadMotionEventScale(context);
+ }
+
+ public int getActivePointerId() {
+ return mActivePointerId;
+ }
+
+ public void onActionDown(MotionEvent ev) {
+ mActivePointerId = ev.getPointerId(0);
+ if (isTrackpadMotionEvent(ev)) {
+ mCurrentTrackpadOffsetX = 0;
+ mCurrentTrackpadOffsetY = 0;
+ } else {
+ mDownPos.set(ev.getX(), ev.getY());
+ mLastPos.set(mDownPos);
+ }
+ }
+
+ public void onActionMove(MotionEvent ev) {
+ int pointerIndex = ev.findPointerIndex(mActivePointerId);
+ if (isTrackpadMotionEvent(ev)) {
+ mCurrentTrackpadOffsetX += ev.getAxisValue(AXIS_GESTURE_X_OFFSET, pointerIndex)
+ * mScale;
+ mCurrentTrackpadOffsetY += ev.getAxisValue(AXIS_GESTURE_Y_OFFSET, pointerIndex)
+ * mScale;
+ } else {
+ mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
+ }
+ }
+
+ public void onActionPointerUp(MotionEvent ev) {
+ int ptrIdx = ev.getActionIndex();
+ int ptrId = ev.getPointerId(ptrIdx);
+ if (ptrId == mActivePointerId && !isTrackpadMotionEvent(ev)) {
+ 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);
+ }
+ }
+
+ public float getDisplacement(MotionEvent ev, NavBarPosition mNavBarPosition) {
+ if (mNavBarPosition.isRightEdge()) {
+ if (isTrackpadMotionEvent(ev)) {
+ return mCurrentTrackpadOffsetX;
+ }
+ return ev.getX() - mDownPos.x;
+ } else if (mNavBarPosition.isLeftEdge()) {
+ if (isTrackpadMotionEvent(ev)) {
+ return -mCurrentTrackpadOffsetX;
+ }
+ return mDownPos.x - ev.getX();
+ } else {
+ if (isTrackpadMotionEvent(ev)) {
+ return mCurrentTrackpadOffsetY;
+ }
+ return ev.getY() - mDownPos.y;
+ }
+ }
+
+ public float getDisplacementX(MotionEvent ev) {
+ return isTrackpadMotionEvent(ev) ? mCurrentTrackpadOffsetX : mLastPos.x - mDownPos.x;
+ }
+
+ public float getDisplacementY(MotionEvent ev) {
+ return isTrackpadMotionEvent(ev) ? mCurrentTrackpadOffsetY : mLastPos.y - mDownPos.y;
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index 92d3d23..51c6621 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -26,6 +26,8 @@
import static com.android.launcher3.PagedView.ACTION_MOVE_ALLOW_EASY_FLING;
import static com.android.launcher3.PagedView.DEBUG_FAILED_QUICKSWITCH;
import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
+import static com.android.launcher3.Utilities.getXVelocity;
+import static com.android.launcher3.Utilities.getYVelocity;
import static com.android.launcher3.Utilities.squaredHypot;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.TraceHelper.FLAG_CHECK_FOR_RACE_CONDITIONS;
@@ -38,8 +40,6 @@
import android.content.Intent;
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;
@@ -65,11 +65,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;
@@ -114,9 +112,8 @@
private final FinishImmediatelyHandler mCleanupHandler = new FinishImmediatelyHandler();
private final boolean mIsDeferredDownTarget;
- private final PointF mDownPos = new PointF();
- private final PointF mLastPos = new PointF();
- private int mActivePointerId = INVALID_POINTER_ID;
+
+ private final MotionEventsHandler mMotionEventsHandler;
// Distance after which we start dragging the window.
private final float mTouchSlop;
@@ -135,12 +132,6 @@
// 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 */);
- };
-
public OtherActivityInputConsumer(Context base, RecentsAnimationDeviceState deviceState,
TaskAnimationManager taskAnimationManager, GestureState gestureState,
boolean isDeferredDownTarget, Consumer<OtherActivityInputConsumer> onCompleteCallback,
@@ -151,7 +142,6 @@
mNavBarPosition = mDeviceState.getNavBarPosition();
mTaskAnimationManager = taskAnimationManager;
mGestureState = gestureState;
- mMainThreadHandler = new Handler(Looper.getMainLooper());
mHandlerFactory = handlerFactory;
mActivityInterface = mGestureState.getActivityInterface();
@@ -164,6 +154,7 @@
mVelocityTracker = VelocityTracker.obtain();
mInputMonitorCompat = inputMonitorCompat;
mInputEventReceiver = inputEventReceiver;
+ mMotionEventsHandler = new MotionEventsHandler(base);
boolean continuingPreviousGesture = mTaskAnimationManager.isRecentsAnimationRunning();
mIsDeferredDownTarget = !continuingPreviousGesture && isDeferredDownTarget;
@@ -230,9 +221,7 @@
Object traceToken = TraceHelper.INSTANCE.beginSection(DOWN_EVT,
FLAG_CHECK_FOR_RACE_CONDITIONS);
- mActivePointerId = ev.getPointerId(0);
- mDownPos.set(ev.getX(), ev.getY());
- mLastPos.set(mDownPos);
+ mMotionEventsHandler.onActionDown(ev);
// Start the window animation on down to give more time for launcher to draw if the
// user didn't start the gesture over the back button
@@ -247,34 +236,28 @@
if (!mPassedPilferInputSlop) {
// Cancel interaction in case of multi-touch interaction
int ptrIdx = ev.getActionIndex();
- if (!mRotationTouchHelper.isInSwipeUpTouchRegion(ev, ptrIdx)) {
+ if (!mRotationTouchHelper.isInSwipeUpTouchRegion(ev, ptrIdx,
+ mActivityInterface)) {
forceCancelGesture(ev);
}
}
break;
}
case 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);
- }
+ mMotionEventsHandler.onActionPointerUp(ev);
break;
}
case ACTION_MOVE: {
- int pointerIndex = ev.findPointerIndex(mActivePointerId);
+ int pointerIndex = ev.findPointerIndex(mMotionEventsHandler.getActivePointerId());
if (pointerIndex == INVALID_POINTER_ID) {
break;
}
- mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
- float displacement = getDisplacement(ev);
- float displacementX = mLastPos.x - mDownPos.x;
- float displacementY = mLastPos.y - mDownPos.y;
+
+ mMotionEventsHandler.onActionMove(ev);
+ final float displacement = mMotionEventsHandler.getDisplacement(ev,
+ mNavBarPosition);
+ final float displacementX = mMotionEventsHandler.getDisplacementX(ev);
+ final float displacementY= mMotionEventsHandler.getDisplacementY(ev);
if (!mPassedWindowMoveSlop) {
if (!mIsDeferredDownTarget) {
@@ -291,6 +274,7 @@
float upDist = -displacement;
boolean passedSlop = squaredHypot(displacementX, displacementY)
>= mSquaredTouchSlop;
+
if (!mPassedSlopOnThisGesture && passedSlop) {
mPassedSlopOnThisGesture = true;
}
@@ -335,7 +319,10 @@
}
if (mDeviceState.isFullyGesturalNavMode()) {
- mMotionPauseDetector.setDisallowPause(upDist < mMotionPauseMinDisplacement
+ boolean minSwipeMet = upDist >= Math.max(mMotionPauseMinDisplacement,
+ mInteractionHandler.getThresholdToAllowMotionPause());
+ mInteractionHandler.setCanSlowSwipeGoHome(minSwipeMet);
+ mMotionPauseDetector.setDisallowPause(!minSwipeMet
|| isLikelyToStartNewTask);
mMotionPauseDetector.addPosition(ev);
mInteractionHandler.setIsLikelyToStartNewTask(isLikelyToStartNewTask);
@@ -346,8 +333,8 @@
case ACTION_CANCEL:
case ACTION_UP: {
if (DEBUG_FAILED_QUICKSWITCH && !mPassedWindowMoveSlop) {
- float displacementX = mLastPos.x - mDownPos.x;
- float displacementY = mLastPos.y - mDownPos.y;
+ final float displacementX = mMotionEventsHandler.getDisplacementX(ev);
+ final float displacementY = mMotionEventsHandler.getDisplacementY(ev);
Log.d("Quickswitch", "mPassedWindowMoveSlop=false"
+ " disp=" + squaredHypot(displacementX, displacementY)
+ " slop=" + mSquaredTouchSlop);
@@ -359,7 +346,6 @@
}
private void notifyGestureStarted(boolean isLikelyToStartNewTask) {
- ActiveGestureLog.INSTANCE.addLog("startQuickstep");
if (mInteractionHandler == null) {
return;
}
@@ -373,8 +359,6 @@
}
private void startTouchTrackingForWindowAnimation(long touchTimeMs) {
- ActiveGestureLog.INSTANCE.addLog("startRecentsAnimation");
-
mInteractionHandler = mHandlerFactory.newHandler(mGestureState, touchTimeMs);
mInteractionHandler.setGestureEndCallback(this::onInteractionGestureFinished);
mMotionPauseDetector.setOnMotionPauseListener(mInteractionHandler.getMotionPauseListener());
@@ -407,16 +391,19 @@
mInteractionHandler.onGestureCancelled();
} else {
mVelocityTracker.computeCurrentVelocity(PX_PER_MS);
- float velocityX = mVelocityTracker.getXVelocity(mActivePointerId);
- float velocityY = mVelocityTracker.getYVelocity(mActivePointerId);
- float velocity = mNavBarPosition.isRightEdge()
- ? velocityX
+ int activePointerId = mMotionEventsHandler.getActivePointerId();
+ float velocityXPxPerMs = getXVelocity(mVelocityTracker, ev, activePointerId);
+ float velocityYPxPerMs = getYVelocity(mVelocityTracker, ev, activePointerId);
+ float velocityPxPerMs = mNavBarPosition.isRightEdge()
+ ? velocityXPxPerMs
: mNavBarPosition.isLeftEdge()
- ? -velocityX
- : velocityY;
- mInteractionHandler.updateDisplacement(getDisplacement(ev) - mStartDisplacement);
- mInteractionHandler.onGestureEnded(velocity, new PointF(velocityX, velocityY),
- mDownPos);
+ ? -velocityXPxPerMs
+ : velocityYPxPerMs;
+ mInteractionHandler.updateDisplacement(
+ mMotionEventsHandler.getDisplacement(ev, mNavBarPosition)
+ - mStartDisplacement);
+ mInteractionHandler.onGestureEnded(
+ velocityPxPerMs, new PointF(velocityXPxPerMs, velocityYPxPerMs));
}
} else {
// Since we start touch tracking on DOWN, we may reach this state without actually
@@ -435,13 +422,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 +443,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
@@ -487,16 +466,6 @@
}
}
- private float getDisplacement(MotionEvent ev) {
- if (mNavBarPosition.isRightEdge()) {
- return ev.getX() - mDownPos.x;
- } else if (mNavBarPosition.isLeftEdge()) {
- return mDownPos.x - ev.getX();
- } else {
- return ev.getY() - mDownPos.y;
- }
- }
-
@Override
public boolean allowInterceptByParent() {
return !mPassedPilferInputSlop;
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/ProgressDelegateInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java
index 2462394..45ffa1c 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java
@@ -28,12 +28,12 @@
import android.graphics.Point;
import android.view.MotionEvent;
+import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.touch.SingleAxisSwipeDetector;
import com.android.launcher3.util.DisplayController;
-import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.GestureState;
import com.android.quickstep.InputConsumer;
import com.android.quickstep.MultiStateCallback;
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..385debb 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java
@@ -15,16 +15,28 @@
*/
package com.android.quickstep.inputconsumers;
+import static android.view.MotionEvent.INVALID_POINTER_ID;
+
+import static com.android.launcher3.Utilities.isTrackpadMotionEvent;
import static com.android.launcher3.Utilities.squaredHypot;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_REVISED_THRESHOLDS;
+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 +49,41 @@
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 mTaskbarNavThresholdY;
+ private final boolean mIsTaskbarAllAppsOpen;
+ private boolean mHasPassedTaskbarNavThreshold;
+
+ 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 taskbarNavThreshold = res.getDimensionPixelSize(ENABLE_TASKBAR_REVISED_THRESHOLDS.get()
+ ? R.dimen.taskbar_nav_threshold_v2
+ : R.dimen.taskbar_nav_threshold);
+ int screenHeight = taskbarActivityContext.getDeviceProfile().heightPx;
+ mTaskbarNavThresholdY = screenHeight - taskbarNavThreshold;
+ mIsTaskbarAllAppsOpen =
+ mTaskbarActivityContext != null && mTaskbarActivityContext.isTaskbarAllAppsOpen();
+
+ mIsTransientTaskbar = DisplayController.isTransientTaskbar(context);
mLongPressDetector = new GestureDetector(context, new SimpleOnGestureListener() {
@Override
@@ -58,6 +91,10 @@
onLongPressDetected(motionEvent);
}
});
+
+ mTransitionCallback = mIsTransientTaskbar
+ ? taskbarActivityContext.getTranslationCallbacks()
+ : null;
}
@Override
@@ -71,33 +108,87 @@
if (mState != STATE_ACTIVE) {
mDelegate.onMotionEvent(ev);
- if (mTaskbarActivityContext != null) {
+ // Only show the transient task bar if the touch events are on the screen.
+ if (mTaskbarActivityContext != null && !isTrackpadMotionEvent(ev)) {
final float x = ev.getRawX();
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);
+
+ mHasPassedTaskbarNavThreshold = 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 passedTaskbarNavThreshold = dY < 0
+ && mLastPos.y < mTaskbarNavThresholdY;
+
+ if (!mHasPassedTaskbarNavThreshold && passedTaskbarNavThreshold) {
+ mHasPassedTaskbarNavThreshold = true;
+ mTaskbarActivityContext.onSwipeToUnstashTaskbar();
+ }
+
+ if (dY < 0) {
+ dY = -OverScroll.dampedScroll(-dY, mTaskbarNavThresholdY);
+ if (mTransitionCallback != null && !mIsTaskbarAllAppsOpen) {
+ 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();
+ }
+ mHasPassedTaskbarNavThreshold = false;
break;
}
}
@@ -111,7 +202,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..e0262d0 100644
--- a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
+++ b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
@@ -42,6 +42,7 @@
import android.os.Bundle;
import android.os.VibrationEffect;
import android.os.Vibrator;
+import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.View.AccessibilityDelegate;
@@ -53,12 +54,13 @@
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;
+import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.util.Executors;
-import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.GestureState;
import com.android.quickstep.TouchInteractionService.TISBinder;
import com.android.quickstep.util.TISBindHelper;
@@ -78,6 +80,7 @@
"#Intent;action=com.android.settings.SEARCH_RESULT_TRAMPOLINE;S.:settings:fragment_args_key=gesture_system_navigation_input_summary;S.:settings:show_fragment=com.android.settings.gestures.SystemNavigationGestureSettings;end";
private static final String EXTRA_ACCENT_COLOR_DARK_MODE = "suwColorAccentDark";
private static final String EXTRA_ACCENT_COLOR_LIGHT_MODE = "suwColorAccentLight";
+ private static final String EXTRA_DEVICE_NAME = "suwDeviceName";
private static final float HINT_BOTTOM_FACTOR = 1 - .94f;
@@ -109,7 +112,8 @@
int mode = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
boolean isDarkTheme = mode == Configuration.UI_MODE_NIGHT_YES;
- int accentColor = getIntent().getIntExtra(
+ Intent intent = getIntent();
+ int accentColor = intent.getIntExtra(
isDarkTheme ? EXTRA_ACCENT_COLOR_DARK_MODE : EXTRA_ACCENT_COLOR_LIGHT_MODE,
isDarkTheme ? Color.WHITE : Color.BLACK);
@@ -120,11 +124,12 @@
mContentView = findViewById(R.id.content_view);
mSwipeUpShift = getResources().getDimension(R.dimen.allset_swipe_up_shift);
- boolean isTablet = InvariantDeviceProfile.INSTANCE.get(getApplicationContext())
- .getDeviceProfile(this).isTablet;
TextView subtitle = findViewById(R.id.subtitle);
- subtitle.setText(isTablet
- ? R.string.allset_description_tablet : R.string.allset_description);
+ String suwDeviceName = intent.getStringExtra(EXTRA_DEVICE_NAME);
+ subtitle.setText(getString(
+ R.string.allset_description_generic,
+ !TextUtils.isEmpty(suwDeviceName)
+ ? suwDeviceName : getString(R.string.default_device_name)));
TextView tv = findViewById(R.id.navigation_settings);
tv.setTextColor(accentColor);
@@ -135,14 +140,23 @@
} 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);
+ DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(this).getDeviceProfile(this);
+ 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();
}
@@ -248,9 +262,6 @@
}
private AnimatedFloat createSwipeUpProxy(GestureState state) {
- if (!state.getHomeIntent().getComponent().getPackageName().equals(getPackageName())) {
- return null;
- }
if (state.getRunningTaskId() != getTaskId()) {
return null;
}
diff --git a/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java b/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java
index 437572b..8eb4059 100644
--- a/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java
+++ b/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java
@@ -41,9 +41,9 @@
import androidx.dynamicanimation.animation.SpringForce;
import com.android.launcher3.R;
-import com.android.launcher3.testing.shared.ResourceUtils;
import com.android.launcher3.anim.Interpolators;
-import com.android.quickstep.util.VibratorWrapper;
+import com.android.launcher3.testing.shared.ResourceUtils;
+import com.android.launcher3.util.VibratorWrapper;
/** Forked from platform/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java. */
public class EdgeBackGesturePanel extends View {
diff --git a/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java b/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
index bf7023c..4a70120 100644
--- a/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
+++ b/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
@@ -28,8 +28,8 @@
import androidx.annotation.NonNull;
import androidx.fragment.app.FragmentActivity;
+import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
import com.android.launcher3.logging.StatsLogManager;
import com.android.quickstep.TouchInteractionService.TISBinder;
import com.android.quickstep.interaction.TutorialController.TutorialType;
@@ -63,7 +63,7 @@
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.gesture_tutorial_activity);
- mSharedPrefs = Utilities.getPrefs(this);
+ mSharedPrefs = LauncherPrefs.getPrefs(this);
mStatsLogManager = StatsLogManager.newInstance(getApplicationContext());
Bundle args = savedInstanceState == null ? getIntent().getExtras() : savedInstanceState;
diff --git a/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java b/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
index 4badf30..57874d9 100644
--- a/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
+++ b/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
@@ -16,6 +16,7 @@
package com.android.quickstep.interaction;
import static com.android.launcher3.Utilities.squaredHypot;
+import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.ASSISTANT_COMPLETED;
import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.ASSISTANT_NOT_STARTED_BAD_ANGLE;
import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.ASSISTANT_NOT_STARTED_SWIPE_TOO_SHORT;
@@ -25,7 +26,6 @@
import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION;
import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.OVERVIEW_GESTURE_COMPLETED;
import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.OVERVIEW_NOT_STARTED_TOO_FAR_FROM_EDGE;
-import static com.android.quickstep.util.VibratorWrapper.OVERVIEW_HAPTIC;
import android.animation.ValueAnimator;
import android.content.Context;
@@ -44,14 +44,14 @@
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.launcher3.util.VibratorWrapper;
import com.android.quickstep.util.MotionPauseDetector;
import com.android.quickstep.util.NavBarPosition;
import com.android.quickstep.util.TriggerSwipeUpTouchTracker;
-import com.android.quickstep.util.VibratorWrapper;
import com.android.systemui.shared.system.QuickStepContract;
/** Utility class to handle Home and Assistant gestures. */
diff --git a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
index 6b016ce..05b246b 100644
--- a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
@@ -24,8 +24,8 @@
import android.os.Build;
import com.android.launcher3.R;
+import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.PendingAnimation;
-import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.SwipeUpAnimationLogic;
import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureResult;
import com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult;
diff --git a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
index b70c411..670ee9b 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;
@@ -43,19 +42,21 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
-import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.GestureState;
import com.android.quickstep.OverviewComponentObserver;
import com.android.quickstep.RecentsAnimationDeviceState;
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 {
@@ -358,7 +359,7 @@
};
RectFSpringAnim windowAnim = createWindowAnimationToHome(startShift,
homeAnimFactory)[0];
- windowAnim.start(mContext, velocityPxPerMs);
+ windowAnim.start(mContext, mDp, velocityPxPerMs);
return windowAnim;
}
}
@@ -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..3d5c143 100644
--- a/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
+++ b/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
@@ -16,8 +16,9 @@
package com.android.quickstep.logging;
-import static com.android.launcher3.Utilities.getDevicePrefs;
-import static com.android.launcher3.Utilities.getPrefs;
+import static com.android.launcher3.LauncherPrefs.THEMED_ICONS;
+import static com.android.launcher3.LauncherPrefs.getDevicePrefs;
+import static com.android.launcher3.LauncherPrefs.getPrefs;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_SCREEN_SUGGESTIONS_DISABLED;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_SCREEN_SUGGESTIONS_ENABLED;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NOTIFICATION_DOT_DISABLED;
@@ -39,16 +40,16 @@
import android.util.Xml;
import com.android.launcher3.AutoInstallsLayout;
+import com.android.launcher3.LauncherPrefs;
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 +180,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(LauncherPrefs.get(mContext).get(THEMED_ICONS)
+ ? 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..0a155cb 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;
@@ -325,6 +342,10 @@
return;
}
+ if (mItemInfo == null) {
+ return;
+ }
+
if (mItemInfo.container < 0 || appState == null) {
// Write log on the model thread so that logs do not go out of order
// (for eg: drop comes after drag)
@@ -336,8 +357,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 +441,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 +467,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 +479,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 +517,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 +539,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..0fdd8b5
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java
@@ -0,0 +1,371 @@
+/*
+ * 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, LAUNCHER_DESTROYED,
+
+ /**
+ * 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 LAUNCHER_DESTROYED:
+ errorDetected |= printErrorIfTrue(
+ true,
+ prefix,
+ /* errorMessage= */ "Launcher destroyed mid-gesture",
+ 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/AssistContentRequester.java b/quickstep/src/com/android/quickstep/util/AssistContentRequester.java
index b1e38eb..0ce54ad 100644
--- a/quickstep/src/com/android/quickstep/util/AssistContentRequester.java
+++ b/quickstep/src/com/android/quickstep/util/AssistContentRequester.java
@@ -52,6 +52,7 @@
}
private final IActivityTaskManager mActivityTaskManager;
+ private final String mAttributionTag;
private final String mPackageName;
private final Executor mCallbackExecutor;
private final Executor mSystemInteractionExecutor;
@@ -62,6 +63,7 @@
public AssistContentRequester(Context context) {
mActivityTaskManager = ActivityTaskManager.getService();
+ mAttributionTag = context.getAttributionTag();
mPackageName = context.getApplicationContext().getPackageName();
mCallbackExecutor = Executors.MAIN_EXECUTOR;
mSystemInteractionExecutor = Executors.UI_HELPER_EXECUTOR;
@@ -78,7 +80,8 @@
mSystemInteractionExecutor.execute(() -> {
try {
mActivityTaskManager.requestAssistDataForTask(
- new AssistDataReceiver(callback, this), taskId, mPackageName);
+ new AssistDataReceiver(callback, this), taskId, mPackageName,
+ mAttributionTag);
} catch (RemoteException e) {
Log.e(TAG, "Requesting assist content failed for task: " + taskId, e);
}
diff --git a/quickstep/src/com/android/quickstep/util/BaseDepthController.java b/quickstep/src/com/android/quickstep/util/BaseDepthController.java
new file mode 100644
index 0000000..cecf58d
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/BaseDepthController.java
@@ -0,0 +1,171 @@
+/*
+ * 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.app.WallpaperManager;
+import android.os.IBinder;
+import android.util.FloatProperty;
+import android.view.AttachedSurfaceControl;
+import android.view.SurfaceControl;
+
+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;
+
+/**
+ * Utility class for applying depth effect
+ */
+public class BaseDepthController {
+
+ private static final FloatProperty<BaseDepthController> DEPTH =
+ new FloatProperty<BaseDepthController>("depth") {
+ @Override
+ public void setValue(BaseDepthController depthController, float depth) {
+ depthController.setDepth(depth);
+ }
+
+ @Override
+ public Float get(BaseDepthController depthController) {
+ return depthController.mDepth;
+ }
+ };
+
+ 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.
+ */
+ protected final int mMaxBlurRadius;
+ protected final WallpaperManager mWallpaperManager;
+ protected boolean mCrossWindowBlursEnabled;
+
+ /**
+ * Ratio from 0 to 1, where 0 is fully zoomed out, and 1 is zoomed in.
+ * @see android.service.wallpaper.WallpaperService.Engine#onZoomChanged(float)
+ */
+ private float mDepth;
+
+ protected SurfaceControl mSurface;
+
+ // Hints that there is potentially content behind Launcher and that we shouldn't optimize by
+ // marking the launcher surface as opaque. Only used in certain Launcher states.
+ private boolean mHasContentBehindLauncher;
+ /**
+ * Last blur value, in pixels, that was applied.
+ * For debugging purposes.
+ */
+ protected int mCurrentBlur;
+ /**
+ * If we requested early wake-up offsets to SurfaceFlinger.
+ */
+ protected boolean mInEarlyWakeUp;
+
+ public BaseDepthController(Launcher activity) {
+ 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) {
+ mCrossWindowBlursEnabled = isEnabled;
+ applyDepthAndBlur();
+ }
+
+ public void setHasContentBehindLauncher(boolean hasContentBehindLauncher) {
+ mHasContentBehindLauncher = hasContentBehindLauncher;
+ }
+
+ protected void applyDepthAndBlur() {
+ float depth = mDepth;
+ IBinder windowToken = mLauncher.getRootView().getWindowToken();
+ if (windowToken != null) {
+ // The API's full zoom-out is three times larger than the zoom-out we apply to the
+ // icons. To keep the two consistent throughout the animation while keeping Launcher's
+ // concept of full depth unchanged, we divide the depth by 3 here.
+ mWallpaperManager.setWallpaperZoomOut(windowToken, depth / 3);
+ }
+
+ if (!BlurUtils.supportsBlursOnWindows()) {
+ return;
+ }
+ if (mSurface == null || !mSurface.isValid()) {
+ return;
+ }
+ boolean hasOpaqueBg = mLauncher.getScrimView().isFullyOpaque();
+ boolean isSurfaceOpaque = !mHasContentBehindLauncher && hasOpaqueBg;
+
+ mCurrentBlur = !mCrossWindowBlursEnabled || hasOpaqueBg
+ ? 0 : (int) (depth * mMaxBlurRadius);
+ SurfaceControl.Transaction transaction = new SurfaceControl.Transaction()
+ .setBackgroundBlurRadius(mSurface, mCurrentBlur)
+ .setOpaque(mSurface, isSurfaceOpaque);
+
+ // Set early wake-up flags when we know we're executing an expensive operation, this way
+ // SurfaceFlinger will adjust its internal offsets to avoid jank.
+ boolean wantsEarlyWakeUp = depth > 0 && depth < 1;
+ if (wantsEarlyWakeUp && !mInEarlyWakeUp) {
+ transaction.setEarlyWakeupStart();
+ mInEarlyWakeUp = true;
+ } else if (!wantsEarlyWakeUp && mInEarlyWakeUp) {
+ transaction.setEarlyWakeupEnd();
+ mInEarlyWakeUp = false;
+ }
+
+ AttachedSurfaceControl rootSurfaceControl =
+ mLauncher.getRootView().getRootSurfaceControl();
+ if (rootSurfaceControl != null) {
+ rootSurfaceControl.applyTransactionOnDraw(transaction);
+ }
+ }
+
+ 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);
+ float depthF = depthI / 256f;
+ if (Float.compare(mDepth, depthF) == 0) {
+ return;
+ }
+ mDepth = depthF;
+ applyDepthAndBlur();
+ }
+
+ /**
+ * Sets the specified app target surface to apply the blur to.
+ */
+ protected void setSurface(SurfaceControl surface) {
+ if (mSurface != surface) {
+ mSurface = surface;
+ applyDepthAndBlur();
+ }
+ }
+}
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/ImageActionUtils.java b/quickstep/src/com/android/quickstep/util/ImageActionUtils.java
index 9fe24de..3a1c99b 100644
--- a/quickstep/src/com/android/quickstep/util/ImageActionUtils.java
+++ b/quickstep/src/com/android/quickstep/util/ImageActionUtils.java
@@ -18,6 +18,8 @@
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION;
+import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_OVERVIEW;
+import static android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.THREAD_POOL_EXECUTOR;
@@ -47,7 +49,7 @@
import androidx.core.content.FileProvider;
import com.android.internal.app.ChooserActivity;
-import com.android.internal.util.ScreenshotHelper;
+import com.android.internal.util.ScreenshotRequest;
import com.android.launcher3.BuildConfig;
import com.android.quickstep.SystemUiProxy;
import com.android.systemui.shared.recents.model.Task;
@@ -74,11 +76,17 @@
* Saves screenshot to location determine by SystemUiProxy
*/
public static void saveScreenshot(SystemUiProxy systemUiProxy, Bitmap screenshot,
- Rect screenshotBounds,
- Insets visibleInsets, Task.TaskKey task) {
- systemUiProxy.handleImageBundleAsScreenshot(
- ScreenshotHelper.HardwareBitmapBundler.hardwareBitmapToBundle(screenshot),
- screenshotBounds, visibleInsets, task);
+ Rect screenshotBounds, Insets visibleInsets, Task.TaskKey task) {
+ ScreenshotRequest request =
+ new ScreenshotRequest.Builder(TAKE_SCREENSHOT_PROVIDED_IMAGE, SCREENSHOT_OVERVIEW)
+ .setTopComponent(task.sourceComponent)
+ .setTaskId(task.id)
+ .setUserId(task.userId)
+ .setBitmap(screenshot)
+ .setBoundsOnScreen(screenshotBounds)
+ .setInsets(visibleInsets)
+ .build();
+ systemUiProxy.takeScreenshot(request);
}
/**
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..300c4a1
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/LogUtils.kt
@@ -0,0 +1,33 @@
+/*
+ * 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/ProxyScreenStatusProvider.java b/quickstep/src/com/android/quickstep/util/ProxyScreenStatusProvider.java
index 3777c65..8f79ccf 100644
--- a/quickstep/src/com/android/quickstep/util/ProxyScreenStatusProvider.java
+++ b/quickstep/src/com/android/quickstep/util/ProxyScreenStatusProvider.java
@@ -39,6 +39,16 @@
mListeners.forEach(ScreenListener::onScreenTurnedOn);
}
+ /** Called when the screen is starting to turn on. */
+ public void onScreenTurningOn() {
+ mListeners.forEach(ScreenListener::onScreenTurningOn);
+ }
+
+ /** Called when the screen is starting to turn off. */
+ public void onScreenTurningOff() {
+ mListeners.forEach(ScreenListener::onScreenTurningOff);
+ }
+
@Override
public void addCallback(@NonNull ScreenListener listener) {
mListeners.add(listener);
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/RecentsAtomicAnimationFactory.java b/quickstep/src/com/android/quickstep/util/RecentsAtomicAnimationFactory.java
index ac77c67..1d008da 100644
--- a/quickstep/src/com/android/quickstep/util/RecentsAtomicAnimationFactory.java
+++ b/quickstep/src/com/android/quickstep/util/RecentsAtomicAnimationFactory.java
@@ -15,13 +15,10 @@
*/
package com.android.quickstep.util;
-import static com.android.launcher3.testing.shared.TestProtocol.BAD_STATE;
import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
-import android.util.Log;
import androidx.dynamicanimation.animation.DynamicAnimation;
@@ -30,8 +27,6 @@
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.quickstep.views.RecentsView;
-import java.util.Arrays;
-
public class RecentsAtomicAnimationFactory<ACTIVITY_TYPE extends StatefulActivity, STATE_TYPE>
extends AtomicAnimationFactory<STATE_TYPE> {
@@ -53,27 +48,6 @@
case INDEX_RECENTS_FADE_ANIM:
ObjectAnimator alpha = ObjectAnimator.ofFloat(mActivity.getOverviewPanel(),
RecentsView.CONTENT_ALPHA, values);
- Log.d(BAD_STATE, "RAAF createStateElementAnimation alpha="
- + Arrays.toString(values));
- alpha.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- Log.d(BAD_STATE, "RAAF createStateElementAnimation onStart");
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- RecentsView recent = mActivity.getOverviewPanel();
- float alpha = recent == null ? -1 : RecentsView.CONTENT_ALPHA.get(recent);
- Log.d(BAD_STATE, "RAAF createStateElementAnimation onCancel, alpha="
- + alpha);
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- Log.d(BAD_STATE, "RAAF createStateElementAnimation onEnd");
- }
- });
return alpha;
case INDEX_RECENTS_TRANSLATE_X_ANIM: {
RecentsView rv = mActivity.getOverviewPanel();
diff --git a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
index c459f30..db8c7f2 100644
--- a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
+++ b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
@@ -45,7 +45,7 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.Utilities;
+import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.util.DisplayController;
@@ -139,7 +139,7 @@
public RecentsOrientedState(Context context, BaseActivityInterface sizeStrategy,
IntConsumer rotationChangeListener) {
mContext = context;
- mSharedPrefs = Utilities.getPrefs(context);
+ mSharedPrefs = LauncherPrefs.getPrefs(context);
mOrientationListener = new OrientationEventListener(context) {
@Override
public void onOrientationChanged(int degrees) {
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/RectFSpringAnim.java b/quickstep/src/com/android/quickstep/util/RectFSpringAnim.java
index c4909de..68739ba 100644
--- a/quickstep/src/com/android/quickstep/util/RectFSpringAnim.java
+++ b/quickstep/src/com/android/quickstep/util/RectFSpringAnim.java
@@ -214,7 +214,7 @@
* @param context The activity context.
* @param velocityPxPerMs Velocity of swipe in px/ms.
*/
- public void start(Context context, PointF velocityPxPerMs) {
+ public void start(Context context, @Nullable DeviceProfile profile, PointF velocityPxPerMs) {
// Only tell caller that we ended if both x and y animations have ended.
OnAnimationEndListener onXEndListener = ((animation, canceled, centerX, velocityX) -> {
mRectXAnimEnded = true;
@@ -252,7 +252,13 @@
float minVisibleChange = Math.abs(1f / mStartRect.height());
ResourceProvider rp = DynamicResource.provider(context);
float damping = rp.getFloat(R.dimen.swipe_up_rect_scale_damping_ratio);
- float stiffness = rp.getFloat(R.dimen.swipe_up_rect_scale_stiffness);
+
+ // Increase the stiffness for devices where we want the window size to transform quicker.
+ boolean shouldUseHigherStiffness = profile != null
+ && (profile.isLandscape || profile.isTablet);
+ float stiffness = shouldUseHigherStiffness
+ ? rp.getFloat(R.dimen.swipe_up_rect_scale_higher_stiffness)
+ : rp.getFloat(R.dimen.swipe_up_rect_scale_stiffness);
mRectScaleAnim = new SpringAnimation(this, RECT_SCALE_PROGRESS)
.setSpring(new SpringForce(1f)
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 d2a2f36..681f068 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -22,24 +22,37 @@
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.text.TextUtils;
+import android.os.RemoteException;
+import android.os.UserHandle;
+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 +62,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 +75,82 @@
* 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) {
- mInitialTaskIntent = intent;
+ /**
+ * 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,
+ @Nullable Task alreadyRunningTask) {
+ if (alreadyRunningTask != null) {
+ mInitialTaskId = alreadyRunningTask.key.id;
+ } else {
+ mInitialTaskIntent = intent;
+ mUser = itemInfo.user;
+ }
+
+ 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 +158,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 +176,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,129 +202,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(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);
@@ -291,7 +429,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
@@ -310,9 +448,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;
}
/**
@@ -320,7 +461,7 @@
* chosen
*/
public boolean isSplitSelectActive() {
- return isInitialTaskIntentSet() && mSecondTaskId == INVALID_TASK_ID;
+ return isInitialTaskIntentSet() && !isSecondTaskIntentSet();
}
/**
@@ -328,7 +469,7 @@
* be launched
*/
public boolean isBothSplitAppsConfirmed() {
- return isInitialTaskIntentSet() && mSecondTaskId != INVALID_TASK_ID;
+ return isInitialTaskIntentSet() && isSecondTaskIntentSet();
}
private boolean isInitialTaskIntentSet() {
@@ -338,4 +479,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..e5c74dc
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java
@@ -0,0 +1,139 @@
+/*
+ * 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.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.content.Intent;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.view.View;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.quickstep.views.FloatingTaskView;
+import com.android.quickstep.views.RecentsView;
+import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
+
+/** Handles when the stage split lands on the home screen. */
+public class SplitToWorkspaceController {
+
+ private final Launcher mLauncher;
+ private final DeviceProfile mDP;
+ private final SplitSelectStateController mController;
+
+ private final int mHalfDividerSize;
+
+ public SplitToWorkspaceController(Launcher launcher, SplitSelectStateController controller) {
+ mLauncher = launcher;
+ mDP = mLauncher.getDeviceProfile();
+ mController = controller;
+
+ mHalfDividerSize = mLauncher.getResources().getDimensionPixelSize(
+ R.dimen.multi_window_task_divider_size) / 2;
+ }
+
+ /**
+ * 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;
+ BitmapInfo bitmapInfo;
+ if (tag instanceof WorkspaceItemInfo) {
+ final WorkspaceItemInfo workspaceItemInfo = (WorkspaceItemInfo) tag;
+ intent = workspaceItemInfo.intent;
+ bitmapInfo = workspaceItemInfo.bitmap;
+ } 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;
+ bitmapInfo = appInfo.bitmap;
+ } else {
+ return false;
+ }
+
+ mController.setSecondTask(intent);
+
+ boolean isTablet = mLauncher.getDeviceProfile().isTablet;
+ SplitAnimationTimings timings = AnimUtils.getDeviceSplitToConfirmTimings(isTablet);
+ PendingAnimation pendingAnimation = new PendingAnimation(timings.getDuration());
+
+ Rect firstTaskStartingBounds = new Rect();
+ Rect firstTaskEndingBounds = new Rect();
+ RectF secondTaskStartingBounds = new RectF();
+ Rect secondTaskEndingBounds = new Rect();
+
+ RecentsView recentsView = mLauncher.getOverviewPanel();
+ recentsView.getPagedOrientationHandler().getFinalSplitPlaceholderBounds(mHalfDividerSize,
+ mDP, mController.getActiveSplitStagePosition(), firstTaskEndingBounds,
+ secondTaskEndingBounds);
+
+ FloatingTaskView firstFloatingTaskView = mController.getFirstFloatingTaskView();
+ firstFloatingTaskView.getBoundsOnScreen(firstTaskStartingBounds);
+ firstFloatingTaskView.addConfirmAnimation(pendingAnimation,
+ new RectF(firstTaskStartingBounds), firstTaskEndingBounds,
+ false /* fadeWithThumbnail */, true /* isStagedTask */);
+
+ FloatingTaskView secondFloatingTaskView = FloatingTaskView.getFloatingTaskView(mLauncher,
+ view, null /* thumbnail */, bitmapInfo.newIcon(mLauncher),
+ secondTaskStartingBounds);
+ secondFloatingTaskView.setAlpha(1);
+ secondFloatingTaskView.addConfirmAnimation(pendingAnimation, secondTaskStartingBounds,
+ secondTaskEndingBounds, true /* fadeWithThumbnail */, false /* isStagedTask */);
+
+ pendingAnimation.addListener(new AnimatorListenerAdapter() {
+ private boolean mIsCancelled = false;
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mIsCancelled = true;
+ cleanUp();
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (!mIsCancelled) {
+ mController.launchSplitTasks(aBoolean -> cleanUp());
+ InteractionJankMonitorWrapper.end(
+ InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER);
+ }
+ }
+
+ private void cleanUp() {
+ mLauncher.getDragLayer().removeView(firstFloatingTaskView);
+ mLauncher.getDragLayer().removeView(secondFloatingTaskView);
+ mController.resetState();
+ }
+ });
+ pendingAnimation.buildAnim().start();
+ 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..cd5edab 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,9 +123,8 @@
if (grid.isVerticalBarLayout()) {
for (int i = hotseatIcons.getChildCount() - 1; i >= 0; i--) {
View child = hotseatIcons.getChildAt(i);
- CellLayout.LayoutParams lp =
- ((CellLayout.LayoutParams) child.getLayoutParams());
- addStaggeredAnimationForView(child, lp.cellY + 1, totalRows, duration);
+ CellLayoutLayoutParams lp = ((CellLayoutLayoutParams) child.getLayoutParams());
+ addStaggeredAnimationForView(child, lp.getCellY() + 1, totalRows, duration);
}
} else {
final int hotseatRow, qsbRow;
@@ -193,8 +193,8 @@
// 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());
- addStaggeredAnimationForView(child, lp.cellY + lp.cellVSpan, totalRows, duration);
+ CellLayoutLayoutParams lp = ((CellLayoutLayoutParams) child.getLayoutParams());
+ addStaggeredAnimationForView(child, lp.getCellY() + lp.cellVSpan, totalRows, duration);
}
mAnimators.addListener(new AnimatorListenerAdapter() {
@@ -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/SystemWindowManagerProxy.java b/quickstep/src/com/android/quickstep/util/SystemWindowManagerProxy.java
index 5dc4613..a34888f 100644
--- a/quickstep/src/com/android/quickstep/util/SystemWindowManagerProxy.java
+++ b/quickstep/src/com/android/quickstep/util/SystemWindowManagerProxy.java
@@ -23,6 +23,7 @@
import android.view.WindowManager;
import android.view.WindowMetrics;
+import com.android.internal.policy.SystemBarUtils;
import com.android.launcher3.util.WindowBounds;
import com.android.launcher3.util.window.CachedDisplayInfo;
import com.android.launcher3.util.window.WindowManagerProxy;
@@ -45,6 +46,13 @@
}
@Override
+ protected int getStatusBarHeight(Context context, boolean isPortrait, int statusBarInset) {
+ // See b/264656380, calculate the status bar height manually as the inset in the system
+ // server might not be updated by this point yet causing extra DeviceProfile updates
+ return SystemBarUtils.getStatusBarHeight(context);
+ }
+
+ @Override
public ArrayMap<CachedDisplayInfo, WindowBounds[]> estimateInternalDisplayBounds(
Context displayInfoContext) {
ArrayMap<CachedDisplayInfo, WindowBounds[]> result = new ArrayMap<>();
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..69f9ce3 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,22 +35,22 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.Log;
+import android.view.RemoteAnimationTarget;
import androidx.annotation.NonNull;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds;
import com.android.launcher3.util.TraceHelper;
-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,8 +102,9 @@
private boolean mLayoutValid = false;
private int mOrientationStateId;
private SplitBounds mSplitBounds;
- private boolean mDrawsBelowRecents;
+ private Boolean mDrawsBelowRecents = null;
private boolean mIsGridTask;
+ private boolean mIsDesktopTask;
private int mTaskRectTranslationX;
private int mTaskRectTranslationY;
@@ -145,6 +146,13 @@
if (mDp == null) {
return 1;
}
+
+ if (mIsDesktopTask) {
+ mTaskRect.set(mThumbnailPosition);
+ mPivot.set(mTaskRect.centerX(), mTaskRect.centerY());
+ return 1;
+ }
+
if (mIsGridTask) {
mSizeStrategy.calculateGridTaskSize(mContext, mDp, mTaskRect,
mOrientationState.getOrientationHandler());
@@ -171,8 +179,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 +192,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 +202,7 @@
mStagePosition = mThumbnailPosition.equals(splitInfo.leftTopBounds) ?
STAGE_POSITION_TOP_OR_LEFT :
STAGE_POSITION_BOTTOM_OR_RIGHT;
+ mPositionHelper.setSplitBounds(convertSplitBounds(mSplitBounds), mStagePosition);
}
/**
@@ -224,6 +236,13 @@
}
/**
+ * Sets whether this task is part of desktop tasks in overview.
+ */
+ public void setIsDesktopTask(boolean desktop) {
+ mIsDesktopTask = desktop;
+ }
+
+ /**
* Apply translations on TaskRect's starting location.
*/
public void setTaskRectTranslation(int taskRectTranslationX, int taskRectTranslationY) {
@@ -317,9 +336,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 +405,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 +437,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/VibrationConstants.java b/quickstep/src/com/android/quickstep/util/VibrationConstants.java
new file mode 100644
index 0000000..0f0306e
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/VibrationConstants.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.quickstep.util;
+
+import android.os.VibrationEffect;
+
+public class VibrationConstants {
+ public static final VibrationEffect EFFECT_TEXTURE_TICK =
+ VibrationEffect.createPredefined(VibrationEffect.EFFECT_TEXTURE_TICK);
+}
\ No newline at end of file
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..44aa41a
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
@@ -0,0 +1,486 @@
+/*
+ * 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.LauncherState.NORMAL;
+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.Launcher;
+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 windowing proto 2 is enabled */
+ public static final boolean DESKTOP_IS_PROTO2_ENABLED = SystemProperties.getBoolean(
+ "persist.wm.debug.desktop_mode_2", false);
+
+ /** Flags 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)
+ || DESKTOP_IS_PROTO2_ENABLED;
+
+ 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<>();
+
+ private ShapeDrawable mBackground;
+
+ 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);
+ mBackground = new ShapeDrawable(shape);
+ mBackground.setTint(getResources().getColor(android.R.color.system_neutral2_300,
+ getContext().getTheme()));
+ // TODO(b/244348395): this should be wallpaper
+ setBackground(mBackground);
+
+ 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();
+ Launcher.getLauncher(mActivity).getStateManager().goToState(NORMAL, false /* animated */);
+ 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;
+ if (mFullscreenProgress > 0) {
+ // Don't show background while we are transitioning to/from fullscreen
+ setBackground(null);
+ } else {
+ setBackground(mBackground);
+ }
+ 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 835c9f7..75a8ea2 100644
--- a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
@@ -1,9 +1,8 @@
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.DEACCEL_3;
import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.anim.Interpolators.clampToProgress;
import android.animation.ValueAnimator;
import android.content.Context;
@@ -12,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;
@@ -23,15 +24,17 @@
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;
import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.launcher3.taskbar.TaskbarActivityContext;
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;
@@ -41,7 +44,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.
*
@@ -49,6 +53,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;
@@ -58,6 +86,7 @@
private PagedOrientationHandler mOrientationHandler;
@SplitConfigurationOptions.StagePosition
private int mStagePosition;
+ private final Rect mTmpRect = new Rect();
public FloatingTaskView(Context context) {
this(context, null);
@@ -72,6 +101,9 @@
mActivity = BaseActivity.fromContext(context);
mIsRtl = Utilities.isRtl(getResources());
mFullscreenParams = new FullscreenDrawParams(context);
+
+ mSplitHolderSize = context.getResources().getDimensionPixelSize(
+ R.dimen.split_placeholder_icon_size);
}
@Override
@@ -100,9 +132,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());
}
@@ -129,10 +160,24 @@
}
public void updateInitialPositionForView(View originalView) {
- Rect viewBounds = new Rect(0, 0, originalView.getWidth(), originalView.getHeight());
- Utilities.getBoundsForViewInDragLayer(mActivity.getDragLayer(), originalView, viewBounds,
- false /* ignoreTransform */, null /* recycle */,
- mStartingPosition);
+ if (originalView.getContext() instanceof TaskbarActivityContext) {
+ // If original View is a button on the Taskbar, find the on-screen bounds and calculate
+ // the equivalent bounds in the DragLayer, so we can set the initial position of
+ // this FloatingTaskView and start the split animation at the correct spot.
+ originalView.getBoundsOnScreen(mTmpRect);
+ mStartingPosition.set(mTmpRect);
+ int[] dragLayerPositionRelativeToScreen =
+ mActivity.getDragLayer().getLocationOnScreen();
+ mStartingPosition.offset(
+ -dragLayerPositionRelativeToScreen[0],
+ -dragLayerPositionRelativeToScreen[1]);
+ } else {
+ Rect viewBounds = new Rect(0, 0, originalView.getWidth(), originalView.getHeight());
+ Utilities.getBoundsForViewInDragLayer(mActivity.getDragLayer(), originalView,
+ viewBounds, false /* ignoreTransform */, null /* recycle */,
+ mStartingPosition);
+ }
+
final BaseDragLayer.LayoutParams lp = new BaseDragLayer.LayoutParams(
Math.round(mStartingPosition.width()),
Math.round(mStartingPosition.height()));
@@ -168,6 +213,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;
@@ -185,8 +234,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];
@@ -200,26 +291,47 @@
RectF floatingTaskViewBounds = new RectF();
if (fadeWithThumbnail) {
- animation.addFloat(mSplitPlaceholderView, SplitPlaceholderView.ALPHA_FLOAT,
- 0, 1, ACCEL);
- animation.addFloat(mThumbnailView, LauncherAnimUtils.VIEW_ALPHA,
- 1, 0, DEACCEL_3);
+ // This code block runs for the placeholder view during Overview > OverviewSplitSelect
+ // and for the selected (secondary) thumbnail during OverviewSplitSelect > Confirmed
+
+ // 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() {
- final FloatProp mDx = new FloatProp(0, prop.dX, 0, animDuration, LINEAR);
- final FloatProp mDy = new FloatProp(0, prop.dY, 0, animDuration, LINEAR);
+ // SplitPlaceholderView: rectangle translates and stretches to new position
+ final FloatProp mDx = new FloatProp(0, prop.dX, 0, animDuration,
+ clampToProgress(timings.getStagedRectXInterpolator(),
+ timings.getStagedRectSlideStartOffset(),
+ timings.getStagedRectSlideEndOffset()));
+ final FloatProp mDy = new FloatProp(0, prop.dY, 0, animDuration,
+ clampToProgress(timings.getStagedRectYInterpolator(),
+ timings.getStagedRectSlideStartOffset(),
+ timings.getStagedRectSlideEndOffset()));
final FloatProp mTaskViewScaleX = new FloatProp(1f, prop.finalTaskViewScaleX, 0,
- animDuration, LINEAR);
+ animDuration, clampToProgress(timings.getStagedRectScaleXInterpolator(),
+ timings.getStagedRectSlideStartOffset(),
+ timings.getStagedRectSlideEndOffset()));
final FloatProp mTaskViewScaleY = new FloatProp(1f, prop.finalTaskViewScaleY, 0,
- animDuration, LINEAR);
+ animDuration, clampToProgress(timings.getStagedRectScaleYInterpolator(),
+ timings.getStagedRectSlideStartOffset(),
+ timings.getStagedRectSlideEndOffset()));
@Override
public void onUpdate(float percent, boolean initOnly) {
// Calculate the icon position.
@@ -231,9 +343,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;
@@ -260,6 +382,10 @@
mActivity.getDeviceProfile(), mStagePosition);
}
+ public int getStagePosition() {
+ return mStagePosition;
+ }
+
private static class SplitOverlayProperties {
private final float finalTaskViewScaleX;
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..3f7d677 100644
--- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
@@ -1,9 +1,7 @@
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;
import android.content.Context;
import android.graphics.PointF;
@@ -18,6 +16,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 +24,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 +86,35 @@
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);
+ }
+
+ /**
+ * Sets up an on-click listener and the visibility for show_windows icon on top of each task.
+ */
+ @Override
+ public void setUpShowAllInstancesListener() {
+ // sets up the listener for the left/top task
+ super.setUpShowAllInstancesListener();
+
+ // right/bottom task's base package name
+ String taskPackageName = mTaskIdAttributeContainer[1].getTask().key.getPackageName();
+
+ // icon of the right/bottom task
+ View showWindowsView = findViewById(R.id.show_windows_right);
+ updateFilterCallback(showWindowsView, getFilterUpdateCallback(taskPackageName));
}
@Override
@@ -175,7 +202,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 +217,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 +237,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 +286,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 +331,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 +352,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 8b8558a..cf033a6 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -21,34 +21,36 @@
import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK;
import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
import static com.android.launcher3.LauncherState.SPRING_LOADED;
-import static com.android.launcher3.testing.shared.TestProtocol.BAD_STATE;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.MotionEvent;
import android.view.Surface;
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.statehandlers.DesktopVisibilityController;
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;
+import com.android.quickstep.RotationTouchHelper;
import com.android.quickstep.util.SplitSelectStateController;
+import com.android.systemui.shared.recents.model.Task;
/**
* {@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) {
@@ -68,7 +70,6 @@
public void init(OverviewActionsView actionsView,
SplitSelectStateController splitPlaceholderView) {
super.init(actionsView, splitPlaceholderView);
- Log.d(BAD_STATE, "LauncherRecentsView init setContentAlpha=0");
setContentAlpha(0);
}
@@ -97,7 +98,7 @@
if (recoveryData.getStagedTaskId() == taskId) {
initiateSplitSelect(
getTaskViewByTaskId(recoveryData.getStagedTaskId()),
- recoveryData.getStagePosition()
+ recoveryData.getStagePosition(), recoveryData.getSource()
);
mActivity.finishSplitSelectRecovery();
}
@@ -107,7 +108,6 @@
@Override
public void reset() {
super.reset();
-
setLayoutRotation(Surface.ROTATION_0, Surface.ROTATION_0);
}
@@ -119,7 +119,6 @@
if (toState == OVERVIEW_MODAL_TASK) {
setOverviewSelectEnabled(true);
}
- Log.d(BAD_STATE, "LRV onStateTransitionStart setFreezeVisibility=true, toState=" + toState);
setFreezeViewVisibility(true);
}
@@ -131,8 +130,6 @@
}
boolean isOverlayEnabled = finalState == OVERVIEW || finalState == OVERVIEW_MODAL_TASK;
setOverlayEnabled(isOverlayEnabled);
- Log.d(BAD_STATE, "LRV onStateTransitionComplete setFreezeVisibility=false, finalState="
- + finalState);
setFreezeViewVisibility(false);
if (finalState != OVERVIEW_MODAL_TASK) {
setOverviewSelectEnabled(false);
@@ -153,6 +150,9 @@
& CLEAR_ALL_BUTTON) != 0;
setDisallowScrollToClearAll(!hasClearAllButton);
}
+ if (mActivity.getDesktopVisibilityController() != null) {
+ mActivity.getDesktopVisibilityController().setOverviewStateEnabled(enabled);
+ }
}
@Override
@@ -168,13 +168,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();
}
}
@@ -193,8 +192,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);
}
@@ -208,4 +208,25 @@
protected boolean canLaunchFullscreenTask() {
return !mActivity.isInState(OVERVIEW_SPLIT_SELECT);
}
+
+ @Override
+ public void onGestureAnimationStart(Task[] runningTasks,
+ RotationTouchHelper rotationTouchHelper) {
+ super.onGestureAnimationStart(runningTasks, rotationTouchHelper);
+ DesktopVisibilityController desktopVisibilityController =
+ mActivity.getDesktopVisibilityController();
+ if (desktopVisibilityController != null) {
+ desktopVisibilityController.setGestureInProgress(true);
+ }
+ }
+
+ @Override
+ public void onGestureAnimationEnd() {
+ super.onGestureAnimationEnd();
+ DesktopVisibilityController desktopVisibilityController =
+ mActivity.getDesktopVisibilityController();
+ if (desktopVisibilityController != null) {
+ desktopVisibilityController.setGestureInProgress(false);
+ }
+ }
}
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 3aa24ac..cdc0574 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -35,27 +35,35 @@
import static com.android.launcher3.anim.Interpolators.ACCEL_0_75;
import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
+import static com.android.launcher3.anim.Interpolators.EMPHASIZED_DECELERATE;
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.anim.Interpolators.OVERSHOOT_0_75;
import static com.android.launcher3.anim.Interpolators.clampToProgress;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_LAUNCH_FROM_STAGED_APP;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW;
+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;
@@ -67,9 +75,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;
@@ -97,6 +109,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;
@@ -122,6 +135,7 @@
import com.android.launcher3.PagedView;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
@@ -129,6 +143,7 @@
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;
@@ -138,19 +153,19 @@
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;
import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.TranslateEdgeEffect;
+import com.android.launcher3.util.VibratorWrapper;
import com.android.launcher3.util.ViewPool;
-import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.BaseActivityInterface;
import com.android.quickstep.GestureState;
import com.android.quickstep.RecentsAnimationController;
import com.android.quickstep.RecentsAnimationTargets;
+import com.android.quickstep.RecentsFilterState;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.RemoteAnimationTargets;
import com.android.quickstep.RemoteTargetGluer;
@@ -162,29 +177,33 @@
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;
import com.android.quickstep.util.TransformParams;
-import com.android.quickstep.util.VibratorWrapper;
+import com.android.quickstep.util.VibrationConstants;
import com.android.systemui.plugins.ResourceProvider;
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.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;
@@ -197,7 +216,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;
@@ -265,7 +284,7 @@
Utilities.ATLEAST_S ? VibrationEffect.Composition.PRIMITIVE_LOW_TICK : -1;
public static final float SCROLL_VIBRATION_PRIMITIVE_SCALE = 0.6f;
public static final VibrationEffect SCROLL_VIBRATION_FALLBACK =
- VibratorWrapper.EFFECT_TEXTURE_TICK;
+ VibrationConstants.EFFECT_TEXTURE_TICK;
/**
* Can be used to tint the color of the RecentsView to simulate a scrim that can views
@@ -381,19 +400,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;
+ return view.mTaskThumbnailSplashAlpha;
}
};
@@ -467,10 +486,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;
@@ -481,13 +501,16 @@
private boolean mOverviewFullscreenEnabled;
private boolean mOverviewSelectEnabled;
+ private boolean mShouldClampScrollOffset;
+ private int mClampedScrollOffsetBound;
+
private float mAdjacentPageHorizontalOffset = 0;
protected float mTaskViewsSecondaryTranslation = 0;
protected float mTaskViewsPrimarySplitTranslation = 0;
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();
@@ -559,7 +582,7 @@
if (taskRemoved) {
dismissTask(taskId);
}
- });
+ }, RecentsFilterState.getFilter(mFilterState.getPackageNameToFilter()));
}
}));
}
@@ -645,11 +668,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);
@@ -702,6 +723,9 @@
@Nullable
private TaskLaunchListener mTaskLaunchListener;
+ // keeps track of the state of the filter for tasks in recents view
+ private final RecentsFilterState mFilterState = new RecentsFilterState();
+
public RecentsView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
BaseActivityInterface sizeStrategy) {
super(context, attrs, defStyleAttr);
@@ -727,6 +751,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);
@@ -735,6 +761,8 @@
mSplitPlaceholderInset = getResources().getDimensionPixelSize(
R.dimen.split_placeholder_inset);
mSquaredTouchSlop = squaredTouchSlop(context);
+ mClampedScrollOffsetBound = getResources().getDimensionPixelSize(
+ R.dimen.transient_taskbar_clamped_offset_bound);
mEmptyIcon = context.getDrawable(R.drawable.ic_empty_recents);
mEmptyIcon.setCallback(this);
@@ -761,6 +789,55 @@
mActivity.getViewCache().setCacheSize(R.layout.digital_wellbeing_toast, 5);
mTintingColor = getForegroundScrimDimColor(context);
+
+ // if multi-instance feature is enabled
+ if (FeatureFlags.ENABLE_MULTI_INSTANCE.get()) {
+ // invalidate the current list of tasks if filter changes
+ mFilterState.setOnFilterUpdatedListener(this::invalidateTaskList);
+ }
+ // make sure filter is turned off by default
+ mFilterState.setFilterBy(null);
+ }
+
+ /** Get the state of the filter */
+ public RecentsFilterState getFilterState() {
+ return mFilterState;
+ }
+
+ /**
+ * Toggles the filter and reloads the recents view if needed.
+ *
+ * @param packageName package name to filter by if the filter is being turned on;
+ * should be null if filter is being turned off
+ */
+ public void setAndApplyFilter(@Nullable String packageName) {
+ mFilterState.setFilterBy(packageName);
+ updateClearAllFunction();
+ reloadIfNeeded();
+ }
+
+ /**
+ * Updates the "Clear All" button and its function depending on the recents view state.
+ *
+ * TODO: add a different button for going back to overview. Present solution is for demo only.
+ */
+ public void updateClearAllFunction() {
+ if (mFilterState.isFiltered()) {
+ mClearAllButton.setText(R.string.recents_back);
+ mClearAllButton.setOnClickListener((view) -> {
+ this.setAndApplyFilter(null);
+ });
+ } else {
+ mClearAllButton.setText(R.string.recents_clear_all);
+ mClearAllButton.setOnClickListener(this::dismissAllTasks);
+ }
+ }
+
+ /**
+ * Invalidates the list of tasks so that an update occurs to the list of tasks if requested.
+ */
+ private void invalidateTaskList() {
+ mTaskListChangeId = -1;
}
public OverScroller getScroller() {
@@ -806,8 +883,7 @@
}
super.dispatchDraw(canvas);
}
- if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mEnableDrawingLiveTile
- && mRemoteTargetHandles != null) {
+ if (mEnableDrawingLiveTile && mRemoteTargetHandles != null) {
redrawLiveTile();
}
}
@@ -902,7 +978,7 @@
mSplitSelectStateController = splitController;
}
- public SplitSelectStateController getSplitPlaceholder() {
+ public SplitSelectStateController getSplitSelectController() {
return mSplitSelectStateController;
}
@@ -930,10 +1006,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();
}
@@ -949,8 +1024,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();
@@ -972,6 +1046,8 @@
}
if (child instanceof GroupedTaskView) {
mGroupedTaskViewPool.recycle((GroupedTaskView) taskView);
+ } else if (child instanceof DesktopTaskView) {
+ mDesktopTaskViewPool.recycle((DesktopTaskView) taskView);
} else {
mTaskViewPool.recycle(taskView);
}
@@ -1046,8 +1122,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)) {
@@ -1059,14 +1135,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() {
@@ -1190,14 +1267,60 @@
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;
}
+ /**
+ * Pulls the list of active Tasks from RecentsModel, and finds the most recently active Task
+ * matching a given ComponentName. Then uses that Task (which could be null) with the given
+ * callback.
+ *
+ * Used in various task-switching or splitscreen operations when we need to check if there is a
+ * currently running Task of a certain type and use the most recent one.
+ */
+ public void findLastActiveTaskAndRunCallback(ComponentName componentName,
+ Consumer<Task> callback) {
+ mModel.getTasks(taskGroups -> {
+ Task lastActiveTask = null;
+ // Loop through tasks in reverse, since they are ordered with most-recent tasks last.
+ for (int i = taskGroups.size() - 1; i >= 0; i--) {
+ GroupTask groupTask = taskGroups.get(i);
+ Task task1 = groupTask.task1;
+ if (isInstanceOfComponent(task1, componentName)) {
+ lastActiveTask = task1;
+ break;
+ }
+ Task task2 = groupTask.task2;
+ if (isInstanceOfComponent(task2, componentName)) {
+ lastActiveTask = task2;
+ break;
+ }
+ }
+
+ callback.accept(lastActiveTask);
+ });
+ }
+
+ /**
+ * Checks if a given Task is the most recently-active Task of type componentName. Used for
+ * selecting already-running Tasks for splitscreen.
+ */
+ public boolean isInstanceOfComponent(@Nullable Task task, ComponentName componentName) {
+ if (task == null) {
+ return false;
+ }
+ // Exclude the task that is already staged
+ if (mSplitHiddenTaskView != null && mSplitHiddenTaskView.getTask().equals(task)) {
+ return false;
+ }
+
+ return task.key.baseIntent.getComponent().equals(componentName);
+ }
+
public void setOverviewStateEnabled(boolean enabled) {
mOverviewStateEnabled = enabled;
updateTaskStackListenerState();
@@ -1228,17 +1351,24 @@
if (!mActivity.getDeviceProfile().isTablet) {
mActionsView.updateDisabledFlags(OverviewActionsView.DISABLED_SCROLLING, true);
}
+ if (mOverviewStateEnabled) { // only when in overview
+ 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
@@ -1423,6 +1553,17 @@
updateGridProperties();
}
+ @Override
+ protected void onScrollerAnimationAborted() {
+ ActiveGestureLog.INSTANCE.addLog("scroller animation aborted",
+ ActiveGestureErrorDetector.GestureEvent.SCROLLER_ANIMATION_ABORTED);
+ }
+
+ @Override
+ protected boolean isPageScrollsInitialized() {
+ return super.isPageScrollsInitialized() && mLoadPlanEverApplied;
+ }
+
protected void applyLoadPlan(ArrayList<GroupTask> taskGroups) {
if (mPendingAnimation != null) {
mPendingAnimation.addEndListener(success -> applyLoadPlan(taskGroups));
@@ -1436,6 +1577,9 @@
// With all tasks removed, touch handling in PagedView is disabled and we need to reset
// touch state or otherwise values will be obsolete.
resetTouchState();
+ if (isPageScrollsInitialized()) {
+ onPageScrollsInitialized();
+ }
return;
}
@@ -1461,34 +1605,66 @@
int previousCurrentPage = mCurrentPage;
removeAllViews();
- // Add views as children based on whether it's grouped or single task
+ // If we are entering Overview as a result of initiating a split from somewhere else
+ // (e.g. split from Home), we need to make sure the staged app is not drawn as a thumbnail.
+ Task stagedTaskToBeRemovedFromGrid =
+ mSplitSelectSource != null ? mSplitSelectSource.alreadyRunningTask : null;
+
+ // update the map of instance counts
+ mFilterState.updateInstanceCountMap(taskGroups);
+
+ // Add views as children based on whether it's grouped or single task. Looping through
+ // taskGroups backwards populates the thumbnail grid from least recent to most recent.
for (int i = taskGroups.size() - 1; i >= 0; i--) {
GroupTask groupTask = taskGroups.get(i);
- boolean hasMultipleTasks = groupTask.hasMultipleTasks();
- TaskView taskView = getTaskViewFromPool(hasMultipleTasks);
+ boolean isRemovalNeeded = stagedTaskToBeRemovedFromGrid != null
+ && groupTask.containsTask(stagedTaskToBeRemovedFromGrid.key.id);
+
+ TaskView taskView;
+ if (isRemovalNeeded && groupTask.hasMultipleTasks()) {
+ // If we need to remove half of a pair of tasks, force a TaskView with Type.SINGLE
+ // to be a temporary container for the remaining task.
+ taskView = getTaskViewFromPool(TaskView.Type.SINGLE);
+ } else {
+ taskView = getTaskViewFromPool(groupTask.taskViewType);
+ }
+
addView(taskView);
- if (hasMultipleTasks) {
+ if (isRemovalNeeded && groupTask.hasMultipleTasks()) {
+ if (groupTask.task1.equals(stagedTaskToBeRemovedFromGrid)) {
+ taskView.bind(groupTask.task2, mOrientationState);
+ } else {
+ taskView.bind(groupTask.task1, mOrientationState);
+ }
+ } else if (isRemovalNeeded) {
+ // If the task we need to remove is not part of a pair, bind it to the TaskView
+ // first (to prevent problems), then remove the whole thing.
+ taskView.bind(groupTask.task1, mOrientationState);
+ removeView(taskView);
+ } else 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);
+
+ // enables instance filtering if the feature flag for it is on
+ if (FeatureFlags.ENABLE_MULTI_INSTANCE.get()) {
+ taskView.setUpShowAllInstancesListener();
+ }
}
- boolean settlingOnNewTask = mNextPage != INVALID_PAGE;
- if (settlingOnNewTask) {
- // Restore mCurrentPage but don't call setCurrentPage() as that clobbers the scroll.
- mCurrentPage = previousCurrentPage;
- } else {
- setCurrentPage(previousCurrentPage);
+ if (!taskGroups.isEmpty()) {
+ addView(mClearAllButton);
}
// Keep same previous focused task
@@ -1515,21 +1691,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 &&
@@ -1542,6 +1733,9 @@
resetTaskVisuals();
onTaskStackUpdated();
updateEnabledOverlays();
+ if (isPageScrollsInitialized()) {
+ onPageScrollsInitialized();
+ }
}
private boolean isModal() {
@@ -1607,24 +1801,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);
@@ -1704,6 +1897,7 @@
// Changed orientations, update controllers so they intercept accordingly.
mActivity.getDragLayer().recreateControllers();
onOrientationChanged();
+ resetTaskVisuals();
}
boolean isInLandscape = mOrientationState.getTouchRotation() != ROTATION_0
@@ -1725,7 +1919,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();
}
@@ -1858,9 +2052,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();
}
@@ -1873,6 +2069,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
@@ -1880,7 +2079,7 @@
if (!mActivity.getDeviceProfile().isTablet) {
return super.getDestinationPage(scaledScroll);
}
- if (!pageScrollsInitialized()) {
+ if (!isPageScrollsInitialized()) {
Log.e(TAG,
"Cannot get destination page: RecentsView not properly initialized",
new IllegalStateException());
@@ -2016,7 +2215,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 {
@@ -2097,10 +2296,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;
@@ -2125,7 +2333,8 @@
*/
public void reloadIfNeeded() {
if (!mModel.isTaskListValid(mTaskListChangeId)) {
- mTaskListChangeId = mModel.getTasks(this::applyLoadPlan);
+ mTaskListChangeId = mModel.getTasks(this::applyLoadPlan, RecentsFilterState
+ .getFilter(mFilterState.getPackageNameToFilter()));
}
}
@@ -2192,8 +2401,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();
}
}
@@ -2205,12 +2420,13 @@
@Nullable AnimatorSet animatorSet, GestureState.GestureEndTarget endTarget,
TaskViewSimulator[] taskViewSimulators) {
mCurrentGestureEndTarget = endTarget;
- if (endTarget == GestureState.GestureEndTarget.RECENTS) {
+ boolean isOverviewEndTarget = endTarget == GestureState.GestureEndTarget.RECENTS;
+ if (isOverviewEndTarget) {
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) {
@@ -2234,10 +2450,12 @@
}
}
}
+ int splashAlpha = endState.showTaskThumbnailSplash() ? 1 : 0;
if (animatorSet == null) {
- setOverviewProgress(1);
+ setTaskThumbnailSplashAlpha(splashAlpha);
} else {
- animatorSet.play(ObjectAnimator.ofFloat(this, OVERVIEW_PROGRESS, 1));
+ animatorSet.play(
+ ObjectAnimator.ofFloat(this, TASK_THUMBNAIL_SPLASH_ALPHA, splashAlpha));
}
}
@@ -2252,9 +2470,6 @@
setEnableFreeScroll(true);
setEnableDrawingLiveTile(mCurrentGestureEndTarget == GestureState.GestureEndTarget.RECENTS);
- if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- setRunningTaskViewShowScreenshot(true);
- }
setRunningTaskHidden(false);
animateUpTaskIconScale();
animateActionsViewIn();
@@ -2292,12 +2507,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
@@ -2306,7 +2528,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
@@ -2341,6 +2563,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.
*/
@@ -2379,12 +2613,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);
}
}
@@ -2675,28 +2907,24 @@
* @param gridProgress 0 = carousel; 1 = 2 row grid.
*/
private void setGridProgress(float gridProgress) {
- int taskCount = getTaskViewCount();
- if (taskCount == 0) {
- return;
- }
-
mGridProgress = gridProgress;
+ int taskCount = getTaskViewCount();
for (int i = 0; i < taskCount; i++) {
requireTaskViewAt(i).setGridProgress(gridProgress);
}
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);
}
}
@@ -2753,7 +2981,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();
@@ -2765,8 +2993,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()
@@ -2787,43 +3014,112 @@
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) {
- mSplitHiddenTaskView.setVisibility(INVISIBLE);
+ // 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 */);
}
+ // Allow user to click staged app to launch into fullscreen
+ 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.ALPHA_FLOAT, 0, 1, ACCEL);
+ 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,
+ 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(animationSuccess ->
+ mSplitSelectStateController.launchSplitTasks(launchSuccess ->
+ resetFromSplitSelectionState()));
+
+ pendingAnimation.buildAnim().start();
+ }
+
/**
* Creates a {@link PendingAnimation} for dismissing the specified {@link TaskView}.
* @param dismissedTaskView the {@link TaskView} to be dismissed
@@ -2834,17 +3130,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();
@@ -2898,8 +3193,6 @@
}
}
- announceForAccessibility(getResources().getString(R.string.task_view_closed));
-
float dismissTranslationInterpolationEnd = 1;
boolean closeGapBetweenClearAll = false;
boolean isClearAllHidden = isClearAllHidden();
@@ -2985,8 +3278,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 ->
@@ -3012,6 +3304,9 @@
}
}
+ SplitAnimationTimings splitTimings =
+ AnimUtils.getDeviceOverviewToSplitTimings(mActivity.getDeviceProfile().isTablet);
+
int distanceFromDismissedTask = 0;
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
@@ -3054,11 +3349,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(
@@ -3089,11 +3404,35 @@
// Animate task with index >= dismissed index and in the same row as the
// dismissed index or next focused index. Offset successive task dismissal
// durations for a staggered effect.
- float animationStartProgress = Utilities.boundToRange(
- INITIAL_DISMISS_TRANSLATION_INTERPOLATION_OFFSET
- + ADDITIONAL_DISMISS_TRANSLATION_INTERPOLATION_OFFSET
- * ++distanceFromDismissedTask, 0f,
- dismissTranslationInterpolationEnd);
+ distanceFromDismissedTask++;
+ 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(
+ splitTimings.getGridSlideStartOffset()
+ + (splitTimings.getGridSlideStaggerOffset()
+ * staggerColumn),
+ 0f,
+ dismissTranslationInterpolationEnd)
+ : Utilities.boundToRange(
+ INITIAL_DISMISS_TRANSLATION_INTERPOLATION_OFFSET
+ + ADDITIONAL_DISMISS_TRANSLATION_INTERPOLATION_OFFSET
+ * 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.
float scale = mTaskWidth / (float) mLastComputedGridTaskSize.width();
@@ -3108,7 +3447,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,
@@ -3116,7 +3455,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);
@@ -3132,8 +3471,8 @@
anim.setFloat(taskView, taskView.getPrimaryDismissTranslationProperty(),
mIsRtl ? primaryTranslation : -primaryTranslation,
- clampToProgress(LINEAR, animationStartProgress,
- dismissTranslationInterpolationEnd));
+ clampToProgress(dismissInterpolator, animationStartProgress,
+ animationEndProgress));
}
}
}
@@ -3157,8 +3496,7 @@
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 {
@@ -3175,13 +3513,14 @@
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 {
removeTaskInternal(dismissedTaskViewId);
}
+ announceForAccessibility(
+ getResources().getString(R.string.task_view_closed));
mActivity.getStatsLogManager().logger()
.withItemInfo(dismissedTaskView.getItemInfo())
.log(LAUNCHER_TASK_DISMISS_SWIPE_UP);
@@ -3277,7 +3616,8 @@
removeViewInLayout(mClearAllButton);
if (isHomeTaskDismissed) {
updateEmptyMessage();
- } else {
+ } else if (!(ENABLE_TASKBAR_IN_OVERVIEW.get() &&
+ mSplitSelectStateController.isSplitSelectActive())) {
startHome();
}
} else {
@@ -3365,7 +3705,6 @@
mPendingAnimation = null;
}
});
- return anim;
}
/**
@@ -3377,11 +3716,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);
}
/**
@@ -3533,8 +3872,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")
@@ -3834,8 +4175,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);
@@ -4043,37 +4383,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, splitSelectSource.alreadyRunningTask);
}
- 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);
}
}
@@ -4081,32 +4429,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;
@@ -4116,28 +4480,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");
@@ -4152,23 +4521,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;
}
@@ -4182,20 +4548,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.setVisibility(VISIBLE);
+ 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;
}
@@ -4299,30 +4677,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,
@@ -4342,7 +4721,7 @@
properties));
}
}
- anim.play(ObjectAnimator.ofFloat(recentsView, OVERVIEW_PROGRESS, 1, 0));
+ anim.play(ObjectAnimator.ofFloat(this, TASK_THUMBNAIL_SPLASH_ALPHA, 0, 1));
return anim;
}
@@ -4398,23 +4777,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) {
@@ -4426,7 +4803,7 @@
dividerAnimator.end();
});
}
- if (ENABLE_QUICKSTEP_LIVE_TILE.get() && tv.isRunningTask()) {
+ if (tv.isRunningTask()) {
finishRecentsAnimation(false /* toRecents */, null);
onTaskLaunchAnimationEnd(true /* success */);
} else {
@@ -4534,9 +4911,16 @@
return;
}
- RemoteTargetGluer gluer = new RemoteTargetGluer(getContext(), getSizeStrategy());
- mRemoteTargetHandles = gluer.assignTargetsForSplitScreen(
- getContext(), recentsAnimationTargets);
+ RemoteTargetGluer gluer;
+ if (DESKTOP_MODE_SUPPORTED && recentsAnimationTargets.hasDesktopTasks()) {
+ gluer = new RemoteTargetGluer(getContext(), getSizeStrategy(), recentsAnimationTargets,
+ true /* forDesktop */);
+ mRemoteTargetHandles = gluer.assignTargetsForDesktop(recentsAnimationTargets);
+ } else {
+ gluer = new RemoteTargetGluer(getContext(), getSizeStrategy());
+ mRemoteTargetHandles = gluer.assignTargetsForSplitScreen(
+ getContext(), recentsAnimationTargets);
+ }
mSplitBoundsConfig = gluer.getSplitBounds();
// Add release check to the targets from the RemoteTargetGluer and not the targets
// passed in because in the event we're in split screen, we use the passed in targets
@@ -4599,7 +4983,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}.
@@ -4810,9 +5194,35 @@
}
/**
+ * Sets whether or not we should clamp the scroll offset.
+ * This is used to avoid x-axis movement when swiping up transient taskbar.
+ * Should only be set at the beginning and end of the gesture, otherwise a jump may occur.
+ * @param clampScrollOffset When true, we clamp the scroll to 0 before the clamp threshold is
+ * met.
+ */
+ public void setClampScrollOffset(boolean clampScrollOffset) {
+ mShouldClampScrollOffset = clampScrollOffset;
+ }
+
+ /**
* Returns how many pixels the page is offset on the currently laid out dominant axis.
*/
public int getScrollOffset(int pageIndex) {
+ int unclampedOffset = getUnclampedScrollOffset(pageIndex);
+ if (!mShouldClampScrollOffset) {
+ return unclampedOffset;
+ }
+ if (Math.abs(unclampedOffset) < mClampedScrollOffsetBound) {
+ return 0;
+ }
+ return unclampedOffset
+ - Math.round(Math.signum(unclampedOffset) * mClampedScrollOffsetBound);
+ }
+
+ /**
+ * Returns how many pixels the page is offset on the currently laid out dominant axis.
+ */
+ private int getUnclampedScrollOffset(int pageIndex) {
if (pageIndex == -1) {
return 0;
}
@@ -5042,17 +5452,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;
@@ -5291,6 +5692,16 @@
return mRecentsAnimationController;
}
+ @Nullable
+ public FloatingTaskView getFirstFloatingTaskView() {
+ 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 7d94505..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;
@@ -25,9 +25,11 @@
import android.widget.FrameLayout;
import androidx.annotation.Nullable;
+import androidx.appcompat.widget.AppCompatTextView;
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;
@@ -40,18 +42,18 @@
*/
public class SplitInstructionsView extends FrameLayout {
private final StatefulActivity mLauncher;
+ private AppCompatTextView mTextView;
- public static final FloatProperty<SplitInstructionsView> ALPHA_FLOAT =
- new FloatProperty<SplitInstructionsView>("SplitInstructionsAlpha") {
+ public static final FloatProperty<SplitInstructionsView> UNFOLD =
+ new FloatProperty<SplitInstructionsView>("SplitInstructionsUnfold") {
@Override
public void setValue(SplitInstructionsView splitInstructionsView, float v) {
- splitInstructionsView.setVisibility(v != 0 ? VISIBLE : GONE);
- splitInstructionsView.setAlpha(v);
+ splitInstructionsView.setScaleY(v);
}
@Override
public Float get(SplitInstructionsView splitInstructionsView) {
- return splitInstructionsView.getAlpha();
+ return splitInstructionsView.getScaleY();
}
};
@@ -77,6 +79,14 @@
false
);
+ 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;
}
@@ -105,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
@@ -120,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 28080d4..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,20 +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();
- }
- };
-
@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..54b58b9 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
@@ -49,11 +48,13 @@
taskContainer: TaskIdAttributeContainer,
alignSecondRow: Boolean = false
): Boolean {
- val activity = BaseDraggingActivity
- .fromContext<BaseDraggingActivity>(taskContainer.taskView.context)
- val taskMenuViewWithArrow = activity.layoutInflater
- .inflate(
- KtR.layout.task_menu_with_arrow,
+ val activity =
+ BaseDraggingActivity.fromContext<BaseDraggingActivity>(
+ taskContainer.taskView.context
+ )
+ val taskMenuViewWithArrow =
+ activity.layoutInflater.inflate(
+ R.layout.task_menu_with_arrow,
activity.dragLayer,
false
) as TaskMenuViewWithArrow<*>
@@ -64,11 +65,11 @@
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
- constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(
- context,
- attrs,
- defStyleAttr
- )
+ constructor(
+ context: Context,
+ attrs: AttributeSet,
+ defStyleAttr: Int
+ ) : super(context, attrs, defStyleAttr)
init {
clipToOutline = true
@@ -92,10 +93,10 @@
private var optionMeasuredHeight = 0
private val arrowHorizontalPadding: Int
- get() = if (taskView.isFocusedTask)
- resources.getDimensionPixelSize(KtR.dimen.task_menu_horizontal_padding)
- else
- 0
+ get() =
+ if (taskView.isFocusedTask)
+ resources.getDimensionPixelSize(R.dimen.task_menu_horizontal_padding)
+ else 0
private var iconView: IconView? = null
private var scrim: View? = null
@@ -119,7 +120,7 @@
override fun onFinishInflate() {
super.onFinishInflate()
- optionLayout = findViewById(KtR.id.menu_option_layout)
+ optionLayout = findViewById(R.id.menu_option_layout)
}
private fun populateAndShowForTask(
@@ -140,19 +141,20 @@
}
private fun addScrim() {
- scrim = View(context).apply {
- layoutParams = FrameLayout.LayoutParams(
- FrameLayout.LayoutParams.MATCH_PARENT,
- FrameLayout.LayoutParams.MATCH_PARENT
- )
- setBackgroundColor(Themes.getAttrColor(context, R.attr.overviewScrimColor))
- alpha = 0f
- }
+ scrim =
+ View(context).apply {
+ layoutParams =
+ FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.MATCH_PARENT,
+ FrameLayout.LayoutParams.MATCH_PARENT
+ )
+ setBackgroundColor(Themes.getAttrColor(context, R.attr.overviewScrimColor))
+ alpha = 0f
+ }
popupContainer.addView(scrim)
}
- /** @return true if successfully able to populate task view menu, false otherwise
- */
+ /** @return true if successfully able to populate task view menu, false otherwise */
private fun populateMenu(): Boolean {
// Icon may not be loaded
if (taskContainer.task.icon == null) return false
@@ -163,14 +165,14 @@
private fun addMenuOptions() {
// Add the options
- TaskOverlayFactory
- .getEnabledShortcuts(taskView, taskContainer)
- .forEach { this.addMenuOption(it) }
+ TaskOverlayFactory.getEnabledShortcuts(taskView, taskContainer).forEach {
+ this.addMenuOption(it)
+ }
// 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
@@ -186,9 +188,9 @@
}
private fun addMenuOption(menuOption: SystemShortcut<*>) {
- val menuOptionView = mActivityContext.layoutInflater.inflate(
- KtR.layout.task_view_menu_option, this, false
- ) as LinearLayout
+ val menuOptionView =
+ mActivityContext.layoutInflater.inflate(R.layout.task_view_menu_option, this, false)
+ as LinearLayout
menuOption.setIconAndLabelFor(
menuOptionView.findViewById(R.id.icon),
menuOptionView.findViewById(R.id.text)
@@ -231,24 +233,25 @@
}
/**
- * Copy the iconView from taskView to dragLayer so it can stay on top of the scrim.
- * It needs to be called after [getTargetObjectLocation] because [mTempRect] needs to be
- * populated.
+ * Copy the iconView from taskView to dragLayer so it can stay on top of the scrim. It needs to
+ * be called after [getTargetObjectLocation] because [mTempRect] needs to be populated.
*/
private fun copyIconToDragLayer(insets: Rect) {
- iconView = IconView(context).apply {
- layoutParams = FrameLayout.LayoutParams(
- taskContainer.iconView.width,
- taskContainer.iconView.height
- )
- x = mTempRect.left.toFloat() - insets.left
- y = mTempRect.top.toFloat() - insets.top
- drawable = taskContainer.iconView.drawable
- setDrawableSize(
- taskContainer.iconView.drawableWidth,
- taskContainer.iconView.drawableHeight
- )
- }
+ iconView =
+ IconView(context).apply {
+ layoutParams =
+ FrameLayout.LayoutParams(
+ taskContainer.iconView.width,
+ taskContainer.iconView.height
+ )
+ x = mTempRect.left.toFloat() - insets.left
+ y = mTempRect.top.toFloat() - insets.top
+ drawable = taskContainer.iconView.drawable
+ setDrawableSize(
+ taskContainer.iconView.drawableWidth,
+ taskContainer.iconView.drawableHeight
+ )
+ }
popupContainer.addView(iconView)
}
@@ -282,12 +285,13 @@
// which means the arrow is left aligned with the menu
val rightAlignedMenuStartX = mTempRect.left - widthWithArrow
val leftAlignedMenuStartX = mTempRect.right + extraHorizontalSpace
- mIsLeftAligned = if (mIsRtl) {
- rightAlignedMenuStartX + insets.left < 0
- } else {
- leftAlignedMenuStartX + (widthWithArrow - extraHorizontalSpace) + insets.left <
+ mIsLeftAligned =
+ if (mIsRtl) {
+ rightAlignedMenuStartX + insets.left < 0
+ } else {
+ leftAlignedMenuStartX + (widthWithArrow - extraHorizontalSpace) + insets.left <
dragLayer.width - insets.right
- }
+ }
var menuStartX = if (mIsLeftAligned) leftAlignedMenuStartX else rightAlignedMenuStartX
@@ -312,8 +316,8 @@
override fun addArrow() {
popupContainer.addView(mArrow)
mArrow.x = getArrowX()
- mArrow.y = y + (optionMeasuredHeight / 2) - (mArrowHeight / 2) +
- extraSpaceForSecondRowAlignment
+ mArrow.y =
+ y + (optionMeasuredHeight / 2) - (mArrowHeight / 2) + extraSpaceForSecondRowAlignment
updateArrowColor()
@@ -323,22 +327,19 @@
}
private fun getArrowX(): Float {
- return if (mIsLeftAligned)
- x - mArrowHeight
- else
- x + measuredWidth + mArrowOffsetVertical
+ return if (mIsLeftAligned) x - mArrowHeight else x + measuredWidth + mArrowOffsetVertical
}
override fun updateArrowColor() {
- mArrow.background = RoundedArrowDrawable(
- mArrowWidth.toFloat(),
- mArrowHeight.toFloat(),
- mArrowPointRadius.toFloat(),
- mIsLeftAligned,
- mArrowColor
- )
+ mArrow.background =
+ RoundedArrowDrawable(
+ mArrowWidth.toFloat(),
+ mArrowHeight.toFloat(),
+ mArrowPointRadius.toFloat(),
+ mIsLeftAligned,
+ mArrowColor
+ )
elevation = mElevation
mArrow.elevation = mElevation
}
-
-}
\ No newline at end of file
+}
diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
index f4b3d98..c71a74e 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,13 +370,15 @@
* <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();
}
private void updateSplashView(Drawable icon) {
if (icon == null || icon.getConstantState() == null) {
+ mSplashViewDrawable = null;
+ mSplashView = null;
return;
}
mSplashViewDrawable = icon.getConstantState().newDrawable().mutate();
@@ -417,11 +416,13 @@
return false;
}
- float thumbnailViewAspect = getWidth() / (float) getHeight();
+ RectF insets = mPreviewPositionHelper.getClippedInsets();
+ float thumbnailViewAspect = (getWidth() + insets.left + insets.right)
+ / (getHeight() + insets.top + insets.bottom);
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 +446,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,7 +468,8 @@
}
private void updateThumbnailMatrix() {
- mPreviewPositionHelper.mIsOrientationChanged = false;
+ DeviceProfile dp = mActivity.getDeviceProfile();
+ mPreviewPositionHelper.setOrientationChanged(false);
if (mBitmapShader != null && mThumbnailData != null) {
mPreviewRect.set(0, 0, mThumbnailData.thumbnail.getWidth(),
mThumbnailData.thumbnail.getHeight());
@@ -475,10 +477,10 @@
.getRecentsActivityRotation();
boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
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 +520,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 2ccd703..aa37fdd 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
@@ -328,9 +342,9 @@
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;
@@ -370,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;
@@ -386,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;
@@ -486,7 +500,7 @@
return;
}
mModalness = modalness;
- mIconView.setAlpha(comp(modalness));
+ mIconView.setAlpha(1 - modalness);
mDigitalWellBeingToast.updateBannerOffset(modalness,
mCurrentFullscreenParams.mCurrentDrawnInsets.top
+ mCurrentFullscreenParams.mCurrentDrawnInsets.bottom);
@@ -512,6 +526,52 @@
setOrientationState(orientedState);
}
+ /**
+ * Sets up an on-click listener and the visibility for show_windows icon on top of the task.
+ */
+ public void setUpShowAllInstancesListener() {
+ String taskPackageName = mTaskIdAttributeContainer[0].mTask.key.getPackageName();
+
+ // icon of the top/left task
+ View showWindowsView = findViewById(R.id.show_windows);
+ updateFilterCallback(showWindowsView, getFilterUpdateCallback(taskPackageName));
+ }
+
+ /**
+ * Returns a callback that updates the state of the filter and the recents overview
+ *
+ * @param taskPackageName package name of the task to filter by
+ */
+ @Nullable
+ protected View.OnClickListener getFilterUpdateCallback(String taskPackageName) {
+ View.OnClickListener cb = (view) -> {
+ // update and apply a new filter
+ getRecentsView().setAndApplyFilter(taskPackageName);
+ };
+
+ if (!getRecentsView().getFilterState().shouldShowFilterUI(taskPackageName)) {
+ cb = null;
+ }
+ return cb;
+ }
+
+ /**
+ * Sets the correct visibility and callback on the provided filterView based on whether
+ * the callback is null or not
+ */
+ protected void updateFilterCallback(@NonNull View filterView,
+ @Nullable View.OnClickListener callback) {
+ // Filtering changes alpha instead of the visibility since visibility
+ // can be altered separately through RecentsView#resetFromSplitSelectionState()
+ if (callback == null) {
+ filterView.setAlpha(0);
+ } else {
+ filterView.setAlpha(1);
+ }
+
+ filterView.setOnClickListener(callback);
+ }
+
public TaskIdAttributeContainer[] getTaskIdAttributeContainers() {
return mTaskIdAttributeContainer;
}
@@ -522,6 +582,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
*/
@@ -560,6 +627,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());
}
@@ -582,17 +661,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;
}
@@ -611,7 +694,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
@@ -646,16 +729,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)) {
@@ -674,14 +755,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;
@@ -691,14 +788,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);
@@ -706,11 +803,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,
@@ -733,12 +835,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();
}
}
@@ -866,10 +979,9 @@
int thumbnailTopMargin = deviceProfile.overviewTaskThumbnailTopMarginPx;
int taskIconHeight = deviceProfile.overviewTaskIconSizePx;
- int taskMargin = isGridTask ? deviceProfile.overviewTaskMarginGridPx
- : deviceProfile.overviewTaskMarginPx;
- int taskIconMargin = thumbnailTopMargin - taskIconHeight - taskMargin;
- orientationHandler.setTaskIconParams(iconParams, taskIconMargin, taskIconHeight,
+ int taskMargin = deviceProfile.overviewTaskMarginPx;
+
+ orientationHandler.setTaskIconParams(iconParams, taskMargin, taskIconHeight,
thumbnailTopMargin, isRtl);
iconParams.width = iconParams.height = taskIconHeight;
mIconView.setLayoutParams(iconParams);
@@ -895,7 +1007,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;
}
@@ -939,7 +1055,7 @@
if (mIconAndDimAnimator != null) {
mIconAndDimAnimator.cancel();
}
- setIconAndDimTransitionProgress(iconScale, invert);
+ setIconsAndBannersTransitionProgress(iconScale, invert);
}
protected void resetPersistentViewTransforms() {
@@ -1057,18 +1173,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) {
@@ -1219,7 +1332,7 @@
DISMISS_TRANSLATION_X, DISMISS_TRANSLATION_Y);
}
- public FloatProperty<TaskView> getSecondaryDissmissTranslationProperty() {
+ public FloatProperty<TaskView> getSecondaryDismissTranslationProperty() {
return getPagedOrientationHandler().getSecondaryValue(
DISMISS_TRANSLATION_X, DISMISS_TRANSLATION_Y);
}
@@ -1362,6 +1475,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();
}
@@ -1474,7 +1593,8 @@
}
public void initiateSplitSelect(SplitPositionOption splitPositionOption) {
- getRecentsView().initiateSplitSelect(this, splitPositionOption.stagePosition);
+ getRecentsView().initiateSplitSelect(this, splitPositionOption.stagePosition,
+ getLogEventForPosition(splitPositionOption.stagePosition));
}
/**
@@ -1493,6 +1613,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 {
@@ -1505,9 +1638,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;
}
@@ -1517,7 +1653,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;
@@ -1536,6 +1672,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 (dp.isTaskbarPresent && 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/TaskbarBaseTestCase.kt b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarBaseTestCase.kt
new file mode 100644
index 0000000..8a78d8c
--- /dev/null
+++ b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarBaseTestCase.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.taskbar
+
+import com.android.launcher3.taskbar.allapps.TaskbarAllAppsController
+import com.android.launcher3.taskbar.overlay.TaskbarOverlayController
+import com.android.systemui.shared.rotation.RotationButtonController
+import org.junit.Before
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+/**
+ * Helper class to extend to get access to all controllers. Gotta be careful of your relationship
+ * with this class though, it can be quite... controlling.
+ */
+abstract class TaskbarBaseTestCase {
+
+ @Mock lateinit var taskbarActivityContext: TaskbarActivityContext
+ @Mock lateinit var taskbarDragController: TaskbarDragController
+ @Mock lateinit var navButtonController: TaskbarNavButtonController
+ @Mock lateinit var navbarButtonsViewController: NavbarButtonsViewController
+ @Mock lateinit var rotationButtonController: RotationButtonController
+ @Mock lateinit var taskbarDragLayerController: TaskbarDragLayerController
+ @Mock lateinit var taskbarScrimViewController: TaskbarScrimViewController
+ @Mock lateinit var taskbarViewController: TaskbarViewController
+ @Mock lateinit var taskbarUnfoldAnimationController: TaskbarUnfoldAnimationController
+ @Mock lateinit var taskbarKeyguardController: TaskbarKeyguardController
+ @Mock lateinit var stashedHandleViewController: StashedHandleViewController
+ @Mock lateinit var taskbarStashController: TaskbarStashController
+ @Mock lateinit var taskbarEduController: TaskbarEduController
+ @Mock lateinit var taskbarAutohideSuspendController: TaskbarAutohideSuspendController
+ @Mock lateinit var taskbarPopupController: TaskbarPopupController
+ @Mock
+ lateinit var taskbarForceVisibleImmersiveController: TaskbarForceVisibleImmersiveController
+ @Mock lateinit var taskbarAllAppsController: TaskbarAllAppsController
+ @Mock lateinit var taskbarInsetsController: TaskbarInsetsController
+ @Mock lateinit var voiceInteractionWindowController: VoiceInteractionWindowController
+ @Mock lateinit var taskbarRecentAppsController: TaskbarRecentAppsController
+ @Mock lateinit var taskbarTranslationController: TaskbarTranslationController
+ @Mock lateinit var taskbarOverlayController: TaskbarOverlayController
+ @Mock lateinit var taskbarEduTooltipController: TaskbarEduTooltipController
+
+ lateinit var mTaskbarControllers: TaskbarControllers
+
+ @Before
+ open fun setup() {
+ /*
+ * NOTE: Mocking of controllers that are written in Kotlin won't work since their methods
+ * are final by default (and should not be changed only for tests), meaning unmockable.
+ * Womp, womp woooommmmppp.
+ * If you want to mock one of those methods, you need to make a parent interface that
+ * includes that method to allow mocking it.
+ */
+ MockitoAnnotations.initMocks(this)
+ mTaskbarControllers =
+ TaskbarControllers(
+ taskbarActivityContext,
+ taskbarDragController,
+ navButtonController,
+ navbarButtonsViewController,
+ rotationButtonController,
+ taskbarDragLayerController,
+ taskbarViewController,
+ taskbarScrimViewController,
+ taskbarUnfoldAnimationController,
+ taskbarKeyguardController,
+ stashedHandleViewController,
+ taskbarStashController,
+ taskbarEduController,
+ taskbarAutohideSuspendController,
+ taskbarPopupController,
+ taskbarForceVisibleImmersiveController,
+ taskbarOverlayController,
+ taskbarAllAppsController,
+ taskbarInsetsController,
+ voiceInteractionWindowController,
+ taskbarTranslationController,
+ taskbarRecentAppsController,
+ taskbarEduTooltipController,
+ )
+ }
+}
diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarKeyguardControllerTest.kt b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarKeyguardControllerTest.kt
new file mode 100644
index 0000000..59db8c8
--- /dev/null
+++ b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarKeyguardControllerTest.kt
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.taskbar
+
+import android.app.KeyguardManager
+import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BACK_DISABLED
+import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING
+import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DOZING
+import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED
+import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED
+import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING
+import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mock
+import org.mockito.Mockito.anyBoolean
+import org.mockito.Mockito.never
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
+
+class TaskbarKeyguardControllerTest : TaskbarBaseTestCase() {
+
+ @Mock
+ lateinit var baseDragLayer: TaskbarDragLayer
+ @Mock
+ lateinit var keyguardManager: KeyguardManager
+
+ @Before
+ override fun setup() {
+ super.setup()
+ whenever(taskbarActivityContext.getSystemService(KeyguardManager::class.java))
+ .thenReturn(keyguardManager)
+ whenever(baseDragLayer.childCount).thenReturn(0)
+ whenever(taskbarActivityContext.dragLayer).thenReturn(baseDragLayer)
+
+ taskbarKeyguardController = TaskbarKeyguardController(taskbarActivityContext)
+ taskbarKeyguardController.init(navbarButtonsViewController)
+ }
+
+ @Test
+ fun uninterestingFlags_noActions() {
+ setFlags(0)
+ verify(navbarButtonsViewController, never()).setKeyguardVisible(anyBoolean(), anyBoolean())
+ }
+
+ @Test
+ fun keyguardShowing() {
+ setFlags(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING)
+ verify(navbarButtonsViewController, times(1)).setKeyguardVisible(
+ true /*isKeyguardVisible*/,
+ false /*isKeyguardOccluded*/)
+ }
+
+ @Test
+ fun dozingShowing() {
+ setFlags(SYSUI_STATE_DEVICE_DOZING)
+ verify(navbarButtonsViewController, times(1)).setKeyguardVisible(
+ true /*isKeyguardVisible*/,
+ false /*isKeyguardOccluded*/)
+ }
+
+ @Test
+ fun keyguardOccluded() {
+ setFlags(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED)
+ verify(navbarButtonsViewController, times(1)).setKeyguardVisible(
+ false /*isKeyguardVisible*/,
+ true /*isKeyguardOccluded*/)
+ }
+
+ @Test
+ fun keyguardOccludedAndDozing() {
+ setFlags(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED.or(SYSUI_STATE_DEVICE_DOZING))
+ verify(navbarButtonsViewController, times(1)).setKeyguardVisible(
+ true /*isKeyguardVisible*/,
+ true /*isKeyguardOccluded*/)
+ }
+
+ @Test
+ fun deviceInsecure_hideBackForBouncer() {
+ whenever(keyguardManager.isDeviceSecure).thenReturn(false)
+ setFlags(SYSUI_STATE_OVERVIEW_DISABLED
+ .or(SYSUI_STATE_HOME_DISABLED)
+ .or(SYSUI_STATE_BOUNCER_SHOWING))
+
+ verify(navbarButtonsViewController, times(1)).setBackForBouncer(false)
+ }
+
+ @Test
+ fun deviceSecure_showBackForBouncer() {
+ whenever(keyguardManager.isDeviceSecure).thenReturn(true)
+ setFlags(SYSUI_STATE_OVERVIEW_DISABLED
+ .or(SYSUI_STATE_HOME_DISABLED)
+ .or(SYSUI_STATE_BOUNCER_SHOWING))
+
+ verify(navbarButtonsViewController, times(1)).setBackForBouncer(true)
+ }
+
+ @Test
+ fun homeShowing_hideBackForBouncer() {
+ whenever(keyguardManager.isDeviceSecure).thenReturn(true)
+ setFlags(SYSUI_STATE_OVERVIEW_DISABLED
+ .or(SYSUI_STATE_BOUNCER_SHOWING))
+
+ verify(navbarButtonsViewController, times(1)).setBackForBouncer(false)
+ }
+
+ @Test
+ fun overviewShowing_hideBackForBouncer() {
+ whenever(keyguardManager.isDeviceSecure).thenReturn(true)
+ setFlags(SYSUI_STATE_HOME_DISABLED
+ .or(SYSUI_STATE_BOUNCER_SHOWING))
+
+ verify(navbarButtonsViewController, times(1)).setBackForBouncer(false)
+ }
+
+ @Test
+ fun backDisabled_hideBackForBouncer() {
+ whenever(keyguardManager.isDeviceSecure).thenReturn(true)
+ setFlags(SYSUI_STATE_BACK_DISABLED
+ .or(SYSUI_STATE_BOUNCER_SHOWING))
+
+ verify(navbarButtonsViewController, times(1)).setBackForBouncer(false)
+
+ // back disabled along with home and overview
+ setFlags(SYSUI_STATE_BACK_DISABLED
+ .or(SYSUI_STATE_HOME_DISABLED)
+ .or(SYSUI_STATE_OVERVIEW_DISABLED)
+ .or(SYSUI_STATE_BOUNCER_SHOWING))
+
+ verify(navbarButtonsViewController, times(2)).setBackForBouncer(false)
+ }
+
+ private fun setFlags(flags: Int) {
+ taskbarKeyguardController.updateStateForSysuiFlags(flags)
+ }
+}
\ No newline at end of file
diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java
index 4eec319..9622619 100644
--- a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java
+++ b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java
@@ -18,6 +18,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -103,12 +104,20 @@
}
@Test
- public void testLongPressHome() {
+ public void testLongPressHome_enabled() {
+ mNavButtonController.setAssistantLongPressEnabled(true /*assistantLongPressEnabled*/);
mNavButtonController.onButtonLongClick(BUTTON_HOME, mockView);
verify(mockSystemUiProxy, times(1)).startAssistant(any());
}
@Test
+ public void testLongPressHome_disabled() {
+ mNavButtonController.setAssistantLongPressEnabled(false /*assistantLongPressEnabled*/);
+ mNavButtonController.onButtonLongClick(BUTTON_HOME, mockView);
+ verify(mockSystemUiProxy, never()).startAssistant(any());
+ }
+
+ @Test
public void testPressHome() {
mNavButtonController.onButtonClick(BUTTON_HOME, mockView);
verify(mockCommandHelper, times(1)).addCommand(TYPE_HOME);
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..236b5db
--- /dev/null
+++ b/quickstep/tests/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactoryTest.kt
@@ -0,0 +1,172 @@
+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.R
+import com.android.launcher3.taskbar.TaskbarManager
+import java.lang.IllegalStateException
+import org.junit.Assume.assumeTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+
+@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
+ )
+ }
+}
diff --git a/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java b/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java
index 3817cf2..59f9d5f 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/DeviceProfilePhone3ButtonTest.kt b/quickstep/tests/src/com/android/quickstep/DeviceProfilePhone3ButtonTest.kt
deleted file mode 100644
index 3daf81d..0000000
--- a/quickstep/tests/src/com/android/quickstep/DeviceProfilePhone3ButtonTest.kt
+++ /dev/null
@@ -1,486 +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.DeviceProfile
-import com.android.launcher3.DeviceProfileBaseTest
-import com.google.common.truth.Truth.assertThat
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-
-/**
- * Tests for DeviceProfile for phone with 3-Button navigation.
- */
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class DeviceProfilePhone3ButtonTest : DeviceProfileBaseTest() {
-
- lateinit var dp: DeviceProfile
-
- @Before
- fun before() {
- initializeVarsForPhone(isGestureMode = false)
- dp = newDP()
- }
-
- @Test
- fun isScalableGrid() {
- assertThat(dp.isScalableGrid).isTrue()
- }
-
- @Test
- fun cellWidthPx() {
- assertThat(dp.cellWidthPx).isEqualTo(265)
- }
-
- @Test
- fun cellHeightPx() {
- assertThat(dp.cellHeightPx).isEqualTo(343)
- }
-
- @Test
- fun getCellSizeX() {
- assertThat(dp.getCellSize().x).isEqualTo(265)
- }
-
- @Test
- fun getCellSizeY() {
- assertThat(dp.getCellSize().y).isEqualTo(552)
- }
-
- @Test
- fun cellLayoutBorderSpacePxX() {
- assertThat(dp.cellLayoutBorderSpacePx.x).isEqualTo(66)
- }
-
- @Test
- fun cellLayoutBorderSpacePxY() {
- assertThat(dp.cellLayoutBorderSpacePx.y).isEqualTo(66)
- }
-
- @Test
- fun cellLayoutPaddingPxLeft() {
- assertThat(dp.cellLayoutPaddingPx.left).isEqualTo(38)
- }
-
- @Test
- fun cellLayoutPaddingPxTop() {
- assertThat(dp.cellLayoutPaddingPx.top).isEqualTo(0)
- }
-
- @Test
- fun cellLayoutPaddingPxRight() {
- assertThat(dp.cellLayoutPaddingPx.right).isEqualTo(38)
- }
-
- @Test
- fun cellLayoutPaddingPxBottom() {
- assertThat(dp.cellLayoutPaddingPx.bottom).isEqualTo(38)
- }
-
- @Test
- fun iconSizePx() {
- assertThat(dp.iconSizePx).isEqualTo(196)
- }
-
- @Test
- fun iconTextSizePx() {
- assertThat(dp.iconTextSizePx).isEqualTo(49)
- }
-
- @Test
- fun iconDrawablePaddingPx() {
- assertThat(dp.iconDrawablePaddingPx).isEqualTo(25)
- }
-
- @Test
- fun folderCellWidthPx() {
- assertThat(dp.folderCellWidthPx).isEqualTo(265)
- }
-
- @Test
- fun folderCellHeightPx() {
- assertThat(dp.folderCellHeightPx).isEqualTo(343)
- }
-
- @Test
- fun folderChildIconSizePx() {
- assertThat(dp.folderChildIconSizePx).isEqualTo(196)
- }
-
- @Test
- fun folderChildTextSizePx() {
- assertThat(dp.folderChildTextSizePx).isEqualTo(49)
- }
-
- @Test
- fun folderChildDrawablePaddingPx() {
- assertThat(dp.folderChildDrawablePaddingPx).isEqualTo(27)
- }
-
- @Test
- fun folderCellLayoutBorderSpaceOriginalPx() {
- assertThat(dp.folderCellLayoutBorderSpaceOriginalPx).isEqualTo(0)
- }
-
- @Test
- fun folderCellLayoutBorderSpacePxX() {
- assertThat(dp.folderCellLayoutBorderSpacePx.x).isEqualTo(0)
- }
-
- @Test
- fun folderCellLayoutBorderSpacePxY() {
- assertThat(dp.folderCellLayoutBorderSpacePx.y).isEqualTo(0)
- }
-
- @Test
- fun bottomSheetTopPadding() {
- assertThat(dp.bottomSheetTopPadding).isEqualTo(38)
- }
-
- @Test
- fun allAppsShiftRange() {
- assertThat(dp.allAppsShiftRange).isEqualTo(1050)
- }
-
- @Test
- fun allAppsTopPadding() {
- assertThat(dp.allAppsTopPadding).isEqualTo(0)
- }
-
- @Test
- fun allAppsIconSizePx() {
- assertThat(dp.allAppsIconSizePx).isEqualTo(232)
- }
-
- @Test
- fun allAppsIconTextSizePx() {
- assertThat(dp.allAppsIconTextSizePx).isEqualTo(58)
- }
-
- @Test
- fun allAppsIconDrawablePaddingPx() {
- assertThat(dp.allAppsIconDrawablePaddingPx).isEqualTo(25)
- }
-
- @Test
- fun allAppsCellHeightPx() {
- assertThat(dp.allAppsCellHeightPx).isEqualTo(409)
- }
-
- @Test
- fun allAppsCellWidthPx() {
- assertThat(dp.allAppsCellWidthPx).isEqualTo(265)
- }
-
- @Test
- fun allAppsBorderSpacePxX() {
- assertThat(dp.allAppsBorderSpacePx.x).isEqualTo(66)
- }
-
- @Test
- fun allAppsBorderSpacePxY() {
- assertThat(dp.allAppsBorderSpacePx.y).isEqualTo(66)
- }
-
- @Test
- fun numShownAllAppsColumns() {
- assertThat(dp.numShownAllAppsColumns).isEqualTo(0)
- }
-
- @Test
- fun allAppsLeftRightPadding() {
- assertThat(dp.allAppsLeftRightPadding).isEqualTo(91)
- }
-
- @Test
- fun allAppsLeftRightMargin() {
- assertThat(dp.allAppsLeftRightMargin).isEqualTo(0)
- }
-
- @Test
- fun hotseatBarSizePx() {
- assertThat(dp.hotseatBarSizePx).isEqualTo(669)
- }
-
- @Test
- fun hotseatCellHeightPx() {
- assertThat(dp.hotseatCellHeightPx).isEqualTo(221)
- }
-
- @Test
- fun hotseatBarBottomPaddingPx() {
- assertThat(dp.hotseatBarBottomSpacePx).isEqualTo(168)
- }
-
- @Test
- fun hotseatBarSidePaddingStartPx() {
- assertThat(dp.hotseatBarSidePaddingStartPx).isEqualTo(0)
- }
-
- @Test
- fun hotseatBarSidePaddingEndPx() {
- assertThat(dp.hotseatBarSidePaddingEndPx).isEqualTo(0)
- }
-
- @Test
- fun hotseatQsbSpace() {
- assertThat(dp.hotseatQsbSpace).isEqualTo(126)
- }
-
- @Test
- fun hotseatQsbHeight() {
- assertThat(dp.hotseatQsbHeight).isEqualTo(221)
- }
-
- @Test
- fun springLoadedHotseatBarTopMarginPx() {
- assertThat(dp.springLoadedHotseatBarTopMarginPx).isEqualTo(266)
- }
-
- @Test
- fun numShownHotseatIcons() {
- assertThat(dp.numShownHotseatIcons).isEqualTo(4)
- }
-
- @Test
- fun hotseatBorderSpace() {
- assertThat(dp.hotseatBorderSpace).isEqualTo(135)
- }
-
- @Test
- fun isQsbInline() {
- assertThat(dp.isQsbInline).isEqualTo(false)
- }
-
- @Test
- fun qsbWidth() {
- assertThat(dp.qsbWidth).isEqualTo(1189)
- }
-
- @Test
- fun isTaskbarPresent() {
- assertThat(dp.isTaskbarPresent).isEqualTo(false)
- }
-
- @Test
- fun isTaskbarPresentInApps() {
- assertThat(dp.isTaskbarPresentInApps).isEqualTo(false)
- }
-
- @Test
- fun taskbarSize() {
- assertThat(dp.taskbarSize).isEqualTo(0)
- }
-
- @Test
- fun desiredWorkspaceHorizontalMarginPx() {
- assertThat(dp.desiredWorkspaceHorizontalMarginPx).isEqualTo(91)
- }
-
- @Test
- fun workspacePaddingLeft() {
- assertThat(dp.workspacePadding.left).isEqualTo(53)
- }
-
- @Test
- fun workspacePaddingTop() {
- assertThat(dp.workspacePadding.top).isEqualTo(0)
- }
-
- @Test
- fun workspacePaddingRight() {
- assertThat(dp.workspacePadding.right).isEqualTo(53)
- }
-
- @Test
- fun workspacePaddingBottom() {
- assertThat(dp.workspacePadding.bottom).isEqualTo(573)
- }
-
- @Test
- fun iconScale() {
- assertThat(dp.iconScale).isEqualTo(1)
- }
-
- @Test
- fun cellScaleToFit() {
- assertThat(dp.cellScaleToFit).isEqualTo(1.182266f)
- }
-
- @Test
- fun workspaceTopPadding() {
- assertThat(dp.workspaceTopPadding).isEqualTo(0)
- }
-
- @Test
- fun workspaceBottomPadding() {
- assertThat(dp.workspaceBottomPadding).isEqualTo(0)
- }
-
- @Test
- fun overviewTaskMarginPx() {
- assertThat(dp.overviewTaskMarginPx).isEqualTo(56)
- }
-
- @Test
- fun overviewTaskMarginGridPx() {
- assertThat(dp.overviewTaskMarginGridPx).isEqualTo(0)
- }
-
- @Test
- fun overviewTaskIconSizePx() {
- assertThat(dp.overviewTaskIconSizePx).isEqualTo(168)
- }
-
- @Test
- fun overviewTaskIconDrawableSizePx() {
- assertThat(dp.overviewTaskIconDrawableSizePx).isEqualTo(154)
- }
-
- @Test
- fun overviewTaskIconDrawableSizeGridPx() {
- assertThat(dp.overviewTaskIconDrawableSizeGridPx).isEqualTo(0)
- }
-
- @Test
- fun overviewTaskThumbnailTopMarginPx() {
- assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(280)
- }
-
- @Test
- fun overviewActionsTopMarginPx() {
- assertThat(dp.overviewActionsTopMarginPx).isEqualTo(84)
- }
-
- @Test
- fun overviewActionsHeight() {
- assertThat(dp.overviewActionsHeight).isEqualTo(168)
- }
-
- @Test
- fun overviewActionsButtonSpacing() {
- assertThat(dp.overviewActionsButtonSpacing).isEqualTo(126)
- }
-
- @Test
- fun overviewPageSpacing() {
- assertThat(dp.overviewPageSpacing).isEqualTo(56)
- }
-
- @Test
- fun overviewRowSpacing() {
- assertThat(dp.overviewRowSpacing).isEqualTo(-112)
- }
-
- @Test
- fun overviewGridSideMargin() {
- assertThat(dp.overviewGridSideMargin).isEqualTo(0)
- }
-
- @Test
- fun dropTargetBarTopMarginPx() {
- assertThat(dp.dropTargetBarTopMarginPx).isEqualTo(112)
- }
-
- @Test
- fun dropTargetBarSizePx() {
- assertThat(dp.dropTargetBarSizePx).isEqualTo(196)
- }
-
- @Test
- fun dropTargetBarBottomMarginPx() {
- assertThat(dp.dropTargetBarBottomMarginPx).isEqualTo(56)
- }
-
- @Test
- fun workspaceSpringLoadedMinNextPageVisiblePx() {
- assertThat(dp.workspaceSpringLoadedMinNextPageVisiblePx).isEqualTo(84)
- }
-
- @Test
- fun getWorkspaceSpringLoadScale() {
- assertThat(dp.workspaceSpringLoadScale).isEqualTo(0.74417657f)
- }
-
- @Test
- fun getCellLayoutHeight() {
- assertThat(dp.cellLayoutHeight).isEqualTo(2447)
- }
-
- @Test
- fun getCellLayoutWidth() {
- assertThat(dp.cellLayoutWidth).isEqualTo(1334)
- }
-
- @Test
- fun getPanelCount() {
- assertThat(dp.panelCount).isEqualTo(1)
- }
-
- @Test
- fun isVerticalBarLayout() {
- assertThat(dp.isVerticalBarLayout).isEqualTo(false)
- }
-
- @Test
- fun getCellLayoutSpringLoadShrunkTop() {
- assertThat(dp.cellLayoutSpringLoadShrunkTop).isEqualTo(364)
- }
-
- @Test
- fun getCellLayoutSpringLoadShrunkBottom() {
- assertThat(dp.cellLayoutSpringLoadShrunkBottom).isEqualTo(2185)
- }
-
- @Test
- fun getQsbOffsetY() {
- assertThat(dp.qsbOffsetY).isEqualTo(147)
- }
-
- @Test
- fun getTaskbarOffsetY() {
- assertThat(dp.taskbarOffsetY).isEqualTo(558)
- }
-
- @Test
- fun getHotseatLayoutPaddingLeft() {
- assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(125)
- }
-
- @Test
- fun getHotseatLayoutPaddingTop() {
- assertThat(dp.getHotseatLayoutPadding(context).top).isEqualTo(0)
- }
-
- @Test
- fun getHotseatLayoutPaddingRight() {
- assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(125)
- }
-
- @Test
- fun getHotseatLayoutPaddingBottom() {
- assertThat(dp.getHotseatLayoutPadding(context).bottom).isEqualTo(448)
- }
-
- @Test
- fun hotseatBarEndOffset() {
- assertThat(dp.hotseatBarEndOffset).isEqualTo(0)
- }
-}
\ No newline at end of file
diff --git a/quickstep/tests/src/com/android/quickstep/DeviceProfilePhoneTest.kt b/quickstep/tests/src/com/android/quickstep/DeviceProfilePhoneTest.kt
deleted file mode 100644
index e588c71..0000000
--- a/quickstep/tests/src/com/android/quickstep/DeviceProfilePhoneTest.kt
+++ /dev/null
@@ -1,486 +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.DeviceProfile
-import com.android.launcher3.DeviceProfileBaseTest
-import com.google.common.truth.Truth.assertThat
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-
-/**
- * Tests for DeviceProfile for phone.
- */
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class DeviceProfilePhoneTest : DeviceProfileBaseTest() {
-
- lateinit var dp: DeviceProfile
-
- @Before
- fun before() {
- initializeVarsForPhone()
- dp = newDP()
- }
-
- @Test
- fun isScalableGrid() {
- assertThat(dp.isScalableGrid).isTrue()
- }
-
- @Test
- fun cellWidthPx() {
- assertThat(dp.cellWidthPx).isEqualTo(265)
- }
-
- @Test
- fun cellHeightPx() {
- assertThat(dp.cellHeightPx).isEqualTo(343)
- }
-
- @Test
- fun getCellSizeX() {
- assertThat(dp.getCellSize().x).isEqualTo(265)
- }
-
- @Test
- fun getCellSizeY() {
- assertThat(dp.getCellSize().y).isEqualTo(552)
- }
-
- @Test
- fun cellLayoutBorderSpacePxX() {
- assertThat(dp.cellLayoutBorderSpacePx.x).isEqualTo(66)
- }
-
- @Test
- fun cellLayoutBorderSpacePxY() {
- assertThat(dp.cellLayoutBorderSpacePx.y).isEqualTo(66)
- }
-
- @Test
- fun cellLayoutPaddingPxLeft() {
- assertThat(dp.cellLayoutPaddingPx.left).isEqualTo(38)
- }
-
- @Test
- fun cellLayoutPaddingPxTop() {
- assertThat(dp.cellLayoutPaddingPx.top).isEqualTo(0)
- }
-
- @Test
- fun cellLayoutPaddingPxRight() {
- assertThat(dp.cellLayoutPaddingPx.right).isEqualTo(38)
- }
-
- @Test
- fun cellLayoutPaddingPxBottom() {
- assertThat(dp.cellLayoutPaddingPx.bottom).isEqualTo(38)
- }
-
- @Test
- fun iconSizePx() {
- assertThat(dp.iconSizePx).isEqualTo(196)
- }
-
- @Test
- fun iconTextSizePx() {
- assertThat(dp.iconTextSizePx).isEqualTo(49)
- }
-
- @Test
- fun iconDrawablePaddingPx() {
- assertThat(dp.iconDrawablePaddingPx).isEqualTo(25)
- }
-
- @Test
- fun folderCellWidthPx() {
- assertThat(dp.folderCellWidthPx).isEqualTo(265)
- }
-
- @Test
- fun folderCellHeightPx() {
- assertThat(dp.folderCellHeightPx).isEqualTo(343)
- }
-
- @Test
- fun folderChildIconSizePx() {
- assertThat(dp.folderChildIconSizePx).isEqualTo(196)
- }
-
- @Test
- fun folderChildTextSizePx() {
- assertThat(dp.folderChildTextSizePx).isEqualTo(49)
- }
-
- @Test
- fun folderChildDrawablePaddingPx() {
- assertThat(dp.folderChildDrawablePaddingPx).isEqualTo(27)
- }
-
- @Test
- fun folderCellLayoutBorderSpaceOriginalPx() {
- assertThat(dp.folderCellLayoutBorderSpaceOriginalPx).isEqualTo(0)
- }
-
- @Test
- fun folderCellLayoutBorderSpacePxX() {
- assertThat(dp.folderCellLayoutBorderSpacePx.x).isEqualTo(0)
- }
-
- @Test
- fun folderCellLayoutBorderSpacePxY() {
- assertThat(dp.folderCellLayoutBorderSpacePx.y).isEqualTo(0)
- }
-
- @Test
- fun bottomSheetTopPadding() {
- assertThat(dp.bottomSheetTopPadding).isEqualTo(38)
- }
-
- @Test
- fun allAppsShiftRange() {
- assertThat(dp.allAppsShiftRange).isEqualTo(1050)
- }
-
- @Test
- fun allAppsTopPadding() {
- assertThat(dp.allAppsTopPadding).isEqualTo(0)
- }
-
- @Test
- fun allAppsIconSizePx() {
- assertThat(dp.allAppsIconSizePx).isEqualTo(232)
- }
-
- @Test
- fun allAppsIconTextSizePx() {
- assertThat(dp.allAppsIconTextSizePx).isEqualTo(58)
- }
-
- @Test
- fun allAppsIconDrawablePaddingPx() {
- assertThat(dp.allAppsIconDrawablePaddingPx).isEqualTo(25)
- }
-
- @Test
- fun allAppsCellHeightPx() {
- assertThat(dp.allAppsCellHeightPx).isEqualTo(409)
- }
-
- @Test
- fun allAppsCellWidthPx() {
- assertThat(dp.allAppsCellWidthPx).isEqualTo(265)
- }
-
- @Test
- fun allAppsBorderSpacePxX() {
- assertThat(dp.allAppsBorderSpacePx.x).isEqualTo(66)
- }
-
- @Test
- fun allAppsBorderSpacePxY() {
- assertThat(dp.allAppsBorderSpacePx.y).isEqualTo(66)
- }
-
- @Test
- fun numShownAllAppsColumns() {
- assertThat(dp.numShownAllAppsColumns).isEqualTo(0)
- }
-
- @Test
- fun allAppsLeftRightPadding() {
- assertThat(dp.allAppsLeftRightPadding).isEqualTo(91)
- }
-
- @Test
- fun allAppsLeftRightMargin() {
- assertThat(dp.allAppsLeftRightMargin).isEqualTo(0)
- }
-
- @Test
- fun hotseatBarSizePx() {
- assertThat(dp.hotseatBarSizePx).isEqualTo(669)
- }
-
- @Test
- fun hotseatCellHeightPx() {
- assertThat(dp.hotseatCellHeightPx).isEqualTo(221)
- }
-
- @Test
- fun hotseatBarBottomPaddingPx() {
- assertThat(dp.hotseatBarBottomSpacePx).isEqualTo(168)
- }
-
- @Test
- fun hotseatBarSidePaddingStartPx() {
- assertThat(dp.hotseatBarSidePaddingStartPx).isEqualTo(0)
- }
-
- @Test
- fun hotseatBarSidePaddingEndPx() {
- assertThat(dp.hotseatBarSidePaddingEndPx).isEqualTo(0)
- }
-
- @Test
- fun hotseatQsbSpace() {
- assertThat(dp.hotseatQsbSpace).isEqualTo(126)
- }
-
- @Test
- fun hotseatQsbHeight() {
- assertThat(dp.hotseatQsbHeight).isEqualTo(221)
- }
-
- @Test
- fun springLoadedHotseatBarTopMarginPx() {
- assertThat(dp.springLoadedHotseatBarTopMarginPx).isEqualTo(266)
- }
-
- @Test
- fun numShownHotseatIcons() {
- assertThat(dp.numShownHotseatIcons).isEqualTo(4)
- }
-
- @Test
- fun hotseatBorderSpace() {
- assertThat(dp.hotseatBorderSpace).isEqualTo(135)
- }
-
- @Test
- fun isQsbInline() {
- assertThat(dp.isQsbInline).isEqualTo(false)
- }
-
- @Test
- fun qsbWidth() {
- assertThat(dp.qsbWidth).isEqualTo(1189)
- }
-
- @Test
- fun isTaskbarPresent() {
- assertThat(dp.isTaskbarPresent).isEqualTo(false)
- }
-
- @Test
- fun isTaskbarPresentInApps() {
- assertThat(dp.isTaskbarPresentInApps).isEqualTo(false)
- }
-
- @Test
- fun taskbarSize() {
- assertThat(dp.taskbarSize).isEqualTo(0)
- }
-
- @Test
- fun desiredWorkspaceHorizontalMarginPx() {
- assertThat(dp.desiredWorkspaceHorizontalMarginPx).isEqualTo(91)
- }
-
- @Test
- fun workspacePaddingLeft() {
- assertThat(dp.workspacePadding.left).isEqualTo(53)
- }
-
- @Test
- fun workspacePaddingTop() {
- assertThat(dp.workspacePadding.top).isEqualTo(0)
- }
-
- @Test
- fun workspacePaddingRight() {
- assertThat(dp.workspacePadding.right).isEqualTo(53)
- }
-
- @Test
- fun workspacePaddingBottom() {
- assertThat(dp.workspacePadding.bottom).isEqualTo(573)
- }
-
- @Test
- fun iconScale() {
- assertThat(dp.iconScale).isEqualTo(1)
- }
-
- @Test
- fun cellScaleToFit() {
- assertThat(dp.cellScaleToFit).isEqualTo(1.182266f)
- }
-
- @Test
- fun workspaceTopPadding() {
- assertThat(dp.workspaceTopPadding).isEqualTo(0)
- }
-
- @Test
- fun workspaceBottomPadding() {
- assertThat(dp.workspaceBottomPadding).isEqualTo(0)
- }
-
- @Test
- fun overviewTaskMarginPx() {
- assertThat(dp.overviewTaskMarginPx).isEqualTo(56)
- }
-
- @Test
- fun overviewTaskMarginGridPx() {
- assertThat(dp.overviewTaskMarginGridPx).isEqualTo(0)
- }
-
- @Test
- fun overviewTaskIconSizePx() {
- assertThat(dp.overviewTaskIconSizePx).isEqualTo(168)
- }
-
- @Test
- fun overviewTaskIconDrawableSizePx() {
- assertThat(dp.overviewTaskIconDrawableSizePx).isEqualTo(154)
- }
-
- @Test
- fun overviewTaskIconDrawableSizeGridPx() {
- assertThat(dp.overviewTaskIconDrawableSizeGridPx).isEqualTo(0)
- }
-
- @Test
- fun overviewTaskThumbnailTopMarginPx() {
- assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(280)
- }
-
- @Test
- fun overviewActionsTopMarginPx() {
- assertThat(dp.overviewActionsTopMarginPx).isEqualTo(84)
- }
-
- @Test
- fun overviewActionsHeight() {
- assertThat(dp.overviewActionsHeight).isEqualTo(168)
- }
-
- @Test
- fun overviewActionsButtonSpacing() {
- assertThat(dp.overviewActionsButtonSpacing).isEqualTo(126)
- }
-
- @Test
- fun overviewPageSpacing() {
- assertThat(dp.overviewPageSpacing).isEqualTo(56)
- }
-
- @Test
- fun overviewRowSpacing() {
- assertThat(dp.overviewRowSpacing).isEqualTo(-112)
- }
-
- @Test
- fun overviewGridSideMargin() {
- assertThat(dp.overviewGridSideMargin).isEqualTo(0)
- }
-
- @Test
- fun dropTargetBarTopMarginPx() {
- assertThat(dp.dropTargetBarTopMarginPx).isEqualTo(112)
- }
-
- @Test
- fun dropTargetBarSizePx() {
- assertThat(dp.dropTargetBarSizePx).isEqualTo(196)
- }
-
- @Test
- fun dropTargetBarBottomMarginPx() {
- assertThat(dp.dropTargetBarBottomMarginPx).isEqualTo(56)
- }
-
- @Test
- fun workspaceSpringLoadedMinNextPageVisiblePx() {
- assertThat(dp.workspaceSpringLoadedMinNextPageVisiblePx).isEqualTo(84)
- }
-
- @Test
- fun getWorkspaceSpringLoadScale() {
- assertThat(dp.workspaceSpringLoadScale).isEqualTo(0.74417657f)
- }
-
- @Test
- fun getCellLayoutHeight() {
- assertThat(dp.cellLayoutHeight).isEqualTo(2447)
- }
-
- @Test
- fun getCellLayoutWidth() {
- assertThat(dp.cellLayoutWidth).isEqualTo(1334)
- }
-
- @Test
- fun getPanelCount() {
- assertThat(dp.panelCount).isEqualTo(1)
- }
-
- @Test
- fun isVerticalBarLayout() {
- assertThat(dp.isVerticalBarLayout).isEqualTo(false)
- }
-
- @Test
- fun getCellLayoutSpringLoadShrunkTop() {
- assertThat(dp.cellLayoutSpringLoadShrunkTop).isEqualTo(364)
- }
-
- @Test
- fun getCellLayoutSpringLoadShrunkBottom() {
- assertThat(dp.cellLayoutSpringLoadShrunkBottom).isEqualTo(2185)
- }
-
- @Test
- fun getQsbOffsetY() {
- assertThat(dp.qsbOffsetY).isEqualTo(147)
- }
-
- @Test
- fun getTaskbarOffsetY() {
- assertThat(dp.taskbarOffsetY).isEqualTo(558)
- }
-
- @Test
- fun getHotseatLayoutPaddingLeft() {
- assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(125)
- }
-
- @Test
- fun getHotseatLayoutPaddingTop() {
- assertThat(dp.getHotseatLayoutPadding(context).top).isEqualTo(0)
- }
-
- @Test
- fun getHotseatLayoutPaddingRight() {
- assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(125)
- }
-
- @Test
- fun getHotseatLayoutPaddingBottom() {
- assertThat(dp.getHotseatLayoutPadding(context).bottom).isEqualTo(448)
- }
-
- @Test
- fun hotseatBarEndOffset() {
- assertThat(dp.hotseatBarEndOffset).isEqualTo(0)
- }
-}
\ No newline at end of file
diff --git a/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletLandscape3ButtonTest.kt b/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletLandscape3ButtonTest.kt
deleted file mode 100644
index d0c9346..0000000
--- a/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletLandscape3ButtonTest.kt
+++ /dev/null
@@ -1,486 +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.DeviceProfile
-import com.android.launcher3.DeviceProfileBaseTest
-import com.google.common.truth.Truth.assertThat
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-
-/**
- * Tests for DeviceProfile for tablet in landscape with 3-Button navigation.
- */
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class DeviceProfileTabletLandscape3ButtonTest : DeviceProfileBaseTest() {
-
- lateinit var dp: DeviceProfile
-
- @Before
- fun before() {
- initializeVarsForTablet(isLandscape = true, isGestureMode = false)
- dp = newDP()
- }
-
- @Test
- fun isScalableGrid() {
- assertThat(dp.isScalableGrid).isTrue()
- }
-
- @Test
- fun cellWidthPx() {
- assertThat(dp.cellWidthPx).isEqualTo(227)
- }
-
- @Test
- fun cellHeightPx() {
- assertThat(dp.cellHeightPx).isEqualTo(294)
- }
-
- @Test
- fun getCellSizeX() {
- assertThat(dp.cellSize.x).isEqualTo(558)
- }
-
- @Test
- fun getCellSizeY() {
- assertThat(dp.cellSize.y).isEqualTo(294)
- }
-
- @Test
- fun cellLayoutBorderSpacePxX() {
- assertThat(dp.cellLayoutBorderSpacePx.x).isEqualTo(57)
- }
-
- @Test
- fun cellLayoutBorderSpacePxY() {
- assertThat(dp.cellLayoutBorderSpacePx.y).isEqualTo(57)
- }
-
- @Test
- fun cellLayoutPaddingPxLeft() {
- assertThat(dp.cellLayoutPaddingPx.left).isEqualTo(59)
- }
-
- @Test
- fun cellLayoutPaddingPxTop() {
- assertThat(dp.cellLayoutPaddingPx.top).isEqualTo(0)
- }
-
- @Test
- fun cellLayoutPaddingPxRight() {
- assertThat(dp.cellLayoutPaddingPx.right).isEqualTo(59)
- }
-
- @Test
- fun cellLayoutPaddingPxBottom() {
- assertThat(dp.cellLayoutPaddingPx.bottom).isEqualTo(59)
- }
-
- @Test
- fun iconSizePx() {
- assertThat(dp.iconSizePx).isEqualTo(112)
- }
-
- @Test
- fun iconTextSizePx() {
- assertThat(dp.iconTextSizePx).isEqualTo(28)
- }
-
- @Test
- fun iconDrawablePaddingPx() {
- assertThat(dp.iconDrawablePaddingPx).isEqualTo(14)
- }
-
- @Test
- fun folderCellWidthPx() {
- assertThat(dp.folderCellWidthPx).isEqualTo(227)
- }
-
- @Test
- fun folderCellHeightPx() {
- assertThat(dp.folderCellHeightPx).isEqualTo(294)
- }
-
- @Test
- fun folderChildIconSizePx() {
- assertThat(dp.folderChildIconSizePx).isEqualTo(112)
- }
-
- @Test
- fun folderChildTextSizePx() {
- assertThat(dp.folderChildTextSizePx).isEqualTo(28)
- }
-
- @Test
- fun folderChildDrawablePaddingPx() {
- assertThat(dp.folderChildDrawablePaddingPx).isEqualTo(48)
- }
-
- @Test
- fun folderCellLayoutBorderSpaceOriginalPx() {
- assertThat(dp.folderCellLayoutBorderSpaceOriginalPx).isEqualTo(0)
- }
-
- @Test
- fun folderCellLayoutBorderSpacePxX() {
- assertThat(dp.folderCellLayoutBorderSpacePx.x).isEqualTo(0)
- }
-
- @Test
- fun folderCellLayoutBorderSpacePxY() {
- assertThat(dp.folderCellLayoutBorderSpacePx.y).isEqualTo(0)
- }
-
- @Test
- fun bottomSheetTopPadding() {
- assertThat(dp.bottomSheetTopPadding).isEqualTo(0)
- }
-
- @Test
- fun allAppsShiftRange() {
- assertThat(dp.allAppsShiftRange).isEqualTo(1600)
- }
-
- @Test
- fun allAppsTopPadding() {
- assertThat(dp.allAppsTopPadding).isEqualTo(0)
- }
-
- @Test
- fun allAppsIconSizePx() {
- assertThat(dp.allAppsIconSizePx).isEqualTo(199)
- }
-
- @Test
- fun allAppsIconTextSizePx() {
- assertThat(dp.allAppsIconTextSizePx).isEqualTo(50)
- }
-
- @Test
- fun allAppsIconDrawablePaddingPx() {
- assertThat(dp.allAppsIconDrawablePaddingPx).isEqualTo(14)
- }
-
- @Test
- fun allAppsCellHeightPx() {
- assertThat(dp.allAppsCellHeightPx).isEqualTo(351)
- }
-
- @Test
- fun allAppsCellWidthPx() {
- assertThat(dp.allAppsCellWidthPx).isEqualTo(227)
- }
-
- @Test
- fun allAppsBorderSpacePxX() {
- assertThat(dp.allAppsBorderSpacePx.x).isEqualTo(57)
- }
-
- @Test
- fun allAppsBorderSpacePxY() {
- assertThat(dp.allAppsBorderSpacePx.y).isEqualTo(57)
- }
-
- @Test
- fun numShownAllAppsColumns() {
- assertThat(dp.numShownAllAppsColumns).isEqualTo(0)
- }
-
- @Test
- fun allAppsLeftRightPadding() {
- assertThat(dp.allAppsLeftRightPadding).isEqualTo(64)
- }
-
- @Test
- fun allAppsLeftRightMargin() {
- assertThat(dp.allAppsLeftRightMargin).isEqualTo(1244)
- }
-
- @Test
- fun hotseatBarSizePx() {
- assertThat(dp.hotseatBarSizePx).isEqualTo(228)
- }
-
- @Test
- fun hotseatCellHeightPx() {
- assertThat(dp.hotseatCellHeightPx).isEqualTo(126)
- }
-
- @Test
- fun hotseatBarBottomSpacePx() {
- assertThat(dp.hotseatBarBottomSpacePx).isEqualTo(116)
- }
-
- @Test
- fun hotseatBarSidePaddingStartPx() {
- assertThat(dp.hotseatBarSidePaddingStartPx).isEqualTo(0)
- }
-
- @Test
- fun hotseatBarSidePaddingEndPx() {
- assertThat(dp.hotseatBarSidePaddingEndPx).isEqualTo(0)
- }
-
- @Test
- fun hotseatQsbSpace() {
- assertThat(dp.hotseatQsbSpace).isEqualTo(56)
- }
-
- @Test
- fun hotseatQsbHeight() {
- assertThat(dp.hotseatQsbHeight).isEqualTo(126)
- }
-
- @Test
- fun springLoadedHotseatBarTopMarginPx() {
- assertThat(dp.springLoadedHotseatBarTopMarginPx).isEqualTo(128)
- }
-
- @Test
- fun numShownHotseatIcons() {
- assertThat(dp.numShownHotseatIcons).isEqualTo(5)
- }
-
- @Test
- fun hotseatBorderSpace() {
- assertThat(dp.hotseatBorderSpace).isEqualTo(32)
- }
-
- @Test
- fun isQsbInline() {
- assertThat(dp.isQsbInline).isEqualTo(true)
- }
-
- @Test
- fun qsbWidth() {
- assertThat(dp.qsbWidth).isEqualTo(1237)
- }
-
- @Test
- fun isTaskbarPresent() {
- assertThat(dp.isTaskbarPresent).isEqualTo(true)
- }
-
- @Test
- fun isTaskbarPresentInApps() {
- assertThat(dp.isTaskbarPresentInApps).isEqualTo(false)
- }
-
- @Test
- fun taskbarSize() {
- assertThat(dp.taskbarSize).isEqualTo(120)
- }
-
- @Test
- fun desiredWorkspaceHorizontalMarginPx() {
- assertThat(dp.desiredWorkspaceHorizontalMarginPx).isEqualTo(78)
- }
-
- @Test
- fun workspacePaddingLeft() {
- assertThat(dp.workspacePadding.left).isEqualTo(19)
- }
-
- @Test
- fun workspacePaddingTop() {
- assertThat(dp.workspacePadding.top).isEqualTo(0)
- }
-
- @Test
- fun workspacePaddingRight() {
- assertThat(dp.workspacePadding.right).isEqualTo(19)
- }
-
- @Test
- fun workspacePaddingBottom() {
- assertThat(dp.workspacePadding.bottom).isEqualTo(93)
- }
-
- @Test
- fun iconScale() {
- assertThat(dp.iconScale).isEqualTo(1)
- }
-
- @Test
- fun cellScaleToFit() {
- assertThat(dp.cellScaleToFit).isEqualTo(1.7736843f)
- }
-
- @Test
- fun workspaceTopPadding() {
- assertThat(dp.workspaceTopPadding).isEqualTo(0)
- }
-
- @Test
- fun workspaceBottomPadding() {
- assertThat(dp.workspaceBottomPadding).isEqualTo(0)
- }
-
- @Test
- fun overviewTaskMarginPx() {
- assertThat(dp.overviewTaskMarginPx).isEqualTo(32)
- }
-
- @Test
- fun overviewTaskMarginGridPx() {
- assertThat(dp.overviewTaskMarginGridPx).isEqualTo(32)
- }
-
- @Test
- fun overviewTaskIconSizePx() {
- assertThat(dp.overviewTaskIconSizePx).isEqualTo(96)
- }
-
- @Test
- fun overviewTaskIconDrawableSizePx() {
- assertThat(dp.overviewTaskIconDrawableSizePx).isEqualTo(88)
- }
-
- @Test
- fun overviewTaskIconDrawableSizeGridPx() {
- assertThat(dp.overviewTaskIconDrawableSizeGridPx).isEqualTo(88)
- }
-
- @Test
- fun overviewTaskThumbnailTopMarginPx() {
- assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(160)
- }
-
- @Test
- fun overviewActionsTopMarginPx() {
- assertThat(dp.overviewActionsTopMarginPx).isEqualTo(40)
- }
-
- @Test
- fun overviewActionsHeight() {
- assertThat(dp.overviewActionsHeight).isEqualTo(96)
- }
-
- @Test
- fun overviewActionsButtonSpacing() {
- assertThat(dp.overviewActionsButtonSpacing).isEqualTo(72)
- }
-
- @Test
- fun overviewPageSpacing() {
- assertThat(dp.overviewPageSpacing).isEqualTo(88)
- }
-
- @Test
- fun overviewRowSpacing() {
- assertThat(dp.overviewRowSpacing).isEqualTo(40)
- }
-
- @Test
- fun overviewGridSideMargin() {
- assertThat(dp.overviewGridSideMargin).isEqualTo(128)
- }
-
- @Test
- fun dropTargetBarTopMarginPx() {
- assertThat(dp.dropTargetBarTopMarginPx).isEqualTo(0)
- }
-
- @Test
- fun dropTargetBarSizePx() {
- assertThat(dp.dropTargetBarSizePx).isEqualTo(144)
- }
-
- @Test
- fun dropTargetBarBottomMarginPx() {
- assertThat(dp.dropTargetBarBottomMarginPx).isEqualTo(64)
- }
-
- @Test
- fun workspaceSpringLoadedMinNextPageVisiblePx() {
- assertThat(dp.workspaceSpringLoadedMinNextPageVisiblePx).isEqualTo(48)
- }
-
- @Test
- fun getWorkspaceSpringLoadScale() {
- assertThat(dp.workspaceSpringLoadScale).isEqualTo(0.7363184f)
- }
-
- @Test
- fun getCellLayoutHeight() {
- assertThat(dp.cellLayoutHeight).isEqualTo(1407)
- }
-
- @Test
- fun getCellLayoutWidth() {
- assertThat(dp.cellLayoutWidth).isEqualTo(2522)
- }
-
- @Test
- fun getPanelCount() {
- assertThat(dp.panelCount).isEqualTo(1)
- }
-
- @Test
- fun isVerticalBarLayout() {
- assertThat(dp.isVerticalBarLayout).isEqualTo(false)
- }
-
- @Test
- fun getCellLayoutSpringLoadShrunkTop() {
- assertThat(dp.cellLayoutSpringLoadShrunkTop).isEqualTo(208)
- }
-
- @Test
- fun getCellLayoutSpringLoadShrunkBottom() {
- assertThat(dp.cellLayoutSpringLoadShrunkBottom).isEqualTo(1244)
- }
-
- @Test
- fun getQsbOffsetY() {
- assertThat(dp.qsbOffsetY).isEqualTo(109)
- }
-
- @Test
- fun getTaskbarOffsetY() {
- assertThat(dp.taskbarOffsetY).isEqualTo(112)
- }
-
- @Test
- fun getHotseatLayoutPaddingLeft() {
- assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(1305)
- }
-
- @Test
- fun getHotseatLayoutPaddingTop() {
- assertThat(dp.getHotseatLayoutPadding(context).top).isEqualTo(-7)
- }
-
- @Test
- fun getHotseatLayoutPaddingRight() {
- assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(566)
- }
-
- @Test
- fun getHotseatLayoutPaddingBottom() {
- assertThat(dp.getHotseatLayoutPadding(context).bottom).isEqualTo(109)
- }
-
- @Test
- fun hotseatBarEndOffset() {
- assertThat(dp.hotseatBarEndOffset).isEqualTo(566)
- }
-}
\ No newline at end of file
diff --git a/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletLandscapeTest.kt b/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletLandscapeTest.kt
deleted file mode 100644
index 456ed2c..0000000
--- a/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletLandscapeTest.kt
+++ /dev/null
@@ -1,486 +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.DeviceProfile
-import com.android.launcher3.DeviceProfileBaseTest
-import com.google.common.truth.Truth.assertThat
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-
-/**
- * Tests for DeviceProfile for tablet in landscape.
- */
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class DeviceProfileTabletLandscapeTest : DeviceProfileBaseTest() {
-
- lateinit var dp: DeviceProfile
-
- @Before
- fun before() {
- initializeVarsForTablet(isLandscape = true)
- dp = newDP()
- }
-
- @Test
- fun isScalableGrid() {
- assertThat(dp.isScalableGrid).isTrue()
- }
-
- @Test
- fun cellWidthPx() {
- assertThat(dp.cellWidthPx).isEqualTo(227)
- }
-
- @Test
- fun cellHeightPx() {
- assertThat(dp.cellHeightPx).isEqualTo(294)
- }
-
- @Test
- fun getCellSizeX() {
- assertThat(dp.cellSize.x).isEqualTo(558)
- }
-
- @Test
- fun getCellSizeY() {
- assertThat(dp.cellSize.y).isEqualTo(294)
- }
-
- @Test
- fun cellLayoutBorderSpacePxX() {
- assertThat(dp.cellLayoutBorderSpacePx.x).isEqualTo(57)
- }
-
- @Test
- fun cellLayoutBorderSpacePxY() {
- assertThat(dp.cellLayoutBorderSpacePx.y).isEqualTo(57)
- }
-
- @Test
- fun cellLayoutPaddingPxLeft() {
- assertThat(dp.cellLayoutPaddingPx.left).isEqualTo(59)
- }
-
- @Test
- fun cellLayoutPaddingPxTop() {
- assertThat(dp.cellLayoutPaddingPx.top).isEqualTo(0)
- }
-
- @Test
- fun cellLayoutPaddingPxRight() {
- assertThat(dp.cellLayoutPaddingPx.right).isEqualTo(59)
- }
-
- @Test
- fun cellLayoutPaddingPxBottom() {
- assertThat(dp.cellLayoutPaddingPx.bottom).isEqualTo(59)
- }
-
- @Test
- fun iconSizePx() {
- assertThat(dp.iconSizePx).isEqualTo(112)
- }
-
- @Test
- fun iconTextSizePx() {
- assertThat(dp.iconTextSizePx).isEqualTo(28)
- }
-
- @Test
- fun iconDrawablePaddingPx() {
- assertThat(dp.iconDrawablePaddingPx).isEqualTo(14)
- }
-
- @Test
- fun folderCellWidthPx() {
- assertThat(dp.folderCellWidthPx).isEqualTo(227)
- }
-
- @Test
- fun folderCellHeightPx() {
- assertThat(dp.folderCellHeightPx).isEqualTo(294)
- }
-
- @Test
- fun folderChildIconSizePx() {
- assertThat(dp.folderChildIconSizePx).isEqualTo(112)
- }
-
- @Test
- fun folderChildTextSizePx() {
- assertThat(dp.folderChildTextSizePx).isEqualTo(28)
- }
-
- @Test
- fun folderChildDrawablePaddingPx() {
- assertThat(dp.folderChildDrawablePaddingPx).isEqualTo(48)
- }
-
- @Test
- fun folderCellLayoutBorderSpaceOriginalPx() {
- assertThat(dp.folderCellLayoutBorderSpaceOriginalPx).isEqualTo(0)
- }
-
- @Test
- fun folderCellLayoutBorderSpacePxX() {
- assertThat(dp.folderCellLayoutBorderSpacePx.x).isEqualTo(0)
- }
-
- @Test
- fun folderCellLayoutBorderSpacePxY() {
- assertThat(dp.folderCellLayoutBorderSpacePx.y).isEqualTo(0)
- }
-
- @Test
- fun bottomSheetTopPadding() {
- assertThat(dp.bottomSheetTopPadding).isEqualTo(0)
- }
-
- @Test
- fun allAppsShiftRange() {
- assertThat(dp.allAppsShiftRange).isEqualTo(1600)
- }
-
- @Test
- fun allAppsTopPadding() {
- assertThat(dp.allAppsTopPadding).isEqualTo(0)
- }
-
- @Test
- fun allAppsIconSizePx() {
- assertThat(dp.allAppsIconSizePx).isEqualTo(199)
- }
-
- @Test
- fun allAppsIconTextSizePx() {
- assertThat(dp.allAppsIconTextSizePx).isEqualTo(50)
- }
-
- @Test
- fun allAppsIconDrawablePaddingPx() {
- assertThat(dp.allAppsIconDrawablePaddingPx).isEqualTo(14)
- }
-
- @Test
- fun allAppsCellHeightPx() {
- assertThat(dp.allAppsCellHeightPx).isEqualTo(351)
- }
-
- @Test
- fun allAppsCellWidthPx() {
- assertThat(dp.allAppsCellWidthPx).isEqualTo(227)
- }
-
- @Test
- fun allAppsBorderSpacePxX() {
- assertThat(dp.allAppsBorderSpacePx.x).isEqualTo(57)
- }
-
- @Test
- fun allAppsBorderSpacePxY() {
- assertThat(dp.allAppsBorderSpacePx.y).isEqualTo(57)
- }
-
- @Test
- fun numShownAllAppsColumns() {
- assertThat(dp.numShownAllAppsColumns).isEqualTo(0)
- }
-
- @Test
- fun allAppsLeftRightPadding() {
- assertThat(dp.allAppsLeftRightPadding).isEqualTo(64)
- }
-
- @Test
- fun allAppsLeftRightMargin() {
- assertThat(dp.allAppsLeftRightMargin).isEqualTo(1244)
- }
-
- @Test
- fun hotseatBarSizePx() {
- assertThat(dp.hotseatBarSizePx).isEqualTo(228)
- }
-
- @Test
- fun hotseatCellHeightPx() {
- assertThat(dp.hotseatCellHeightPx).isEqualTo(126)
- }
-
- @Test
- fun hotseatBarBottomSpacePx() {
- assertThat(dp.hotseatBarBottomSpacePx).isEqualTo(116)
- }
-
- @Test
- fun hotseatBarSidePaddingStartPx() {
- assertThat(dp.hotseatBarSidePaddingStartPx).isEqualTo(0)
- }
-
- @Test
- fun hotseatBarSidePaddingEndPx() {
- assertThat(dp.hotseatBarSidePaddingEndPx).isEqualTo(0)
- }
-
- @Test
- fun hotseatQsbSpace() {
- assertThat(dp.hotseatQsbSpace).isEqualTo(56)
- }
-
- @Test
- fun hotseatQsbHeight() {
- assertThat(dp.hotseatQsbHeight).isEqualTo(126)
- }
-
- @Test
- fun springLoadedHotseatBarTopMarginPx() {
- assertThat(dp.springLoadedHotseatBarTopMarginPx).isEqualTo(128)
- }
-
- @Test
- fun numShownHotseatIcons() {
- assertThat(dp.numShownHotseatIcons).isEqualTo(4)
- }
-
- @Test
- fun hotseatBorderSpace() {
- assertThat(dp.hotseatBorderSpace).isEqualTo(503)
- }
-
- @Test
- fun isQsbInline() {
- assertThat(dp.isQsbInline).isEqualTo(true)
- }
-
- @Test
- fun qsbWidth() {
- assertThat(dp.qsbWidth).isEqualTo(-503)
- }
-
- @Test
- fun isTaskbarPresent() {
- assertThat(dp.isTaskbarPresent).isEqualTo(true)
- }
-
- @Test
- fun isTaskbarPresentInApps() {
- assertThat(dp.isTaskbarPresentInApps).isEqualTo(false)
- }
-
- @Test
- fun taskbarSize() {
- assertThat(dp.taskbarSize).isEqualTo(120)
- }
-
- @Test
- fun desiredWorkspaceHorizontalMarginPx() {
- assertThat(dp.desiredWorkspaceHorizontalMarginPx).isEqualTo(78)
- }
-
- @Test
- fun workspacePaddingLeft() {
- assertThat(dp.workspacePadding.left).isEqualTo(19)
- }
-
- @Test
- fun workspacePaddingTop() {
- assertThat(dp.workspacePadding.top).isEqualTo(0)
- }
-
- @Test
- fun workspacePaddingRight() {
- assertThat(dp.workspacePadding.right).isEqualTo(19)
- }
-
- @Test
- fun workspacePaddingBottom() {
- assertThat(dp.workspacePadding.bottom).isEqualTo(93)
- }
-
- @Test
- fun iconScale() {
- assertThat(dp.iconScale).isEqualTo(1)
- }
-
- @Test
- fun cellScaleToFit() {
- assertThat(dp.cellScaleToFit).isEqualTo(1.7736843f)
- }
-
- @Test
- fun workspaceTopPadding() {
- assertThat(dp.workspaceTopPadding).isEqualTo(0)
- }
-
- @Test
- fun workspaceBottomPadding() {
- assertThat(dp.workspaceBottomPadding).isEqualTo(0)
- }
-
- @Test
- fun overviewTaskMarginPx() {
- assertThat(dp.overviewTaskMarginPx).isEqualTo(32)
- }
-
- @Test
- fun overviewTaskMarginGridPx() {
- assertThat(dp.overviewTaskMarginGridPx).isEqualTo(32)
- }
-
- @Test
- fun overviewTaskIconSizePx() {
- assertThat(dp.overviewTaskIconSizePx).isEqualTo(96)
- }
-
- @Test
- fun overviewTaskIconDrawableSizePx() {
- assertThat(dp.overviewTaskIconDrawableSizePx).isEqualTo(88)
- }
-
- @Test
- fun overviewTaskIconDrawableSizeGridPx() {
- assertThat(dp.overviewTaskIconDrawableSizeGridPx).isEqualTo(88)
- }
-
- @Test
- fun overviewTaskThumbnailTopMarginPx() {
- assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(160)
- }
-
- @Test
- fun overviewActionsTopMarginPx() {
- assertThat(dp.overviewActionsTopMarginPx).isEqualTo(40)
- }
-
- @Test
- fun overviewActionsHeight() {
- assertThat(dp.overviewActionsHeight).isEqualTo(96)
- }
-
- @Test
- fun overviewActionsButtonSpacing() {
- assertThat(dp.overviewActionsButtonSpacing).isEqualTo(72)
- }
-
- @Test
- fun overviewPageSpacing() {
- assertThat(dp.overviewPageSpacing).isEqualTo(88)
- }
-
- @Test
- fun overviewRowSpacing() {
- assertThat(dp.overviewRowSpacing).isEqualTo(40)
- }
-
- @Test
- fun overviewGridSideMargin() {
- assertThat(dp.overviewGridSideMargin).isEqualTo(128)
- }
-
- @Test
- fun dropTargetBarTopMarginPx() {
- assertThat(dp.dropTargetBarTopMarginPx).isEqualTo(0)
- }
-
- @Test
- fun dropTargetBarSizePx() {
- assertThat(dp.dropTargetBarSizePx).isEqualTo(144)
- }
-
- @Test
- fun dropTargetBarBottomMarginPx() {
- assertThat(dp.dropTargetBarBottomMarginPx).isEqualTo(64)
- }
-
- @Test
- fun workspaceSpringLoadedMinNextPageVisiblePx() {
- assertThat(dp.workspaceSpringLoadedMinNextPageVisiblePx).isEqualTo(48)
- }
-
- @Test
- fun getWorkspaceSpringLoadScale() {
- assertThat(dp.workspaceSpringLoadScale).isEqualTo(0.7363184f)
- }
-
- @Test
- fun getCellLayoutHeight() {
- assertThat(dp.cellLayoutHeight).isEqualTo(1407)
- }
-
- @Test
- fun getCellLayoutWidth() {
- assertThat(dp.cellLayoutWidth).isEqualTo(2522)
- }
-
- @Test
- fun getPanelCount() {
- assertThat(dp.panelCount).isEqualTo(1)
- }
-
- @Test
- fun isVerticalBarLayout() {
- assertThat(dp.isVerticalBarLayout).isEqualTo(false)
- }
-
- @Test
- fun getCellLayoutSpringLoadShrunkTop() {
- assertThat(dp.cellLayoutSpringLoadShrunkTop).isEqualTo(208)
- }
-
- @Test
- fun getCellLayoutSpringLoadShrunkBottom() {
- assertThat(dp.cellLayoutSpringLoadShrunkBottom).isEqualTo(1244)
- }
-
- @Test
- fun getQsbOffsetY() {
- assertThat(dp.qsbOffsetY).isEqualTo(109)
- }
-
- @Test
- fun getTaskbarOffsetY() {
- assertThat(dp.taskbarOffsetY).isEqualTo(112)
- }
-
- @Test
- fun getHotseatLayoutPaddingLeft() {
- assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(301)
- }
-
- @Test
- fun getHotseatLayoutPaddingTop() {
- assertThat(dp.getHotseatLayoutPadding(context).top).isEqualTo(-7)
- }
-
- @Test
- fun getHotseatLayoutPaddingRight() {
- assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(301)
- }
-
- @Test
- fun getHotseatLayoutPaddingBottom() {
- assertThat(dp.getHotseatLayoutPadding(context).bottom).isEqualTo(109)
- }
-
- @Test
- fun hotseatBarEndOffset() {
- assertThat(dp.hotseatBarEndOffset).isEqualTo(0)
- }
-}
\ No newline at end of file
diff --git a/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletPortrait3ButtonTest.kt b/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletPortrait3ButtonTest.kt
deleted file mode 100644
index eed3598..0000000
--- a/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletPortrait3ButtonTest.kt
+++ /dev/null
@@ -1,486 +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.DeviceProfile
-import com.android.launcher3.DeviceProfileBaseTest
-import com.google.common.truth.Truth.assertThat
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-
-/**
- * Tests for DeviceProfile for tablet in portrait with 3-Button navigation.
- */
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class DeviceProfileTabletPortrait3ButtonTest : DeviceProfileBaseTest() {
-
- lateinit var dp: DeviceProfile
-
- @Before
- fun before() {
- initializeVarsForTablet(isGestureMode = false)
- dp = newDP()
- }
-
- @Test
- fun isScalableGrid() {
- assertThat(dp.isScalableGrid).isTrue()
- }
-
- @Test
- fun cellWidthPx() {
- assertThat(dp.cellWidthPx).isEqualTo(294)
- }
-
- @Test
- fun cellHeightPx() {
- assertThat(dp.cellHeightPx).isEqualTo(382)
- }
-
- @Test
- fun getCellSizeX() {
- assertThat(dp.cellSize.x).isEqualTo(294)
- }
-
- @Test
- fun getCellSizeY() {
- assertThat(dp.cellSize.y).isEqualTo(482)
- }
-
- @Test
- fun cellLayoutBorderSpacePxX() {
- assertThat(dp.cellLayoutBorderSpacePx.x).isEqualTo(74)
- }
-
- @Test
- fun cellLayoutBorderSpacePxY() {
- assertThat(dp.cellLayoutBorderSpacePx.y).isEqualTo(74)
- }
-
- @Test
- fun cellLayoutPaddingPxLeft() {
- assertThat(dp.cellLayoutPaddingPx.left).isEqualTo(72)
- }
-
- @Test
- fun cellLayoutPaddingPxTop() {
- assertThat(dp.cellLayoutPaddingPx.top).isEqualTo(0)
- }
-
- @Test
- fun cellLayoutPaddingPxRight() {
- assertThat(dp.cellLayoutPaddingPx.right).isEqualTo(72)
- }
-
- @Test
- fun cellLayoutPaddingPxBottom() {
- assertThat(dp.cellLayoutPaddingPx.bottom).isEqualTo(72)
- }
-
- @Test
- fun iconSizePx() {
- assertThat(dp.iconSizePx).isEqualTo(112)
- }
-
- @Test
- fun iconTextSizePx() {
- assertThat(dp.iconTextSizePx).isEqualTo(28)
- }
-
- @Test
- fun iconDrawablePaddingPx() {
- assertThat(dp.iconDrawablePaddingPx).isEqualTo(14)
- }
-
- @Test
- fun folderCellWidthPx() {
- assertThat(dp.folderCellWidthPx).isEqualTo(294)
- }
-
- @Test
- fun folderCellHeightPx() {
- assertThat(dp.folderCellHeightPx).isEqualTo(382)
- }
-
- @Test
- fun folderChildIconSizePx() {
- assertThat(dp.folderChildIconSizePx).isEqualTo(112)
- }
-
- @Test
- fun folderChildTextSizePx() {
- assertThat(dp.folderChildTextSizePx).isEqualTo(28)
- }
-
- @Test
- fun folderChildDrawablePaddingPx() {
- assertThat(dp.folderChildDrawablePaddingPx).isEqualTo(77)
- }
-
- @Test
- fun folderCellLayoutBorderSpaceOriginalPx() {
- assertThat(dp.folderCellLayoutBorderSpaceOriginalPx).isEqualTo(0)
- }
-
- @Test
- fun folderCellLayoutBorderSpacePxX() {
- assertThat(dp.folderCellLayoutBorderSpacePx.x).isEqualTo(0)
- }
-
- @Test
- fun folderCellLayoutBorderSpacePxY() {
- assertThat(dp.folderCellLayoutBorderSpacePx.y).isEqualTo(0)
- }
-
- @Test
- fun bottomSheetTopPadding() {
- assertThat(dp.bottomSheetTopPadding).isEqualTo(600)
- }
-
- @Test
- fun allAppsShiftRange() {
- assertThat(dp.allAppsShiftRange).isEqualTo(1960)
- }
-
- @Test
- fun allAppsTopPadding() {
- assertThat(dp.allAppsTopPadding).isEqualTo(600)
- }
-
- @Test
- fun allAppsIconSizePx() {
- assertThat(dp.allAppsIconSizePx).isEqualTo(257)
- }
-
- @Test
- fun allAppsIconTextSizePx() {
- assertThat(dp.allAppsIconTextSizePx).isEqualTo(64)
- }
-
- @Test
- fun allAppsIconDrawablePaddingPx() {
- assertThat(dp.allAppsIconDrawablePaddingPx).isEqualTo(14)
- }
-
- @Test
- fun allAppsCellHeightPx() {
- assertThat(dp.allAppsCellHeightPx).isEqualTo(456)
- }
-
- @Test
- fun allAppsCellWidthPx() {
- assertThat(dp.allAppsCellWidthPx).isEqualTo(294)
- }
-
- @Test
- fun allAppsBorderSpacePxX() {
- assertThat(dp.allAppsBorderSpacePx.x).isEqualTo(74)
- }
-
- @Test
- fun allAppsBorderSpacePxY() {
- assertThat(dp.allAppsBorderSpacePx.y).isEqualTo(74)
- }
-
- @Test
- fun numShownAllAppsColumns() {
- assertThat(dp.numShownAllAppsColumns).isEqualTo(0)
- }
-
- @Test
- fun allAppsLeftRightPadding() {
- assertThat(dp.allAppsLeftRightPadding).isEqualTo(56)
- }
-
- @Test
- fun allAppsLeftRightMargin() {
- assertThat(dp.allAppsLeftRightMargin).isEqualTo(781)
- }
-
- @Test
- fun hotseatBarSizePx() {
- assertThat(dp.hotseatBarSizePx).isEqualTo(386)
- }
-
- @Test
- fun hotseatCellHeightPx() {
- assertThat(dp.hotseatCellHeightPx).isEqualTo(126)
- }
-
- @Test
- fun hotseatBarBottomSpacePx() {
- assertThat(dp.hotseatBarBottomSpacePx).isEqualTo(116)
- }
-
- @Test
- fun hotseatBarSidePaddingStartPx() {
- assertThat(dp.hotseatBarSidePaddingStartPx).isEqualTo(0)
- }
-
- @Test
- fun hotseatBarSidePaddingEndPx() {
- assertThat(dp.hotseatBarSidePaddingEndPx).isEqualTo(0)
- }
-
- @Test
- fun hotseatQsbSpace() {
- assertThat(dp.hotseatQsbSpace).isEqualTo(56)
- }
-
- @Test
- fun hotseatQsbHeight() {
- assertThat(dp.hotseatQsbHeight).isEqualTo(126)
- }
-
- @Test
- fun springLoadedHotseatBarTopMarginPx() {
- assertThat(dp.springLoadedHotseatBarTopMarginPx).isEqualTo(216)
- }
-
- @Test
- fun numShownHotseatIcons() {
- assertThat(dp.numShownHotseatIcons).isEqualTo(4)
- }
-
- @Test
- fun hotseatBorderSpace() {
- assertThat(dp.hotseatBorderSpace).isEqualTo(32)
- }
-
- @Test
- fun isQsbInline() {
- assertThat(dp.isQsbInline).isEqualTo(false)
- }
-
- @Test
- fun qsbWidth() {
- assertThat(dp.qsbWidth).isEqualTo(1216)
- }
-
- @Test
- fun isTaskbarPresent() {
- assertThat(dp.isTaskbarPresent).isEqualTo(true)
- }
-
- @Test
- fun isTaskbarPresentInApps() {
- assertThat(dp.isTaskbarPresentInApps).isEqualTo(false)
- }
-
- @Test
- fun taskbarSize() {
- assertThat(dp.taskbarSize).isEqualTo(120)
- }
-
- @Test
- fun desiredWorkspaceHorizontalMarginPx() {
- assertThat(dp.desiredWorkspaceHorizontalMarginPx).isEqualTo(101)
- }
-
- @Test
- fun workspacePaddingLeft() {
- assertThat(dp.workspacePadding.left).isEqualTo(29)
- }
-
- @Test
- fun workspacePaddingTop() {
- assertThat(dp.workspacePadding.top).isEqualTo(0)
- }
-
- @Test
- fun workspacePaddingRight() {
- assertThat(dp.workspacePadding.right).isEqualTo(29)
- }
-
- @Test
- fun workspacePaddingBottom() {
- assertThat(dp.workspacePadding.bottom).isEqualTo(238)
- }
-
- @Test
- fun iconScale() {
- assertThat(dp.iconScale).isEqualTo(1)
- }
-
- @Test
- fun cellScaleToFit() {
- assertThat(dp.cellScaleToFit).isEqualTo(2.2988505f)
- }
-
- @Test
- fun workspaceTopPadding() {
- assertThat(dp.workspaceTopPadding).isEqualTo(0)
- }
-
- @Test
- fun workspaceBottomPadding() {
- assertThat(dp.workspaceBottomPadding).isEqualTo(0)
- }
-
- @Test
- fun overviewTaskMarginPx() {
- assertThat(dp.overviewTaskMarginPx).isEqualTo(32)
- }
-
- @Test
- fun overviewTaskMarginGridPx() {
- assertThat(dp.overviewTaskMarginGridPx).isEqualTo(32)
- }
-
- @Test
- fun overviewTaskIconSizePx() {
- assertThat(dp.overviewTaskIconSizePx).isEqualTo(96)
- }
-
- @Test
- fun overviewTaskIconDrawableSizePx() {
- assertThat(dp.overviewTaskIconDrawableSizePx).isEqualTo(88)
- }
-
- @Test
- fun overviewTaskIconDrawableSizeGridPx() {
- assertThat(dp.overviewTaskIconDrawableSizeGridPx).isEqualTo(88)
- }
-
- @Test
- fun overviewTaskThumbnailTopMarginPx() {
- assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(160)
- }
-
- @Test
- fun overviewActionsTopMarginPx() {
- assertThat(dp.overviewActionsTopMarginPx).isEqualTo(48)
- }
-
- @Test
- fun overviewActionsHeight() {
- assertThat(dp.overviewActionsHeight).isEqualTo(96)
- }
-
- @Test
- fun overviewActionsButtonSpacing() {
- assertThat(dp.overviewActionsButtonSpacing).isEqualTo(72)
- }
-
- @Test
- fun overviewPageSpacing() {
- assertThat(dp.overviewPageSpacing).isEqualTo(88)
- }
-
- @Test
- fun overviewRowSpacing() {
- assertThat(dp.overviewRowSpacing).isEqualTo(40)
- }
-
- @Test
- fun overviewGridSideMargin() {
- assertThat(dp.overviewGridSideMargin).isEqualTo(128)
- }
-
- @Test
- fun dropTargetBarTopMarginPx() {
- assertThat(dp.dropTargetBarTopMarginPx).isEqualTo(220)
- }
-
- @Test
- fun dropTargetBarSizePx() {
- assertThat(dp.dropTargetBarSizePx).isEqualTo(144)
- }
-
- @Test
- fun dropTargetBarBottomMarginPx() {
- assertThat(dp.dropTargetBarBottomMarginPx).isEqualTo(96)
- }
-
- @Test
- fun workspaceSpringLoadedMinNextPageVisiblePx() {
- assertThat(dp.workspaceSpringLoadedMinNextPageVisiblePx).isEqualTo(48)
- }
-
- @Test
- fun getWorkspaceSpringLoadScale() {
- assertThat(dp.workspaceSpringLoadScale).isEqualTo(0.6741674f)
- }
-
- @Test
- fun getCellLayoutHeight() {
- assertThat(dp.cellLayoutHeight).isEqualTo(2222)
- }
-
- @Test
- fun getCellLayoutWidth() {
- assertThat(dp.cellLayoutWidth).isEqualTo(1542)
- }
-
- @Test
- fun getPanelCount() {
- assertThat(dp.panelCount).isEqualTo(1)
- }
-
- @Test
- fun isVerticalBarLayout() {
- assertThat(dp.isVerticalBarLayout).isEqualTo(false)
- }
-
- @Test
- fun getCellLayoutSpringLoadShrunkTop() {
- assertThat(dp.cellLayoutSpringLoadShrunkTop).isEqualTo(460)
- }
-
- @Test
- fun getCellLayoutSpringLoadShrunkBottom() {
- assertThat(dp.cellLayoutSpringLoadShrunkBottom).isEqualTo(1958)
- }
-
- @Test
- fun getQsbOffsetY() {
- assertThat(dp.qsbOffsetY).isEqualTo(272)
- }
-
- @Test
- fun getTaskbarOffsetY() {
- assertThat(dp.taskbarOffsetY).isEqualTo(112)
- }
-
- @Test
- fun getHotseatLayoutPaddingLeft() {
- assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(528)
- }
-
- @Test
- fun getHotseatLayoutPaddingTop() {
- assertThat(dp.getHotseatLayoutPadding(context).top).isEqualTo(151)
- }
-
- @Test
- fun getHotseatLayoutPaddingRight() {
- assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(528)
- }
-
- @Test
- fun getHotseatLayoutPaddingBottom() {
- assertThat(dp.getHotseatLayoutPadding(context).bottom).isEqualTo(109)
- }
-
- @Test
- fun hotseatBarEndOffset() {
- assertThat(dp.hotseatBarEndOffset).isEqualTo(502)
- }
-}
\ No newline at end of file
diff --git a/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletPortraitTest.kt b/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletPortraitTest.kt
deleted file mode 100644
index 4be3e45..0000000
--- a/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletPortraitTest.kt
+++ /dev/null
@@ -1,486 +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.DeviceProfile
-import com.android.launcher3.DeviceProfileBaseTest
-import com.google.common.truth.Truth.assertThat
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-
-/**
- * Tests for DeviceProfile for tablet in portrait.
- */
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class DeviceProfileTabletPortraitTest : DeviceProfileBaseTest() {
-
- lateinit var dp: DeviceProfile
-
- @Before
- fun before() {
- initializeVarsForTablet()
- dp = newDP()
- }
-
- @Test
- fun isScalableGrid() {
- assertThat(dp.isScalableGrid).isTrue()
- }
-
- @Test
- fun cellWidthPx() {
- assertThat(dp.cellWidthPx).isEqualTo(294)
- }
-
- @Test
- fun cellHeightPx() {
- assertThat(dp.cellHeightPx).isEqualTo(382)
- }
-
- @Test
- fun getCellSizeX() {
- assertThat(dp.cellSize.x).isEqualTo(294)
- }
-
- @Test
- fun getCellSizeY() {
- assertThat(dp.cellSize.y).isEqualTo(482)
- }
-
- @Test
- fun cellLayoutBorderSpacePxX() {
- assertThat(dp.cellLayoutBorderSpacePx.x).isEqualTo(74)
- }
-
- @Test
- fun cellLayoutBorderSpacePxY() {
- assertThat(dp.cellLayoutBorderSpacePx.y).isEqualTo(74)
- }
-
- @Test
- fun cellLayoutPaddingPxLeft() {
- assertThat(dp.cellLayoutPaddingPx.left).isEqualTo(72)
- }
-
- @Test
- fun cellLayoutPaddingPxTop() {
- assertThat(dp.cellLayoutPaddingPx.top).isEqualTo(0)
- }
-
- @Test
- fun cellLayoutPaddingPxRight() {
- assertThat(dp.cellLayoutPaddingPx.right).isEqualTo(72)
- }
-
- @Test
- fun cellLayoutPaddingPxBottom() {
- assertThat(dp.cellLayoutPaddingPx.bottom).isEqualTo(72)
- }
-
- @Test
- fun iconSizePx() {
- assertThat(dp.iconSizePx).isEqualTo(112)
- }
-
- @Test
- fun iconTextSizePx() {
- assertThat(dp.iconTextSizePx).isEqualTo(28)
- }
-
- @Test
- fun iconDrawablePaddingPx() {
- assertThat(dp.iconDrawablePaddingPx).isEqualTo(14)
- }
-
- @Test
- fun folderCellWidthPx() {
- assertThat(dp.folderCellWidthPx).isEqualTo(294)
- }
-
- @Test
- fun folderCellHeightPx() {
- assertThat(dp.folderCellHeightPx).isEqualTo(382)
- }
-
- @Test
- fun folderChildIconSizePx() {
- assertThat(dp.folderChildIconSizePx).isEqualTo(112)
- }
-
- @Test
- fun folderChildTextSizePx() {
- assertThat(dp.folderChildTextSizePx).isEqualTo(28)
- }
-
- @Test
- fun folderChildDrawablePaddingPx() {
- assertThat(dp.folderChildDrawablePaddingPx).isEqualTo(77)
- }
-
- @Test
- fun folderCellLayoutBorderSpaceOriginalPx() {
- assertThat(dp.folderCellLayoutBorderSpaceOriginalPx).isEqualTo(0)
- }
-
- @Test
- fun folderCellLayoutBorderSpacePxX() {
- assertThat(dp.folderCellLayoutBorderSpacePx.x).isEqualTo(0)
- }
-
- @Test
- fun folderCellLayoutBorderSpacePxY() {
- assertThat(dp.folderCellLayoutBorderSpacePx.y).isEqualTo(0)
- }
-
- @Test
- fun bottomSheetTopPadding() {
- assertThat(dp.bottomSheetTopPadding).isEqualTo(600)
- }
-
- @Test
- fun allAppsShiftRange() {
- assertThat(dp.allAppsShiftRange).isEqualTo(1960)
- }
-
- @Test
- fun allAppsTopPadding() {
- assertThat(dp.allAppsTopPadding).isEqualTo(600)
- }
-
- @Test
- fun allAppsIconSizePx() {
- assertThat(dp.allAppsIconSizePx).isEqualTo(257)
- }
-
- @Test
- fun allAppsIconTextSizePx() {
- assertThat(dp.allAppsIconTextSizePx).isEqualTo(64)
- }
-
- @Test
- fun allAppsIconDrawablePaddingPx() {
- assertThat(dp.allAppsIconDrawablePaddingPx).isEqualTo(14)
- }
-
- @Test
- fun allAppsCellHeightPx() {
- assertThat(dp.allAppsCellHeightPx).isEqualTo(456)
- }
-
- @Test
- fun allAppsCellWidthPx() {
- assertThat(dp.allAppsCellWidthPx).isEqualTo(294)
- }
-
- @Test
- fun allAppsBorderSpacePxX() {
- assertThat(dp.allAppsBorderSpacePx.x).isEqualTo(74)
- }
-
- @Test
- fun allAppsBorderSpacePxY() {
- assertThat(dp.allAppsBorderSpacePx.y).isEqualTo(74)
- }
-
- @Test
- fun numShownAllAppsColumns() {
- assertThat(dp.numShownAllAppsColumns).isEqualTo(0)
- }
-
- @Test
- fun allAppsLeftRightPadding() {
- assertThat(dp.allAppsLeftRightPadding).isEqualTo(56)
- }
-
- @Test
- fun allAppsLeftRightMargin() {
- assertThat(dp.allAppsLeftRightMargin).isEqualTo(781)
- }
-
- @Test
- fun hotseatBarSizePx() {
- assertThat(dp.hotseatBarSizePx).isEqualTo(386)
- }
-
- @Test
- fun hotseatCellHeightPx() {
- assertThat(dp.hotseatCellHeightPx).isEqualTo(126)
- }
-
- @Test
- fun hotseatBarBottomSpacePx() {
- assertThat(dp.hotseatBarBottomSpacePx).isEqualTo(116)
- }
-
- @Test
- fun hotseatBarSidePaddingStartPx() {
- assertThat(dp.hotseatBarSidePaddingStartPx).isEqualTo(0)
- }
-
- @Test
- fun hotseatBarSidePaddingEndPx() {
- assertThat(dp.hotseatBarSidePaddingEndPx).isEqualTo(0)
- }
-
- @Test
- fun hotseatQsbSpace() {
- assertThat(dp.hotseatQsbSpace).isEqualTo(56)
- }
-
- @Test
- fun hotseatQsbHeight() {
- assertThat(dp.hotseatQsbHeight).isEqualTo(126)
- }
-
- @Test
- fun springLoadedHotseatBarTopMarginPx() {
- assertThat(dp.springLoadedHotseatBarTopMarginPx).isEqualTo(216)
- }
-
- @Test
- fun numShownHotseatIcons() {
- assertThat(dp.numShownHotseatIcons).isEqualTo(4)
- }
-
- @Test
- fun hotseatBorderSpace() {
- assertThat(dp.hotseatBorderSpace).isEqualTo(256)
- }
-
- @Test
- fun isQsbInline() {
- assertThat(dp.isQsbInline).isEqualTo(false)
- }
-
- @Test
- fun qsbWidth() {
- assertThat(dp.qsbWidth).isEqualTo(1216)
- }
-
- @Test
- fun isTaskbarPresent() {
- assertThat(dp.isTaskbarPresent).isEqualTo(true)
- }
-
- @Test
- fun isTaskbarPresentInApps() {
- assertThat(dp.isTaskbarPresentInApps).isEqualTo(false)
- }
-
- @Test
- fun taskbarSize() {
- assertThat(dp.taskbarSize).isEqualTo(120)
- }
-
- @Test
- fun desiredWorkspaceHorizontalMarginPx() {
- assertThat(dp.desiredWorkspaceHorizontalMarginPx).isEqualTo(101)
- }
-
- @Test
- fun workspacePaddingLeft() {
- assertThat(dp.workspacePadding.left).isEqualTo(29)
- }
-
- @Test
- fun workspacePaddingTop() {
- assertThat(dp.workspacePadding.top).isEqualTo(0)
- }
-
- @Test
- fun workspacePaddingRight() {
- assertThat(dp.workspacePadding.right).isEqualTo(29)
- }
-
- @Test
- fun workspacePaddingBottom() {
- assertThat(dp.workspacePadding.bottom).isEqualTo(238)
- }
-
- @Test
- fun iconScale() {
- assertThat(dp.iconScale).isEqualTo(1)
- }
-
- @Test
- fun cellScaleToFit() {
- assertThat(dp.cellScaleToFit).isEqualTo(2.2988505f)
- }
-
- @Test
- fun workspaceTopPadding() {
- assertThat(dp.workspaceTopPadding).isEqualTo(0)
- }
-
- @Test
- fun workspaceBottomPadding() {
- assertThat(dp.workspaceBottomPadding).isEqualTo(0)
- }
-
- @Test
- fun overviewTaskMarginPx() {
- assertThat(dp.overviewTaskMarginPx).isEqualTo(32)
- }
-
- @Test
- fun overviewTaskMarginGridPx() {
- assertThat(dp.overviewTaskMarginGridPx).isEqualTo(32)
- }
-
- @Test
- fun overviewTaskIconSizePx() {
- assertThat(dp.overviewTaskIconSizePx).isEqualTo(96)
- }
-
- @Test
- fun overviewTaskIconDrawableSizePx() {
- assertThat(dp.overviewTaskIconDrawableSizePx).isEqualTo(88)
- }
-
- @Test
- fun overviewTaskIconDrawableSizeGridPx() {
- assertThat(dp.overviewTaskIconDrawableSizeGridPx).isEqualTo(88)
- }
-
- @Test
- fun overviewTaskThumbnailTopMarginPx() {
- assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(160)
- }
-
- @Test
- fun overviewActionsTopMarginPx() {
- assertThat(dp.overviewActionsTopMarginPx).isEqualTo(48)
- }
-
- @Test
- fun overviewActionsHeight() {
- assertThat(dp.overviewActionsHeight).isEqualTo(96)
- }
-
- @Test
- fun overviewActionsButtonSpacing() {
- assertThat(dp.overviewActionsButtonSpacing).isEqualTo(72)
- }
-
- @Test
- fun overviewPageSpacing() {
- assertThat(dp.overviewPageSpacing).isEqualTo(88)
- }
-
- @Test
- fun overviewRowSpacing() {
- assertThat(dp.overviewRowSpacing).isEqualTo(40)
- }
-
- @Test
- fun overviewGridSideMargin() {
- assertThat(dp.overviewGridSideMargin).isEqualTo(128)
- }
-
- @Test
- fun dropTargetBarTopMarginPx() {
- assertThat(dp.dropTargetBarTopMarginPx).isEqualTo(220)
- }
-
- @Test
- fun dropTargetBarSizePx() {
- assertThat(dp.dropTargetBarSizePx).isEqualTo(144)
- }
-
- @Test
- fun dropTargetBarBottomMarginPx() {
- assertThat(dp.dropTargetBarBottomMarginPx).isEqualTo(96)
- }
-
- @Test
- fun workspaceSpringLoadedMinNextPageVisiblePx() {
- assertThat(dp.workspaceSpringLoadedMinNextPageVisiblePx).isEqualTo(48)
- }
-
- @Test
- fun getWorkspaceSpringLoadScale() {
- assertThat(dp.workspaceSpringLoadScale).isEqualTo(0.6741674f)
- }
-
- @Test
- fun getCellLayoutHeight() {
- assertThat(dp.cellLayoutHeight).isEqualTo(2222)
- }
-
- @Test
- fun getCellLayoutWidth() {
- assertThat(dp.cellLayoutWidth).isEqualTo(1542)
- }
-
- @Test
- fun getPanelCount() {
- assertThat(dp.panelCount).isEqualTo(1)
- }
-
- @Test
- fun isVerticalBarLayout() {
- assertThat(dp.isVerticalBarLayout).isEqualTo(false)
- }
-
- @Test
- fun getCellLayoutSpringLoadShrunkTop() {
- assertThat(dp.cellLayoutSpringLoadShrunkTop).isEqualTo(460)
- }
-
- @Test
- fun getCellLayoutSpringLoadShrunkBottom() {
- assertThat(dp.cellLayoutSpringLoadShrunkBottom).isEqualTo(1958)
- }
-
- @Test
- fun getQsbOffsetY() {
- assertThat(dp.qsbOffsetY).isEqualTo(272)
- }
-
- @Test
- fun getTaskbarOffsetY() {
- assertThat(dp.taskbarOffsetY).isEqualTo(112)
- }
-
- @Test
- fun getHotseatLayoutPaddingLeft() {
- assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(192)
- }
-
- @Test
- fun getHotseatLayoutPaddingTop() {
- assertThat(dp.getHotseatLayoutPadding(context).top).isEqualTo(151)
- }
-
- @Test
- fun getHotseatLayoutPaddingRight() {
- assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(192)
- }
-
- @Test
- fun getHotseatLayoutPaddingBottom() {
- assertThat(dp.getHotseatLayoutPadding(context).bottom).isEqualTo(109)
- }
-
- @Test
- fun hotseatBarEndOffset() {
- assertThat(dp.hotseatBarEndOffset).isEqualTo(0)
- }
-}
\ No newline at end of file
diff --git a/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelLandscape3ButtonTest.kt b/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelLandscape3ButtonTest.kt
deleted file mode 100644
index 6728540..0000000
--- a/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelLandscape3ButtonTest.kt
+++ /dev/null
@@ -1,486 +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.DeviceProfile
-import com.android.launcher3.DeviceProfileBaseTest
-import com.google.common.truth.Truth.assertThat
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-
-/**
- * Tests for DeviceProfile for two panel in landscape with 3-Button navigation.
- */
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class DeviceProfileTwoPanelLandscape3ButtonTest : DeviceProfileBaseTest() {
-
- lateinit var dp: DeviceProfile
-
- @Before
- fun before() {
- initializeVarsForTablet(isLandscape = true, isTwoPanel = true, isGestureMode = false)
- dp = newDP()
- }
-
- @Test
- fun isScalableGrid() {
- assertThat(dp.isScalableGrid).isTrue()
- }
-
- @Test
- fun cellWidthPx() {
- assertThat(dp.cellWidthPx).isEqualTo(200)
- }
-
- @Test
- fun cellHeightPx() {
- assertThat(dp.cellHeightPx).isEqualTo(260)
- }
-
- @Test
- fun getCellSizeX() {
- assertThat(dp.cellSize.x).isEqualTo(259)
- }
-
- @Test
- fun getCellSizeY() {
- assertThat(dp.cellSize.y).isEqualTo(260)
- }
-
- @Test
- fun cellLayoutBorderSpacePxX() {
- assertThat(dp.cellLayoutBorderSpacePx.x).isEqualTo(50)
- }
-
- @Test
- fun cellLayoutBorderSpacePxY() {
- assertThat(dp.cellLayoutBorderSpacePx.y).isEqualTo(50)
- }
-
- @Test
- fun cellLayoutPaddingPxLeft() {
- assertThat(dp.cellLayoutPaddingPx.left).isEqualTo(25)
- }
-
- @Test
- fun cellLayoutPaddingPxTop() {
- assertThat(dp.cellLayoutPaddingPx.top).isEqualTo(0)
- }
-
- @Test
- fun cellLayoutPaddingPxRight() {
- assertThat(dp.cellLayoutPaddingPx.right).isEqualTo(25)
- }
-
- @Test
- fun cellLayoutPaddingPxBottom() {
- assertThat(dp.cellLayoutPaddingPx.bottom).isEqualTo(25)
- }
-
- @Test
- fun iconSizePx() {
- assertThat(dp.iconSizePx).isEqualTo(112)
- }
-
- @Test
- fun iconTextSizePx() {
- assertThat(dp.iconTextSizePx).isEqualTo(28)
- }
-
- @Test
- fun iconDrawablePaddingPx() {
- assertThat(dp.iconDrawablePaddingPx).isEqualTo(14)
- }
-
- @Test
- fun folderCellWidthPx() {
- assertThat(dp.folderCellWidthPx).isEqualTo(200)
- }
-
- @Test
- fun folderCellHeightPx() {
- assertThat(dp.folderCellHeightPx).isEqualTo(260)
- }
-
- @Test
- fun folderChildIconSizePx() {
- assertThat(dp.folderChildIconSizePx).isEqualTo(112)
- }
-
- @Test
- fun folderChildTextSizePx() {
- assertThat(dp.folderChildTextSizePx).isEqualTo(28)
- }
-
- @Test
- fun folderChildDrawablePaddingPx() {
- assertThat(dp.folderChildDrawablePaddingPx).isEqualTo(36)
- }
-
- @Test
- fun folderCellLayoutBorderSpaceOriginalPx() {
- assertThat(dp.folderCellLayoutBorderSpaceOriginalPx).isEqualTo(0)
- }
-
- @Test
- fun folderCellLayoutBorderSpacePxX() {
- assertThat(dp.folderCellLayoutBorderSpacePx.x).isEqualTo(0)
- }
-
- @Test
- fun folderCellLayoutBorderSpacePxY() {
- assertThat(dp.folderCellLayoutBorderSpacePx.y).isEqualTo(0)
- }
-
- @Test
- fun bottomSheetTopPadding() {
- assertThat(dp.bottomSheetTopPadding).isEqualTo(0)
- }
-
- @Test
- fun allAppsShiftRange() {
- assertThat(dp.allAppsShiftRange).isEqualTo(1600)
- }
-
- @Test
- fun allAppsTopPadding() {
- assertThat(dp.allAppsTopPadding).isEqualTo(0)
- }
-
- @Test
- fun allAppsIconSizePx() {
- assertThat(dp.allAppsIconSizePx).isEqualTo(175)
- }
-
- @Test
- fun allAppsIconTextSizePx() {
- assertThat(dp.allAppsIconTextSizePx).isEqualTo(44)
- }
-
- @Test
- fun allAppsIconDrawablePaddingPx() {
- assertThat(dp.allAppsIconDrawablePaddingPx).isEqualTo(14)
- }
-
- @Test
- fun allAppsCellHeightPx() {
- assertThat(dp.allAppsCellHeightPx).isEqualTo(310)
- }
-
- @Test
- fun allAppsCellWidthPx() {
- assertThat(dp.allAppsCellWidthPx).isEqualTo(200)
- }
-
- @Test
- fun allAppsBorderSpacePxX() {
- assertThat(dp.allAppsBorderSpacePx.x).isEqualTo(50)
- }
-
- @Test
- fun allAppsBorderSpacePxY() {
- assertThat(dp.allAppsBorderSpacePx.y).isEqualTo(50)
- }
-
- @Test
- fun numShownAllAppsColumns() {
- assertThat(dp.numShownAllAppsColumns).isEqualTo(0)
- }
-
- @Test
- fun allAppsLeftRightPadding() {
- assertThat(dp.allAppsLeftRightPadding).isEqualTo(64)
- }
-
- @Test
- fun allAppsLeftRightMargin() {
- assertThat(dp.allAppsLeftRightMargin).isEqualTo(1241)
- }
-
- @Test
- fun hotseatBarSizePx() {
- assertThat(dp.hotseatBarSizePx).isEqualTo(386)
- }
-
- @Test
- fun hotseatCellHeightPx() {
- assertThat(dp.hotseatCellHeightPx).isEqualTo(126)
- }
-
- @Test
- fun hotseatBarBottomSpacePx() {
- assertThat(dp.hotseatBarBottomSpacePx).isEqualTo(116)
- }
-
- @Test
- fun hotseatBarSidePaddingStartPx() {
- assertThat(dp.hotseatBarSidePaddingStartPx).isEqualTo(0)
- }
-
- @Test
- fun hotseatBarSidePaddingEndPx() {
- assertThat(dp.hotseatBarSidePaddingEndPx).isEqualTo(0)
- }
-
- @Test
- fun hotseatQsbSpace() {
- assertThat(dp.hotseatQsbSpace).isEqualTo(56)
- }
-
- @Test
- fun hotseatQsbHeight() {
- assertThat(dp.hotseatQsbHeight).isEqualTo(126)
- }
-
- @Test
- fun springLoadedHotseatBarTopMarginPx() {
- assertThat(dp.springLoadedHotseatBarTopMarginPx).isEqualTo(128)
- }
-
- @Test
- fun numShownHotseatIcons() {
- assertThat(dp.numShownHotseatIcons).isEqualTo(6)
- }
-
- @Test
- fun hotseatBorderSpace() {
- assertThat(dp.hotseatBorderSpace).isEqualTo(32)
- }
-
- @Test
- fun isQsbInline() {
- assertThat(dp.isQsbInline).isEqualTo(false)
- }
-
- @Test
- fun qsbWidth() {
- assertThat(dp.qsbWidth).isEqualTo(1039)
- }
-
- @Test
- fun isTaskbarPresent() {
- assertThat(dp.isTaskbarPresent).isEqualTo(true)
- }
-
- @Test
- fun isTaskbarPresentInApps() {
- assertThat(dp.isTaskbarPresentInApps).isEqualTo(false)
- }
-
- @Test
- fun taskbarSize() {
- assertThat(dp.taskbarSize).isEqualTo(120)
- }
-
- @Test
- fun desiredWorkspaceHorizontalMarginPx() {
- assertThat(dp.desiredWorkspaceHorizontalMarginPx).isEqualTo(68)
- }
-
- @Test
- fun workspacePaddingLeft() {
- assertThat(dp.workspacePadding.left).isEqualTo(43)
- }
-
- @Test
- fun workspacePaddingTop() {
- assertThat(dp.workspacePadding.top).isEqualTo(0)
- }
-
- @Test
- fun workspacePaddingRight() {
- assertThat(dp.workspacePadding.right).isEqualTo(43)
- }
-
- @Test
- fun workspacePaddingBottom() {
- assertThat(dp.workspacePadding.bottom).isEqualTo(285)
- }
-
- @Test
- fun iconScale() {
- assertThat(dp.iconScale).isEqualTo(1)
- }
-
- @Test
- fun cellScaleToFit() {
- assertThat(dp.cellScaleToFit).isEqualTo(1.5657895f)
- }
-
- @Test
- fun workspaceTopPadding() {
- assertThat(dp.workspaceTopPadding).isEqualTo(0)
- }
-
- @Test
- fun workspaceBottomPadding() {
- assertThat(dp.workspaceBottomPadding).isEqualTo(0)
- }
-
- @Test
- fun overviewTaskMarginPx() {
- assertThat(dp.overviewTaskMarginPx).isEqualTo(32)
- }
-
- @Test
- fun overviewTaskMarginGridPx() {
- assertThat(dp.overviewTaskMarginGridPx).isEqualTo(32)
- }
-
- @Test
- fun overviewTaskIconSizePx() {
- assertThat(dp.overviewTaskIconSizePx).isEqualTo(96)
- }
-
- @Test
- fun overviewTaskIconDrawableSizePx() {
- assertThat(dp.overviewTaskIconDrawableSizePx).isEqualTo(88)
- }
-
- @Test
- fun overviewTaskIconDrawableSizeGridPx() {
- assertThat(dp.overviewTaskIconDrawableSizeGridPx).isEqualTo(88)
- }
-
- @Test
- fun overviewTaskThumbnailTopMarginPx() {
- assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(160)
- }
-
- @Test
- fun overviewActionsTopMarginPx() {
- assertThat(dp.overviewActionsTopMarginPx).isEqualTo(40)
- }
-
- @Test
- fun overviewActionsHeight() {
- assertThat(dp.overviewActionsHeight).isEqualTo(96)
- }
-
- @Test
- fun overviewActionsButtonSpacing() {
- assertThat(dp.overviewActionsButtonSpacing).isEqualTo(72)
- }
-
- @Test
- fun overviewPageSpacing() {
- assertThat(dp.overviewPageSpacing).isEqualTo(88)
- }
-
- @Test
- fun overviewRowSpacing() {
- assertThat(dp.overviewRowSpacing).isEqualTo(40)
- }
-
- @Test
- fun overviewGridSideMargin() {
- assertThat(dp.overviewGridSideMargin).isEqualTo(128)
- }
-
- @Test
- fun dropTargetBarTopMarginPx() {
- assertThat(dp.dropTargetBarTopMarginPx).isEqualTo(0)
- }
-
- @Test
- fun dropTargetBarSizePx() {
- assertThat(dp.dropTargetBarSizePx).isEqualTo(144)
- }
-
- @Test
- fun dropTargetBarBottomMarginPx() {
- assertThat(dp.dropTargetBarBottomMarginPx).isEqualTo(64)
- }
-
- @Test
- fun workspaceSpringLoadedMinNextPageVisiblePx() {
- assertThat(dp.workspaceSpringLoadedMinNextPageVisiblePx).isEqualTo(48)
- }
-
- @Test
- fun getWorkspaceSpringLoadScale() {
- assertThat(dp.workspaceSpringLoadScale).isEqualTo(0.7226337f)
- }
-
- @Test
- fun getCellLayoutHeight() {
- assertThat(dp.cellLayoutHeight).isEqualTo(1215)
- }
-
- @Test
- fun getCellLayoutWidth() {
- assertThat(dp.cellLayoutWidth).isEqualTo(1237)
- }
-
- @Test
- fun getPanelCount() {
- assertThat(dp.panelCount).isEqualTo(2)
- }
-
- @Test
- fun isVerticalBarLayout() {
- assertThat(dp.isVerticalBarLayout).isEqualTo(false)
- }
-
- @Test
- fun getCellLayoutSpringLoadShrunkTop() {
- assertThat(dp.cellLayoutSpringLoadShrunkTop).isEqualTo(208)
- }
-
- @Test
- fun getCellLayoutSpringLoadShrunkBottom() {
- assertThat(dp.cellLayoutSpringLoadShrunkBottom).isEqualTo(1086)
- }
-
- @Test
- fun getQsbOffsetY() {
- assertThat(dp.qsbOffsetY).isEqualTo(272)
- }
-
- @Test
- fun getTaskbarOffsetY() {
- assertThat(dp.taskbarOffsetY).isEqualTo(112)
- }
-
- @Test
- fun getHotseatLayoutPaddingLeft() {
- assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(864)
- }
-
- @Test
- fun getHotseatLayoutPaddingTop() {
- assertThat(dp.getHotseatLayoutPadding(context).top).isEqualTo(151)
- }
-
- @Test
- fun getHotseatLayoutPaddingRight() {
- assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(864)
- }
-
- @Test
- fun getHotseatLayoutPaddingBottom() {
- assertThat(dp.getHotseatLayoutPadding(context).bottom).isEqualTo(109)
- }
-
- @Test
- fun hotseatBarEndOffset() {
- assertThat(dp.hotseatBarEndOffset).isEqualTo(566)
- }
-}
\ No newline at end of file
diff --git a/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelLandscapeTest.kt b/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelLandscapeTest.kt
deleted file mode 100644
index ba3ef55..0000000
--- a/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelLandscapeTest.kt
+++ /dev/null
@@ -1,486 +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.DeviceProfile
-import com.android.launcher3.DeviceProfileBaseTest
-import com.google.common.truth.Truth.assertThat
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-
-/**
- * Tests for DeviceProfile for two panel in landscape.
- */
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class DeviceProfileTwoPanelLandscapeTest : DeviceProfileBaseTest() {
-
- lateinit var dp: DeviceProfile
-
- @Before
- fun before() {
- initializeVarsForTablet(isLandscape = true, isTwoPanel = true)
- dp = newDP()
- }
-
- @Test
- fun isScalableGrid() {
- assertThat(dp.isScalableGrid).isTrue()
- }
-
- @Test
- fun cellWidthPx() {
- assertThat(dp.cellWidthPx).isEqualTo(200)
- }
-
- @Test
- fun cellHeightPx() {
- assertThat(dp.cellHeightPx).isEqualTo(260)
- }
-
- @Test
- fun getCellSizeX() {
- assertThat(dp.cellSize.x).isEqualTo(259)
- }
-
- @Test
- fun getCellSizeY() {
- assertThat(dp.cellSize.y).isEqualTo(260)
- }
-
- @Test
- fun cellLayoutBorderSpacePxX() {
- assertThat(dp.cellLayoutBorderSpacePx.x).isEqualTo(50)
- }
-
- @Test
- fun cellLayoutBorderSpacePxY() {
- assertThat(dp.cellLayoutBorderSpacePx.y).isEqualTo(50)
- }
-
- @Test
- fun cellLayoutPaddingPxLeft() {
- assertThat(dp.cellLayoutPaddingPx.left).isEqualTo(25)
- }
-
- @Test
- fun cellLayoutPaddingPxTop() {
- assertThat(dp.cellLayoutPaddingPx.top).isEqualTo(0)
- }
-
- @Test
- fun cellLayoutPaddingPxRight() {
- assertThat(dp.cellLayoutPaddingPx.right).isEqualTo(25)
- }
-
- @Test
- fun cellLayoutPaddingPxBottom() {
- assertThat(dp.cellLayoutPaddingPx.bottom).isEqualTo(25)
- }
-
- @Test
- fun iconSizePx() {
- assertThat(dp.iconSizePx).isEqualTo(112)
- }
-
- @Test
- fun iconTextSizePx() {
- assertThat(dp.iconTextSizePx).isEqualTo(28)
- }
-
- @Test
- fun iconDrawablePaddingPx() {
- assertThat(dp.iconDrawablePaddingPx).isEqualTo(14)
- }
-
- @Test
- fun folderCellWidthPx() {
- assertThat(dp.folderCellWidthPx).isEqualTo(200)
- }
-
- @Test
- fun folderCellHeightPx() {
- assertThat(dp.folderCellHeightPx).isEqualTo(260)
- }
-
- @Test
- fun folderChildIconSizePx() {
- assertThat(dp.folderChildIconSizePx).isEqualTo(112)
- }
-
- @Test
- fun folderChildTextSizePx() {
- assertThat(dp.folderChildTextSizePx).isEqualTo(28)
- }
-
- @Test
- fun folderChildDrawablePaddingPx() {
- assertThat(dp.folderChildDrawablePaddingPx).isEqualTo(36)
- }
-
- @Test
- fun folderCellLayoutBorderSpaceOriginalPx() {
- assertThat(dp.folderCellLayoutBorderSpaceOriginalPx).isEqualTo(0)
- }
-
- @Test
- fun folderCellLayoutBorderSpacePxX() {
- assertThat(dp.folderCellLayoutBorderSpacePx.x).isEqualTo(0)
- }
-
- @Test
- fun folderCellLayoutBorderSpacePxY() {
- assertThat(dp.folderCellLayoutBorderSpacePx.y).isEqualTo(0)
- }
-
- @Test
- fun bottomSheetTopPadding() {
- assertThat(dp.bottomSheetTopPadding).isEqualTo(0)
- }
-
- @Test
- fun allAppsShiftRange() {
- assertThat(dp.allAppsShiftRange).isEqualTo(1600)
- }
-
- @Test
- fun allAppsTopPadding() {
- assertThat(dp.allAppsTopPadding).isEqualTo(0)
- }
-
- @Test
- fun allAppsIconSizePx() {
- assertThat(dp.allAppsIconSizePx).isEqualTo(175)
- }
-
- @Test
- fun allAppsIconTextSizePx() {
- assertThat(dp.allAppsIconTextSizePx).isEqualTo(44)
- }
-
- @Test
- fun allAppsIconDrawablePaddingPx() {
- assertThat(dp.allAppsIconDrawablePaddingPx).isEqualTo(14)
- }
-
- @Test
- fun allAppsCellHeightPx() {
- assertThat(dp.allAppsCellHeightPx).isEqualTo(310)
- }
-
- @Test
- fun allAppsCellWidthPx() {
- assertThat(dp.allAppsCellWidthPx).isEqualTo(200)
- }
-
- @Test
- fun allAppsBorderSpacePxX() {
- assertThat(dp.allAppsBorderSpacePx.x).isEqualTo(50)
- }
-
- @Test
- fun allAppsBorderSpacePxY() {
- assertThat(dp.allAppsBorderSpacePx.y).isEqualTo(50)
- }
-
- @Test
- fun numShownAllAppsColumns() {
- assertThat(dp.numShownAllAppsColumns).isEqualTo(0)
- }
-
- @Test
- fun allAppsLeftRightPadding() {
- assertThat(dp.allAppsLeftRightPadding).isEqualTo(64)
- }
-
- @Test
- fun allAppsLeftRightMargin() {
- assertThat(dp.allAppsLeftRightMargin).isEqualTo(1241)
- }
-
- @Test
- fun hotseatBarSizePx() {
- assertThat(dp.hotseatBarSizePx).isEqualTo(386)
- }
-
- @Test
- fun hotseatCellHeightPx() {
- assertThat(dp.hotseatCellHeightPx).isEqualTo(126)
- }
-
- @Test
- fun hotseatBarBottomSpacePx() {
- assertThat(dp.hotseatBarBottomSpacePx).isEqualTo(116)
- }
-
- @Test
- fun hotseatBarSidePaddingStartPx() {
- assertThat(dp.hotseatBarSidePaddingStartPx).isEqualTo(0)
- }
-
- @Test
- fun hotseatBarSidePaddingEndPx() {
- assertThat(dp.hotseatBarSidePaddingEndPx).isEqualTo(0)
- }
-
- @Test
- fun hotseatQsbSpace() {
- assertThat(dp.hotseatQsbSpace).isEqualTo(56)
- }
-
- @Test
- fun hotseatQsbHeight() {
- assertThat(dp.hotseatQsbHeight).isEqualTo(126)
- }
-
- @Test
- fun springLoadedHotseatBarTopMarginPx() {
- assertThat(dp.springLoadedHotseatBarTopMarginPx).isEqualTo(128)
- }
-
- @Test
- fun numShownHotseatIcons() {
- assertThat(dp.numShownHotseatIcons).isEqualTo(6)
- }
-
- @Test
- fun hotseatBorderSpace() {
- assertThat(dp.hotseatBorderSpace).isEqualTo(73)
- }
-
- @Test
- fun isQsbInline() {
- assertThat(dp.isQsbInline).isEqualTo(false)
- }
-
- @Test
- fun qsbWidth() {
- assertThat(dp.qsbWidth).isEqualTo(1039)
- }
-
- @Test
- fun isTaskbarPresent() {
- assertThat(dp.isTaskbarPresent).isEqualTo(true)
- }
-
- @Test
- fun isTaskbarPresentInApps() {
- assertThat(dp.isTaskbarPresentInApps).isEqualTo(false)
- }
-
- @Test
- fun taskbarSize() {
- assertThat(dp.taskbarSize).isEqualTo(120)
- }
-
- @Test
- fun desiredWorkspaceHorizontalMarginPx() {
- assertThat(dp.desiredWorkspaceHorizontalMarginPx).isEqualTo(68)
- }
-
- @Test
- fun workspacePaddingLeft() {
- assertThat(dp.workspacePadding.left).isEqualTo(43)
- }
-
- @Test
- fun workspacePaddingTop() {
- assertThat(dp.workspacePadding.top).isEqualTo(0)
- }
-
- @Test
- fun workspacePaddingRight() {
- assertThat(dp.workspacePadding.right).isEqualTo(43)
- }
-
- @Test
- fun workspacePaddingBottom() {
- assertThat(dp.workspacePadding.bottom).isEqualTo(285)
- }
-
- @Test
- fun iconScale() {
- assertThat(dp.iconScale).isEqualTo(1)
- }
-
- @Test
- fun cellScaleToFit() {
- assertThat(dp.cellScaleToFit).isEqualTo(1.5657895f)
- }
-
- @Test
- fun workspaceTopPadding() {
- assertThat(dp.workspaceTopPadding).isEqualTo(0)
- }
-
- @Test
- fun workspaceBottomPadding() {
- assertThat(dp.workspaceBottomPadding).isEqualTo(0)
- }
-
- @Test
- fun overviewTaskMarginPx() {
- assertThat(dp.overviewTaskMarginPx).isEqualTo(32)
- }
-
- @Test
- fun overviewTaskMarginGridPx() {
- assertThat(dp.overviewTaskMarginGridPx).isEqualTo(32)
- }
-
- @Test
- fun overviewTaskIconSizePx() {
- assertThat(dp.overviewTaskIconSizePx).isEqualTo(96)
- }
-
- @Test
- fun overviewTaskIconDrawableSizePx() {
- assertThat(dp.overviewTaskIconDrawableSizePx).isEqualTo(88)
- }
-
- @Test
- fun overviewTaskIconDrawableSizeGridPx() {
- assertThat(dp.overviewTaskIconDrawableSizeGridPx).isEqualTo(88)
- }
-
- @Test
- fun overviewTaskThumbnailTopMarginPx() {
- assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(160)
- }
-
- @Test
- fun overviewActionsTopMarginPx() {
- assertThat(dp.overviewActionsTopMarginPx).isEqualTo(40)
- }
-
- @Test
- fun overviewActionsHeight() {
- assertThat(dp.overviewActionsHeight).isEqualTo(96)
- }
-
- @Test
- fun overviewActionsButtonSpacing() {
- assertThat(dp.overviewActionsButtonSpacing).isEqualTo(72)
- }
-
- @Test
- fun overviewPageSpacing() {
- assertThat(dp.overviewPageSpacing).isEqualTo(88)
- }
-
- @Test
- fun overviewRowSpacing() {
- assertThat(dp.overviewRowSpacing).isEqualTo(40)
- }
-
- @Test
- fun overviewGridSideMargin() {
- assertThat(dp.overviewGridSideMargin).isEqualTo(128)
- }
-
- @Test
- fun dropTargetBarTopMarginPx() {
- assertThat(dp.dropTargetBarTopMarginPx).isEqualTo(0)
- }
-
- @Test
- fun dropTargetBarSizePx() {
- assertThat(dp.dropTargetBarSizePx).isEqualTo(144)
- }
-
- @Test
- fun dropTargetBarBottomMarginPx() {
- assertThat(dp.dropTargetBarBottomMarginPx).isEqualTo(64)
- }
-
- @Test
- fun workspaceSpringLoadedMinNextPageVisiblePx() {
- assertThat(dp.workspaceSpringLoadedMinNextPageVisiblePx).isEqualTo(48)
- }
-
- @Test
- fun getWorkspaceSpringLoadScale() {
- assertThat(dp.workspaceSpringLoadScale).isEqualTo(0.7226337f)
- }
-
- @Test
- fun getCellLayoutHeight() {
- assertThat(dp.cellLayoutHeight).isEqualTo(1215)
- }
-
- @Test
- fun getCellLayoutWidth() {
- assertThat(dp.cellLayoutWidth).isEqualTo(1237)
- }
-
- @Test
- fun getPanelCount() {
- assertThat(dp.panelCount).isEqualTo(2)
- }
-
- @Test
- fun isVerticalBarLayout() {
- assertThat(dp.isVerticalBarLayout).isEqualTo(false)
- }
-
- @Test
- fun getCellLayoutSpringLoadShrunkTop() {
- assertThat(dp.cellLayoutSpringLoadShrunkTop).isEqualTo(208)
- }
-
- @Test
- fun getCellLayoutSpringLoadShrunkBottom() {
- assertThat(dp.cellLayoutSpringLoadShrunkBottom).isEqualTo(1086)
- }
-
- @Test
- fun getQsbOffsetY() {
- assertThat(dp.qsbOffsetY).isEqualTo(272)
- }
-
- @Test
- fun getTaskbarOffsetY() {
- assertThat(dp.taskbarOffsetY).isEqualTo(112)
- }
-
- @Test
- fun getHotseatLayoutPaddingLeft() {
- assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(761)
- }
-
- @Test
- fun getHotseatLayoutPaddingTop() {
- assertThat(dp.getHotseatLayoutPadding(context).top).isEqualTo(151)
- }
-
- @Test
- fun getHotseatLayoutPaddingRight() {
- assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(761)
- }
-
- @Test
- fun getHotseatLayoutPaddingBottom() {
- assertThat(dp.getHotseatLayoutPadding(context).bottom).isEqualTo(109)
- }
-
- @Test
- fun hotseatBarEndOffset() {
- assertThat(dp.hotseatBarEndOffset).isEqualTo(0)
- }
-}
\ No newline at end of file
diff --git a/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelPortrait3ButtonTest.kt b/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelPortrait3ButtonTest.kt
deleted file mode 100644
index a4b446a..0000000
--- a/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelPortrait3ButtonTest.kt
+++ /dev/null
@@ -1,486 +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.DeviceProfile
-import com.android.launcher3.DeviceProfileBaseTest
-import com.google.common.truth.Truth.assertThat
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-
-/**
- * Tests for DeviceProfile for two panel in portrait with 3-Button navigation.
- */
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class DeviceProfileTwoPanelPortrait3ButtonTest : DeviceProfileBaseTest() {
-
- lateinit var dp: DeviceProfile
-
- @Before
- fun before() {
- initializeVarsForTablet(isTwoPanel = true, isGestureMode = false)
- dp = newDP()
- }
-
- @Test
- fun isScalableGrid() {
- assertThat(dp.isScalableGrid).isTrue()
- }
-
- @Test
- fun cellWidthPx() {
- assertThat(dp.cellWidthPx).isEqualTo(153)
- }
-
- @Test
- fun cellHeightPx() {
- assertThat(dp.cellHeightPx).isEqualTo(199)
- }
-
- @Test
- fun getCellSizeX() {
- assertThat(dp.cellSize.x).isEqualTo(153)
- }
-
- @Test
- fun getCellSizeY() {
- assertThat(dp.cellSize.y).isEqualTo(509)
- }
-
- @Test
- fun cellLayoutBorderSpacePxX() {
- assertThat(dp.cellLayoutBorderSpacePx.x).isEqualTo(38)
- }
-
- @Test
- fun cellLayoutBorderSpacePxY() {
- assertThat(dp.cellLayoutBorderSpacePx.y).isEqualTo(38)
- }
-
- @Test
- fun cellLayoutPaddingPxLeft() {
- assertThat(dp.cellLayoutPaddingPx.left).isEqualTo(19)
- }
-
- @Test
- fun cellLayoutPaddingPxTop() {
- assertThat(dp.cellLayoutPaddingPx.top).isEqualTo(0)
- }
-
- @Test
- fun cellLayoutPaddingPxRight() {
- assertThat(dp.cellLayoutPaddingPx.right).isEqualTo(19)
- }
-
- @Test
- fun cellLayoutPaddingPxBottom() {
- assertThat(dp.cellLayoutPaddingPx.bottom).isEqualTo(19)
- }
-
- @Test
- fun iconSizePx() {
- assertThat(dp.iconSizePx).isEqualTo(112)
- }
-
- @Test
- fun iconTextSizePx() {
- assertThat(dp.iconTextSizePx).isEqualTo(28)
- }
-
- @Test
- fun iconDrawablePaddingPx() {
- assertThat(dp.iconDrawablePaddingPx).isEqualTo(14)
- }
-
- @Test
- fun folderCellWidthPx() {
- assertThat(dp.folderCellWidthPx).isEqualTo(153)
- }
-
- @Test
- fun folderCellHeightPx() {
- assertThat(dp.folderCellHeightPx).isEqualTo(199)
- }
-
- @Test
- fun folderChildIconSizePx() {
- assertThat(dp.folderChildIconSizePx).isEqualTo(112)
- }
-
- @Test
- fun folderChildTextSizePx() {
- assertThat(dp.folderChildTextSizePx).isEqualTo(28)
- }
-
- @Test
- fun folderChildDrawablePaddingPx() {
- assertThat(dp.folderChildDrawablePaddingPx).isEqualTo(16)
- }
-
- @Test
- fun folderCellLayoutBorderSpaceOriginalPx() {
- assertThat(dp.folderCellLayoutBorderSpaceOriginalPx).isEqualTo(0)
- }
-
- @Test
- fun folderCellLayoutBorderSpacePxX() {
- assertThat(dp.folderCellLayoutBorderSpacePx.x).isEqualTo(0)
- }
-
- @Test
- fun folderCellLayoutBorderSpacePxY() {
- assertThat(dp.folderCellLayoutBorderSpacePx.y).isEqualTo(0)
- }
-
- @Test
- fun bottomSheetTopPadding() {
- assertThat(dp.bottomSheetTopPadding).isEqualTo(600)
- }
-
- @Test
- fun allAppsShiftRange() {
- assertThat(dp.allAppsShiftRange).isEqualTo(1960)
- }
-
- @Test
- fun allAppsTopPadding() {
- assertThat(dp.allAppsTopPadding).isEqualTo(600)
- }
-
- @Test
- fun allAppsIconSizePx() {
- assertThat(dp.allAppsIconSizePx).isEqualTo(134)
- }
-
- @Test
- fun allAppsIconTextSizePx() {
- assertThat(dp.allAppsIconTextSizePx).isEqualTo(34)
- }
-
- @Test
- fun allAppsIconDrawablePaddingPx() {
- assertThat(dp.allAppsIconDrawablePaddingPx).isEqualTo(14)
- }
-
- @Test
- fun allAppsCellHeightPx() {
- assertThat(dp.allAppsCellHeightPx).isEqualTo(237)
- }
-
- @Test
- fun allAppsCellWidthPx() {
- assertThat(dp.allAppsCellWidthPx).isEqualTo(153)
- }
-
- @Test
- fun allAppsBorderSpacePxX() {
- assertThat(dp.allAppsBorderSpacePx.x).isEqualTo(38)
- }
-
- @Test
- fun allAppsBorderSpacePxY() {
- assertThat(dp.allAppsBorderSpacePx.y).isEqualTo(38)
- }
-
- @Test
- fun numShownAllAppsColumns() {
- assertThat(dp.numShownAllAppsColumns).isEqualTo(0)
- }
-
- @Test
- fun allAppsLeftRightPadding() {
- assertThat(dp.allAppsLeftRightPadding).isEqualTo(56)
- }
-
- @Test
- fun allAppsLeftRightMargin() {
- assertThat(dp.allAppsLeftRightMargin).isEqualTo(763)
- }
-
- @Test
- fun hotseatBarSizePx() {
- assertThat(dp.hotseatBarSizePx).isEqualTo(386)
- }
-
- @Test
- fun hotseatCellHeightPx() {
- assertThat(dp.hotseatCellHeightPx).isEqualTo(126)
- }
-
- @Test
- fun hotseatBarBottomSpacePx() {
- assertThat(dp.hotseatBarBottomSpacePx).isEqualTo(116)
- }
-
- @Test
- fun hotseatBarSidePaddingStartPx() {
- assertThat(dp.hotseatBarSidePaddingStartPx).isEqualTo(0)
- }
-
- @Test
- fun hotseatBarSidePaddingEndPx() {
- assertThat(dp.hotseatBarSidePaddingEndPx).isEqualTo(0)
- }
-
- @Test
- fun hotseatQsbSpace() {
- assertThat(dp.hotseatQsbSpace).isEqualTo(56)
- }
-
- @Test
- fun hotseatQsbHeight() {
- assertThat(dp.hotseatQsbHeight).isEqualTo(126)
- }
-
- @Test
- fun springLoadedHotseatBarTopMarginPx() {
- assertThat(dp.springLoadedHotseatBarTopMarginPx).isEqualTo(216)
- }
-
- @Test
- fun numShownHotseatIcons() {
- assertThat(dp.numShownHotseatIcons).isEqualTo(6)
- }
-
- @Test
- fun hotseatBorderSpace() {
- assertThat(dp.hotseatBorderSpace).isEqualTo(32)
- }
-
- @Test
- fun isQsbInline() {
- assertThat(dp.isQsbInline).isEqualTo(false)
- }
-
- @Test
- fun qsbWidth() {
- assertThat(dp.qsbWidth).isEqualTo(685)
- }
-
- @Test
- fun isTaskbarPresent() {
- assertThat(dp.isTaskbarPresent).isEqualTo(true)
- }
-
- @Test
- fun isTaskbarPresentInApps() {
- assertThat(dp.isTaskbarPresentInApps).isEqualTo(false)
- }
-
- @Test
- fun taskbarSize() {
- assertThat(dp.taskbarSize).isEqualTo(120)
- }
-
- @Test
- fun desiredWorkspaceHorizontalMarginPx() {
- assertThat(dp.desiredWorkspaceHorizontalMarginPx).isEqualTo(52)
- }
-
- @Test
- fun workspacePaddingLeft() {
- assertThat(dp.workspacePadding.left).isEqualTo(33)
- }
-
- @Test
- fun workspacePaddingTop() {
- assertThat(dp.workspacePadding.top).isEqualTo(0)
- }
-
- @Test
- fun workspacePaddingRight() {
- assertThat(dp.workspacePadding.right).isEqualTo(33)
- }
-
- @Test
- fun workspacePaddingBottom() {
- assertThat(dp.workspacePadding.bottom).isEqualTo(291)
- }
-
- @Test
- fun iconScale() {
- assertThat(dp.iconScale).isEqualTo(1)
- }
-
- @Test
- fun cellScaleToFit() {
- assertThat(dp.cellScaleToFit).isEqualTo(1.1976048f)
- }
-
- @Test
- fun workspaceTopPadding() {
- assertThat(dp.workspaceTopPadding).isEqualTo(0)
- }
-
- @Test
- fun workspaceBottomPadding() {
- assertThat(dp.workspaceBottomPadding).isEqualTo(0)
- }
-
- @Test
- fun overviewTaskMarginPx() {
- assertThat(dp.overviewTaskMarginPx).isEqualTo(32)
- }
-
- @Test
- fun overviewTaskMarginGridPx() {
- assertThat(dp.overviewTaskMarginGridPx).isEqualTo(32)
- }
-
- @Test
- fun overviewTaskIconSizePx() {
- assertThat(dp.overviewTaskIconSizePx).isEqualTo(96)
- }
-
- @Test
- fun overviewTaskIconDrawableSizePx() {
- assertThat(dp.overviewTaskIconDrawableSizePx).isEqualTo(88)
- }
-
- @Test
- fun overviewTaskIconDrawableSizeGridPx() {
- assertThat(dp.overviewTaskIconDrawableSizeGridPx).isEqualTo(88)
- }
-
- @Test
- fun overviewTaskThumbnailTopMarginPx() {
- assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(160)
- }
-
- @Test
- fun overviewActionsTopMarginPx() {
- assertThat(dp.overviewActionsTopMarginPx).isEqualTo(48)
- }
-
- @Test
- fun overviewActionsHeight() {
- assertThat(dp.overviewActionsHeight).isEqualTo(96)
- }
-
- @Test
- fun overviewActionsButtonSpacing() {
- assertThat(dp.overviewActionsButtonSpacing).isEqualTo(72)
- }
-
- @Test
- fun overviewPageSpacing() {
- assertThat(dp.overviewPageSpacing).isEqualTo(88)
- }
-
- @Test
- fun overviewRowSpacing() {
- assertThat(dp.overviewRowSpacing).isEqualTo(40)
- }
-
- @Test
- fun overviewGridSideMargin() {
- assertThat(dp.overviewGridSideMargin).isEqualTo(128)
- }
-
- @Test
- fun dropTargetBarTopMarginPx() {
- assertThat(dp.dropTargetBarTopMarginPx).isEqualTo(220)
- }
-
- @Test
- fun dropTargetBarSizePx() {
- assertThat(dp.dropTargetBarSizePx).isEqualTo(144)
- }
-
- @Test
- fun dropTargetBarBottomMarginPx() {
- assertThat(dp.dropTargetBarBottomMarginPx).isEqualTo(96)
- }
-
- @Test
- fun workspaceSpringLoadedMinNextPageVisiblePx() {
- assertThat(dp.workspaceSpringLoadedMinNextPageVisiblePx).isEqualTo(48)
- }
-
- @Test
- fun getWorkspaceSpringLoadScale() {
- assertThat(dp.workspaceSpringLoadScale).isEqualTo(0.69064087f)
- }
-
- @Test
- fun getCellLayoutHeight() {
- assertThat(dp.cellLayoutHeight).isEqualTo(2169)
- }
-
- @Test
- fun getCellLayoutWidth() {
- assertThat(dp.cellLayoutWidth).isEqualTo(767)
- }
-
- @Test
- fun getPanelCount() {
- assertThat(dp.panelCount).isEqualTo(2)
- }
-
- @Test
- fun isVerticalBarLayout() {
- assertThat(dp.isVerticalBarLayout).isEqualTo(false)
- }
-
- @Test
- fun getCellLayoutSpringLoadShrunkTop() {
- assertThat(dp.cellLayoutSpringLoadShrunkTop).isEqualTo(460)
- }
-
- @Test
- fun getCellLayoutSpringLoadShrunkBottom() {
- assertThat(dp.cellLayoutSpringLoadShrunkBottom).isEqualTo(1958)
- }
-
- @Test
- fun getQsbOffsetY() {
- assertThat(dp.qsbOffsetY).isEqualTo(272)
- }
-
- @Test
- fun getTaskbarOffsetY() {
- assertThat(dp.taskbarOffsetY).isEqualTo(112)
- }
-
- @Test
- fun getHotseatLayoutPaddingLeft() {
- assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(266)
- }
-
- @Test
- fun getHotseatLayoutPaddingTop() {
- assertThat(dp.getHotseatLayoutPadding(context).top).isEqualTo(151)
- }
-
- @Test
- fun getHotseatLayoutPaddingRight() {
- assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(502)
- }
-
- @Test
- fun getHotseatLayoutPaddingBottom() {
- assertThat(dp.getHotseatLayoutPadding(context).bottom).isEqualTo(109)
- }
-
- @Test
- fun hotseatBarEndOffset() {
- assertThat(dp.hotseatBarEndOffset).isEqualTo(502)
- }
-}
\ No newline at end of file
diff --git a/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelPortraitTest.kt b/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelPortraitTest.kt
deleted file mode 100644
index 0c5968e..0000000
--- a/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelPortraitTest.kt
+++ /dev/null
@@ -1,486 +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.DeviceProfile
-import com.android.launcher3.DeviceProfileBaseTest
-import com.google.common.truth.Truth.assertThat
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-
-/**
- * Tests for DeviceProfile for two panel in portrait.
- */
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class DeviceProfileTwoPanelPortraitTest : DeviceProfileBaseTest() {
-
- lateinit var dp: DeviceProfile
-
- @Before
- fun before() {
- initializeVarsForTablet(isTwoPanel = true)
- dp = newDP()
- }
-
- @Test
- fun isScalableGrid() {
- assertThat(dp.isScalableGrid).isTrue()
- }
-
- @Test
- fun cellWidthPx() {
- assertThat(dp.cellWidthPx).isEqualTo(153)
- }
-
- @Test
- fun cellHeightPx() {
- assertThat(dp.cellHeightPx).isEqualTo(199)
- }
-
- @Test
- fun getCellSizeX() {
- assertThat(dp.cellSize.x).isEqualTo(153)
- }
-
- @Test
- fun getCellSizeY() {
- assertThat(dp.cellSize.y).isEqualTo(509)
- }
-
- @Test
- fun cellLayoutBorderSpacePxX() {
- assertThat(dp.cellLayoutBorderSpacePx.x).isEqualTo(38)
- }
-
- @Test
- fun cellLayoutBorderSpacePxY() {
- assertThat(dp.cellLayoutBorderSpacePx.y).isEqualTo(38)
- }
-
- @Test
- fun cellLayoutPaddingPxLeft() {
- assertThat(dp.cellLayoutPaddingPx.left).isEqualTo(19)
- }
-
- @Test
- fun cellLayoutPaddingPxTop() {
- assertThat(dp.cellLayoutPaddingPx.top).isEqualTo(0)
- }
-
- @Test
- fun cellLayoutPaddingPxRight() {
- assertThat(dp.cellLayoutPaddingPx.right).isEqualTo(19)
- }
-
- @Test
- fun cellLayoutPaddingPxBottom() {
- assertThat(dp.cellLayoutPaddingPx.bottom).isEqualTo(19)
- }
-
- @Test
- fun iconSizePx() {
- assertThat(dp.iconSizePx).isEqualTo(112)
- }
-
- @Test
- fun iconTextSizePx() {
- assertThat(dp.iconTextSizePx).isEqualTo(28)
- }
-
- @Test
- fun iconDrawablePaddingPx() {
- assertThat(dp.iconDrawablePaddingPx).isEqualTo(14)
- }
-
- @Test
- fun folderCellWidthPx() {
- assertThat(dp.folderCellWidthPx).isEqualTo(153)
- }
-
- @Test
- fun folderCellHeightPx() {
- assertThat(dp.folderCellHeightPx).isEqualTo(199)
- }
-
- @Test
- fun folderChildIconSizePx() {
- assertThat(dp.folderChildIconSizePx).isEqualTo(112)
- }
-
- @Test
- fun folderChildTextSizePx() {
- assertThat(dp.folderChildTextSizePx).isEqualTo(28)
- }
-
- @Test
- fun folderChildDrawablePaddingPx() {
- assertThat(dp.folderChildDrawablePaddingPx).isEqualTo(16)
- }
-
- @Test
- fun folderCellLayoutBorderSpaceOriginalPx() {
- assertThat(dp.folderCellLayoutBorderSpaceOriginalPx).isEqualTo(0)
- }
-
- @Test
- fun folderCellLayoutBorderSpacePxX() {
- assertThat(dp.folderCellLayoutBorderSpacePx.x).isEqualTo(0)
- }
-
- @Test
- fun folderCellLayoutBorderSpacePxY() {
- assertThat(dp.folderCellLayoutBorderSpacePx.y).isEqualTo(0)
- }
-
- @Test
- fun bottomSheetTopPadding() {
- assertThat(dp.bottomSheetTopPadding).isEqualTo(600)
- }
-
- @Test
- fun allAppsShiftRange() {
- assertThat(dp.allAppsShiftRange).isEqualTo(1960)
- }
-
- @Test
- fun allAppsTopPadding() {
- assertThat(dp.allAppsTopPadding).isEqualTo(600)
- }
-
- @Test
- fun allAppsIconSizePx() {
- assertThat(dp.allAppsIconSizePx).isEqualTo(134)
- }
-
- @Test
- fun allAppsIconTextSizePx() {
- assertThat(dp.allAppsIconTextSizePx).isEqualTo(34)
- }
-
- @Test
- fun allAppsIconDrawablePaddingPx() {
- assertThat(dp.allAppsIconDrawablePaddingPx).isEqualTo(14)
- }
-
- @Test
- fun allAppsCellHeightPx() {
- assertThat(dp.allAppsCellHeightPx).isEqualTo(237)
- }
-
- @Test
- fun allAppsCellWidthPx() {
- assertThat(dp.allAppsCellWidthPx).isEqualTo(153)
- }
-
- @Test
- fun allAppsBorderSpacePxX() {
- assertThat(dp.allAppsBorderSpacePx.x).isEqualTo(38)
- }
-
- @Test
- fun allAppsBorderSpacePxY() {
- assertThat(dp.allAppsBorderSpacePx.y).isEqualTo(38)
- }
-
- @Test
- fun numShownAllAppsColumns() {
- assertThat(dp.numShownAllAppsColumns).isEqualTo(0)
- }
-
- @Test
- fun allAppsLeftRightPadding() {
- assertThat(dp.allAppsLeftRightPadding).isEqualTo(56)
- }
-
- @Test
- fun allAppsLeftRightMargin() {
- assertThat(dp.allAppsLeftRightMargin).isEqualTo(763)
- }
-
- @Test
- fun hotseatBarSizePx() {
- assertThat(dp.hotseatBarSizePx).isEqualTo(386)
- }
-
- @Test
- fun hotseatCellHeightPx() {
- assertThat(dp.hotseatCellHeightPx).isEqualTo(126)
- }
-
- @Test
- fun hotseatBarBottomSpacePx() {
- assertThat(dp.hotseatBarBottomSpacePx).isEqualTo(116)
- }
-
- @Test
- fun hotseatBarSidePaddingStartPx() {
- assertThat(dp.hotseatBarSidePaddingStartPx).isEqualTo(0)
- }
-
- @Test
- fun hotseatBarSidePaddingEndPx() {
- assertThat(dp.hotseatBarSidePaddingEndPx).isEqualTo(0)
- }
-
- @Test
- fun hotseatQsbSpace() {
- assertThat(dp.hotseatQsbSpace).isEqualTo(56)
- }
-
- @Test
- fun hotseatQsbHeight() {
- assertThat(dp.hotseatQsbHeight).isEqualTo(126)
- }
-
- @Test
- fun springLoadedHotseatBarTopMarginPx() {
- assertThat(dp.springLoadedHotseatBarTopMarginPx).isEqualTo(216)
- }
-
- @Test
- fun numShownHotseatIcons() {
- assertThat(dp.numShownHotseatIcons).isEqualTo(6)
- }
-
- @Test
- fun hotseatBorderSpace() {
- assertThat(dp.hotseatBorderSpace).isEqualTo(2)
- }
-
- @Test
- fun isQsbInline() {
- assertThat(dp.isQsbInline).isEqualTo(false)
- }
-
- @Test
- fun qsbWidth() {
- assertThat(dp.qsbWidth).isEqualTo(685)
- }
-
- @Test
- fun isTaskbarPresent() {
- assertThat(dp.isTaskbarPresent).isEqualTo(true)
- }
-
- @Test
- fun isTaskbarPresentInApps() {
- assertThat(dp.isTaskbarPresentInApps).isEqualTo(false)
- }
-
- @Test
- fun taskbarSize() {
- assertThat(dp.taskbarSize).isEqualTo(120)
- }
-
- @Test
- fun desiredWorkspaceHorizontalMarginPx() {
- assertThat(dp.desiredWorkspaceHorizontalMarginPx).isEqualTo(52)
- }
-
- @Test
- fun workspacePaddingLeft() {
- assertThat(dp.workspacePadding.left).isEqualTo(33)
- }
-
- @Test
- fun workspacePaddingTop() {
- assertThat(dp.workspacePadding.top).isEqualTo(0)
- }
-
- @Test
- fun workspacePaddingRight() {
- assertThat(dp.workspacePadding.right).isEqualTo(33)
- }
-
- @Test
- fun workspacePaddingBottom() {
- assertThat(dp.workspacePadding.bottom).isEqualTo(291)
- }
-
- @Test
- fun iconScale() {
- assertThat(dp.iconScale).isEqualTo(1)
- }
-
- @Test
- fun cellScaleToFit() {
- assertThat(dp.cellScaleToFit).isEqualTo(1.1976048f)
- }
-
- @Test
- fun workspaceTopPadding() {
- assertThat(dp.workspaceTopPadding).isEqualTo(0)
- }
-
- @Test
- fun workspaceBottomPadding() {
- assertThat(dp.workspaceBottomPadding).isEqualTo(0)
- }
-
- @Test
- fun overviewTaskMarginPx() {
- assertThat(dp.overviewTaskMarginPx).isEqualTo(32)
- }
-
- @Test
- fun overviewTaskMarginGridPx() {
- assertThat(dp.overviewTaskMarginGridPx).isEqualTo(32)
- }
-
- @Test
- fun overviewTaskIconSizePx() {
- assertThat(dp.overviewTaskIconSizePx).isEqualTo(96)
- }
-
- @Test
- fun overviewTaskIconDrawableSizePx() {
- assertThat(dp.overviewTaskIconDrawableSizePx).isEqualTo(88)
- }
-
- @Test
- fun overviewTaskIconDrawableSizeGridPx() {
- assertThat(dp.overviewTaskIconDrawableSizeGridPx).isEqualTo(88)
- }
-
- @Test
- fun overviewTaskThumbnailTopMarginPx() {
- assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(160)
- }
-
- @Test
- fun overviewActionsTopMarginPx() {
- assertThat(dp.overviewActionsTopMarginPx).isEqualTo(48)
- }
-
- @Test
- fun overviewActionsHeight() {
- assertThat(dp.overviewActionsHeight).isEqualTo(96)
- }
-
- @Test
- fun overviewActionsButtonSpacing() {
- assertThat(dp.overviewActionsButtonSpacing).isEqualTo(72)
- }
-
- @Test
- fun overviewPageSpacing() {
- assertThat(dp.overviewPageSpacing).isEqualTo(88)
- }
-
- @Test
- fun overviewRowSpacing() {
- assertThat(dp.overviewRowSpacing).isEqualTo(40)
- }
-
- @Test
- fun overviewGridSideMargin() {
- assertThat(dp.overviewGridSideMargin).isEqualTo(128)
- }
-
- @Test
- fun dropTargetBarTopMarginPx() {
- assertThat(dp.dropTargetBarTopMarginPx).isEqualTo(220)
- }
-
- @Test
- fun dropTargetBarSizePx() {
- assertThat(dp.dropTargetBarSizePx).isEqualTo(144)
- }
-
- @Test
- fun dropTargetBarBottomMarginPx() {
- assertThat(dp.dropTargetBarBottomMarginPx).isEqualTo(96)
- }
-
- @Test
- fun workspaceSpringLoadedMinNextPageVisiblePx() {
- assertThat(dp.workspaceSpringLoadedMinNextPageVisiblePx).isEqualTo(48)
- }
-
- @Test
- fun getWorkspaceSpringLoadScale() {
- assertThat(dp.workspaceSpringLoadScale).isEqualTo(0.69064087f)
- }
-
- @Test
- fun getCellLayoutHeight() {
- assertThat(dp.cellLayoutHeight).isEqualTo(2169)
- }
-
- @Test
- fun getCellLayoutWidth() {
- assertThat(dp.cellLayoutWidth).isEqualTo(767)
- }
-
- @Test
- fun getPanelCount() {
- assertThat(dp.panelCount).isEqualTo(2)
- }
-
- @Test
- fun isVerticalBarLayout() {
- assertThat(dp.isVerticalBarLayout).isEqualTo(false)
- }
-
- @Test
- fun getCellLayoutSpringLoadShrunkTop() {
- assertThat(dp.cellLayoutSpringLoadShrunkTop).isEqualTo(460)
- }
-
- @Test
- fun getCellLayoutSpringLoadShrunkBottom() {
- assertThat(dp.cellLayoutSpringLoadShrunkBottom).isEqualTo(1958)
- }
-
- @Test
- fun getQsbOffsetY() {
- assertThat(dp.qsbOffsetY).isEqualTo(272)
- }
-
- @Test
- fun getTaskbarOffsetY() {
- assertThat(dp.taskbarOffsetY).isEqualTo(112)
- }
-
- @Test
- fun getHotseatLayoutPaddingLeft() {
- assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(459)
- }
-
- @Test
- fun getHotseatLayoutPaddingTop() {
- assertThat(dp.getHotseatLayoutPadding(context).top).isEqualTo(151)
- }
-
- @Test
- fun getHotseatLayoutPaddingRight() {
- assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(459)
- }
-
- @Test
- fun getHotseatLayoutPaddingBottom() {
- assertThat(dp.getHotseatLayoutPadding(context).bottom).isEqualTo(109)
- }
-
- @Test
- fun hotseatBarEndOffset() {
- assertThat(dp.hotseatBarEndOffset).isEqualTo(0)
- }
-}
\ No newline at end of file
diff --git a/quickstep/tests/src/com/android/quickstep/DeviceProfileVerticalBar3ButtonTest.kt b/quickstep/tests/src/com/android/quickstep/DeviceProfileVerticalBar3ButtonTest.kt
deleted file mode 100644
index 2bad6bb..0000000
--- a/quickstep/tests/src/com/android/quickstep/DeviceProfileVerticalBar3ButtonTest.kt
+++ /dev/null
@@ -1,486 +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.DeviceProfile
-import com.android.launcher3.DeviceProfileBaseTest
-import com.google.common.truth.Truth.assertThat
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-
-/**
- * Tests for DeviceProfile for landscape phone with vertical bar and 3-Button navigation.
- */
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class DeviceProfileVerticalBar3ButtonTest : DeviceProfileBaseTest() {
-
- lateinit var dp: DeviceProfile
-
- @Before
- fun before() {
- initializeVarsForPhone(isVerticalBar = true, isGestureMode = false)
- dp = newDP()
- }
-
- @Test
- fun isScalableGrid() {
- assertThat(dp.isScalableGrid).isFalse()
- }
-
- @Test
- fun cellWidthPx() {
- assertThat(dp.cellWidthPx).isEqualTo(210)
- }
-
- @Test
- fun cellHeightPx() {
- assertThat(dp.cellHeightPx).isEqualTo(221)
- }
-
- @Test
- fun getCellSizeX() {
- assertThat(dp.cellSize.x).isEqualTo(675)
- }
-
- @Test
- fun getCellSizeY() {
- assertThat(dp.cellSize.y).isEqualTo(321)
- }
-
- @Test
- fun cellLayoutBorderSpacePxX() {
- assertThat(dp.cellLayoutBorderSpacePx.x).isEqualTo(0)
- }
-
- @Test
- fun cellLayoutBorderSpacePxY() {
- assertThat(dp.cellLayoutBorderSpacePx.y).isEqualTo(0)
- }
-
- @Test
- fun cellLayoutPaddingPxLeft() {
- assertThat(dp.cellLayoutPaddingPx.left).isEqualTo(70)
- }
-
- @Test
- fun cellLayoutPaddingPxTop() {
- assertThat(dp.cellLayoutPaddingPx.top).isEqualTo(0)
- }
-
- @Test
- fun cellLayoutPaddingPxRight() {
- assertThat(dp.cellLayoutPaddingPx.right).isEqualTo(70)
- }
-
- @Test
- fun cellLayoutPaddingPxBottom() {
- assertThat(dp.cellLayoutPaddingPx.bottom).isEqualTo(53)
- }
-
- @Test
- fun iconSizePx() {
- assertThat(dp.iconSizePx).isEqualTo(196)
- }
-
- @Test
- fun iconTextSizePx() {
- assertThat(dp.iconTextSizePx).isEqualTo(0)
- }
-
- @Test
- fun iconDrawablePaddingPx() {
- assertThat(dp.iconDrawablePaddingPx).isEqualTo(0)
- }
-
- @Test
- fun folderCellWidthPx() {
- assertThat(dp.folderCellWidthPx).isEqualTo(260)
- }
-
- @Test
- fun folderCellHeightPx() {
- assertThat(dp.folderCellHeightPx).isEqualTo(304)
- }
-
- @Test
- fun folderChildIconSizePx() {
- assertThat(dp.folderChildIconSizePx).isEqualTo(196)
- }
-
- @Test
- fun folderChildTextSizePx() {
- assertThat(dp.folderChildTextSizePx).isEqualTo(49)
- }
-
- @Test
- fun folderChildDrawablePaddingPx() {
- assertThat(dp.folderChildDrawablePaddingPx).isEqualTo(14)
- }
-
- @Test
- fun folderCellLayoutBorderSpaceOriginalPx() {
- assertThat(dp.folderCellLayoutBorderSpaceOriginalPx).isEqualTo(0)
- }
-
- @Test
- fun folderCellLayoutBorderSpacePxX() {
- assertThat(dp.folderCellLayoutBorderSpacePx.x).isEqualTo(0)
- }
-
- @Test
- fun folderCellLayoutBorderSpacePxY() {
- assertThat(dp.folderCellLayoutBorderSpacePx.y).isEqualTo(0)
- }
-
- @Test
- fun bottomSheetTopPadding() {
- assertThat(dp.bottomSheetTopPadding).isEqualTo(53)
- }
-
- @Test
- fun allAppsShiftRange() {
- assertThat(dp.allAppsShiftRange).isEqualTo(1050)
- }
-
- @Test
- fun allAppsTopPadding() {
- assertThat(dp.allAppsTopPadding).isEqualTo(0)
- }
-
- @Test
- fun allAppsIconSizePx() {
- assertThat(dp.allAppsIconSizePx).isEqualTo(196)
- }
-
- @Test
- fun allAppsIconTextSizePx() {
- assertThat(dp.allAppsIconTextSizePx).isEqualTo(49)
- }
-
- @Test
- fun allAppsIconDrawablePaddingPx() {
- assertThat(dp.allAppsIconDrawablePaddingPx).isEqualTo(28)
- }
-
- @Test
- fun allAppsCellHeightPx() {
- assertThat(dp.allAppsCellHeightPx).isEqualTo(422)
- }
-
- @Test
- fun allAppsCellWidthPx() {
- assertThat(dp.allAppsCellWidthPx).isEqualTo(252)
- }
-
- @Test
- fun allAppsBorderSpacePxX() {
- assertThat(dp.allAppsBorderSpacePx.x).isEqualTo(56)
- }
-
- @Test
- fun allAppsBorderSpacePxY() {
- assertThat(dp.allAppsBorderSpacePx.y).isEqualTo(56)
- }
-
- @Test
- fun numShownAllAppsColumns() {
- assertThat(dp.numShownAllAppsColumns).isEqualTo(0)
- }
-
- @Test
- fun allAppsLeftRightPadding() {
- assertThat(dp.allAppsLeftRightPadding).isEqualTo(0)
- }
-
- @Test
- fun allAppsLeftRightMargin() {
- assertThat(dp.allAppsLeftRightMargin).isEqualTo(0)
- }
-
- @Test
- fun hotseatBarSizePx() {
- assertThat(dp.hotseatBarSizePx).isEqualTo(336)
- }
-
- @Test
- fun hotseatCellHeightPx() {
- assertThat(dp.hotseatCellHeightPx).isEqualTo(221)
- }
-
- @Test
- fun hotseatBarBottomSpacePx() {
- assertThat(dp.hotseatBarBottomSpacePx).isEqualTo(168)
- }
-
- @Test
- fun hotseatBarSidePaddingStartPx() {
- assertThat(dp.hotseatBarSidePaddingStartPx).isEqualTo(84)
- }
-
- @Test
- fun hotseatBarSidePaddingEndPx() {
- assertThat(dp.hotseatBarSidePaddingEndPx).isEqualTo(56)
- }
-
- @Test
- fun hotseatQsbSpace() {
- assertThat(dp.hotseatQsbSpace).isEqualTo(126)
- }
-
- @Test
- fun hotseatQsbHeight() {
- assertThat(dp.hotseatQsbHeight).isEqualTo(221)
- }
-
- @Test
- fun springLoadedHotseatBarTopMarginPx() {
- assertThat(dp.springLoadedHotseatBarTopMarginPx).isEqualTo(158)
- }
-
- @Test
- fun numShownHotseatIcons() {
- assertThat(dp.numShownHotseatIcons).isEqualTo(4)
- }
-
- @Test
- fun hotseatBorderSpace() {
- assertThat(dp.hotseatBorderSpace).isEqualTo(0)
- }
-
- @Test
- fun isQsbInline() {
- assertThat(dp.isQsbInline).isEqualTo(false)
- }
-
- @Test
- fun qsbWidth() {
- assertThat(dp.qsbWidth).isEqualTo(2221)
- }
-
- @Test
- fun isTaskbarPresent() {
- assertThat(dp.isTaskbarPresent).isEqualTo(false)
- }
-
- @Test
- fun isTaskbarPresentInApps() {
- assertThat(dp.isTaskbarPresentInApps).isEqualTo(false)
- }
-
- @Test
- fun taskbarSize() {
- assertThat(dp.taskbarSize).isEqualTo(0)
- }
-
- @Test
- fun desiredWorkspaceHorizontalMarginPx() {
- assertThat(dp.desiredWorkspaceHorizontalMarginPx).isEqualTo(0)
- }
-
- @Test
- fun workspacePaddingLeft() {
- assertThat(dp.workspacePadding.left).isEqualTo(14)
- }
-
- @Test
- fun workspacePaddingTop() {
- assertThat(dp.workspacePadding.top).isEqualTo(0)
- }
-
- @Test
- fun workspacePaddingRight() {
- assertThat(dp.workspacePadding.right).isEqualTo(266)
- }
-
- @Test
- fun workspacePaddingBottom() {
- assertThat(dp.workspacePadding.bottom).isEqualTo(0)
- }
-
- @Test
- fun iconScale() {
- assertThat(dp.iconScale).isEqualTo(1)
- }
-
- @Test
- fun cellScaleToFit() {
- assertThat(dp.cellScaleToFit).isEqualTo(1.0f)
- }
-
- @Test
- fun workspaceTopPadding() {
- assertThat(dp.workspaceTopPadding).isEqualTo(0)
- }
-
- @Test
- fun workspaceBottomPadding() {
- assertThat(dp.workspaceBottomPadding).isEqualTo(0)
- }
-
- @Test
- fun overviewTaskMarginPx() {
- assertThat(dp.overviewTaskMarginPx).isEqualTo(56)
- }
-
- @Test
- fun overviewTaskMarginGridPx() {
- assertThat(dp.overviewTaskMarginGridPx).isEqualTo(0)
- }
-
- @Test
- fun overviewTaskIconSizePx() {
- assertThat(dp.overviewTaskIconSizePx).isEqualTo(168)
- }
-
- @Test
- fun overviewTaskIconDrawableSizePx() {
- assertThat(dp.overviewTaskIconDrawableSizePx).isEqualTo(154)
- }
-
- @Test
- fun overviewTaskIconDrawableSizeGridPx() {
- assertThat(dp.overviewTaskIconDrawableSizeGridPx).isEqualTo(0)
- }
-
- @Test
- fun overviewTaskThumbnailTopMarginPx() {
- assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(280)
- }
-
- @Test
- fun overviewActionsTopMarginPx() {
- assertThat(dp.overviewActionsTopMarginPx).isEqualTo(42)
- }
-
- @Test
- fun overviewActionsHeight() {
- assertThat(dp.overviewActionsHeight).isEqualTo(168)
- }
-
- @Test
- fun overviewActionsButtonSpacing() {
- assertThat(dp.overviewActionsButtonSpacing).isEqualTo(126)
- }
-
- @Test
- fun overviewPageSpacing() {
- assertThat(dp.overviewPageSpacing).isEqualTo(56)
- }
-
- @Test
- fun overviewRowSpacing() {
- assertThat(dp.overviewRowSpacing).isEqualTo(-112)
- }
-
- @Test
- fun overviewGridSideMargin() {
- assertThat(dp.overviewGridSideMargin).isEqualTo(0)
- }
-
- @Test
- fun dropTargetBarTopMarginPx() {
- assertThat(dp.dropTargetBarTopMarginPx).isEqualTo(21)
- }
-
- @Test
- fun dropTargetBarSizePx() {
- assertThat(dp.dropTargetBarSizePx).isEqualTo(126)
- }
-
- @Test
- fun dropTargetBarBottomMarginPx() {
- assertThat(dp.dropTargetBarBottomMarginPx).isEqualTo(21)
- }
-
- @Test
- fun workspaceSpringLoadedMinNextPageVisiblePx() {
- assertThat(dp.workspaceSpringLoadedMinNextPageVisiblePx).isEqualTo(84)
- }
-
- @Test
- fun getWorkspaceSpringLoadScale() {
- assertThat(dp.workspaceSpringLoadScale).isEqualTo(0.8880597f)
- }
-
- @Test
- fun getCellLayoutHeight() {
- assertThat(dp.cellLayoutHeight).isEqualTo(1340)
- }
-
- @Test
- fun getCellLayoutWidth() {
- assertThat(dp.cellLayoutWidth).isEqualTo(2840)
- }
-
- @Test
- fun getPanelCount() {
- assertThat(dp.panelCount).isEqualTo(1)
- }
-
- @Test
- fun isVerticalBarLayout() {
- assertThat(dp.isVerticalBarLayout).isEqualTo(true)
- }
-
- @Test
- fun getCellLayoutSpringLoadShrunkTop() {
- assertThat(dp.cellLayoutSpringLoadShrunkTop).isEqualTo(168)
- }
-
- @Test
- fun getCellLayoutSpringLoadShrunkBottom() {
- assertThat(dp.cellLayoutSpringLoadShrunkBottom).isEqualTo(1358)
- }
-
- @Test
- fun getQsbOffsetY() {
- assertThat(dp.qsbOffsetY).isEqualTo(147)
- }
-
- @Test
- fun getTaskbarOffsetY() {
- assertThat(dp.taskbarOffsetY).isEqualTo(225)
- }
-
- @Test
- fun getHotseatLayoutPaddingLeft() {
- assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(56)
- }
-
- @Test
- fun getHotseatLayoutPaddingTop() {
- assertThat(dp.getHotseatLayoutPadding(context).top).isEqualTo(0)
- }
-
- @Test
- fun getHotseatLayoutPaddingRight() {
- assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(84)
- }
-
- @Test
- fun getHotseatLayoutPaddingBottom() {
- assertThat(dp.getHotseatLayoutPadding(context).bottom).isEqualTo(165)
- }
-
- @Test
- fun hotseatBarEndOffset() {
- assertThat(dp.hotseatBarEndOffset).isEqualTo(0)
- }
-}
\ No newline at end of file
diff --git a/quickstep/tests/src/com/android/quickstep/DeviceProfileVerticalBarTest.kt b/quickstep/tests/src/com/android/quickstep/DeviceProfileVerticalBarTest.kt
deleted file mode 100644
index 6256a43..0000000
--- a/quickstep/tests/src/com/android/quickstep/DeviceProfileVerticalBarTest.kt
+++ /dev/null
@@ -1,486 +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.DeviceProfile
-import com.android.launcher3.DeviceProfileBaseTest
-import com.google.common.truth.Truth.assertThat
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-
-/**
- * Tests for DeviceProfile for landscape phone with vertical bar.
- */
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class DeviceProfileVerticalBarTest : DeviceProfileBaseTest() {
-
- lateinit var dp: DeviceProfile
-
- @Before
- fun before() {
- initializeVarsForPhone(isVerticalBar = true)
- dp = newDP()
- }
-
- @Test
- fun isScalableGrid() {
- assertThat(dp.isScalableGrid).isFalse()
- }
-
- @Test
- fun cellWidthPx() {
- assertThat(dp.cellWidthPx).isEqualTo(210)
- }
-
- @Test
- fun cellHeightPx() {
- assertThat(dp.cellHeightPx).isEqualTo(221)
- }
-
- @Test
- fun getCellSizeX() {
- assertThat(dp.cellSize.x).isEqualTo(675)
- }
-
- @Test
- fun getCellSizeY() {
- assertThat(dp.cellSize.y).isEqualTo(321)
- }
-
- @Test
- fun cellLayoutBorderSpacePxX() {
- assertThat(dp.cellLayoutBorderSpacePx.x).isEqualTo(0)
- }
-
- @Test
- fun cellLayoutBorderSpacePxY() {
- assertThat(dp.cellLayoutBorderSpacePx.y).isEqualTo(0)
- }
-
- @Test
- fun cellLayoutPaddingPxLeft() {
- assertThat(dp.cellLayoutPaddingPx.left).isEqualTo(70)
- }
-
- @Test
- fun cellLayoutPaddingPxTop() {
- assertThat(dp.cellLayoutPaddingPx.top).isEqualTo(0)
- }
-
- @Test
- fun cellLayoutPaddingPxRight() {
- assertThat(dp.cellLayoutPaddingPx.right).isEqualTo(70)
- }
-
- @Test
- fun cellLayoutPaddingPxBottom() {
- assertThat(dp.cellLayoutPaddingPx.bottom).isEqualTo(53)
- }
-
- @Test
- fun iconSizePx() {
- assertThat(dp.iconSizePx).isEqualTo(196)
- }
-
- @Test
- fun iconTextSizePx() {
- assertThat(dp.iconTextSizePx).isEqualTo(0)
- }
-
- @Test
- fun iconDrawablePaddingPx() {
- assertThat(dp.iconDrawablePaddingPx).isEqualTo(0)
- }
-
- @Test
- fun folderCellWidthPx() {
- assertThat(dp.folderCellWidthPx).isEqualTo(260)
- }
-
- @Test
- fun folderCellHeightPx() {
- assertThat(dp.folderCellHeightPx).isEqualTo(304)
- }
-
- @Test
- fun folderChildIconSizePx() {
- assertThat(dp.folderChildIconSizePx).isEqualTo(196)
- }
-
- @Test
- fun folderChildTextSizePx() {
- assertThat(dp.folderChildTextSizePx).isEqualTo(49)
- }
-
- @Test
- fun folderChildDrawablePaddingPx() {
- assertThat(dp.folderChildDrawablePaddingPx).isEqualTo(14)
- }
-
- @Test
- fun folderCellLayoutBorderSpaceOriginalPx() {
- assertThat(dp.folderCellLayoutBorderSpaceOriginalPx).isEqualTo(0)
- }
-
- @Test
- fun folderCellLayoutBorderSpacePxX() {
- assertThat(dp.folderCellLayoutBorderSpacePx.x).isEqualTo(0)
- }
-
- @Test
- fun folderCellLayoutBorderSpacePxY() {
- assertThat(dp.folderCellLayoutBorderSpacePx.y).isEqualTo(0)
- }
-
- @Test
- fun bottomSheetTopPadding() {
- assertThat(dp.bottomSheetTopPadding).isEqualTo(53)
- }
-
- @Test
- fun allAppsShiftRange() {
- assertThat(dp.allAppsShiftRange).isEqualTo(1050)
- }
-
- @Test
- fun allAppsTopPadding() {
- assertThat(dp.allAppsTopPadding).isEqualTo(0)
- }
-
- @Test
- fun allAppsIconSizePx() {
- assertThat(dp.allAppsIconSizePx).isEqualTo(196)
- }
-
- @Test
- fun allAppsIconTextSizePx() {
- assertThat(dp.allAppsIconTextSizePx).isEqualTo(49)
- }
-
- @Test
- fun allAppsIconDrawablePaddingPx() {
- assertThat(dp.allAppsIconDrawablePaddingPx).isEqualTo(28)
- }
-
- @Test
- fun allAppsCellHeightPx() {
- assertThat(dp.allAppsCellHeightPx).isEqualTo(422)
- }
-
- @Test
- fun allAppsCellWidthPx() {
- assertThat(dp.allAppsCellWidthPx).isEqualTo(252)
- }
-
- @Test
- fun allAppsBorderSpacePxX() {
- assertThat(dp.allAppsBorderSpacePx.x).isEqualTo(56)
- }
-
- @Test
- fun allAppsBorderSpacePxY() {
- assertThat(dp.allAppsBorderSpacePx.y).isEqualTo(56)
- }
-
- @Test
- fun numShownAllAppsColumns() {
- assertThat(dp.numShownAllAppsColumns).isEqualTo(0)
- }
-
- @Test
- fun allAppsLeftRightPadding() {
- assertThat(dp.allAppsLeftRightPadding).isEqualTo(0)
- }
-
- @Test
- fun allAppsLeftRightMargin() {
- assertThat(dp.allAppsLeftRightMargin).isEqualTo(0)
- }
-
- @Test
- fun hotseatBarSizePx() {
- assertThat(dp.hotseatBarSizePx).isEqualTo(336)
- }
-
- @Test
- fun hotseatCellHeightPx() {
- assertThat(dp.hotseatCellHeightPx).isEqualTo(221)
- }
-
- @Test
- fun hotseatBarBottomSpacePx() {
- assertThat(dp.hotseatBarBottomSpacePx).isEqualTo(168)
- }
-
- @Test
- fun hotseatBarSidePaddingStartPx() {
- assertThat(dp.hotseatBarSidePaddingStartPx).isEqualTo(84)
- }
-
- @Test
- fun hotseatBarSidePaddingEndPx() {
- assertThat(dp.hotseatBarSidePaddingEndPx).isEqualTo(56)
- }
-
- @Test
- fun hotseatQsbSpace() {
- assertThat(dp.hotseatQsbSpace).isEqualTo(126)
- }
-
- @Test
- fun hotseatQsbHeight() {
- assertThat(dp.hotseatQsbHeight).isEqualTo(221)
- }
-
- @Test
- fun springLoadedHotseatBarTopMarginPx() {
- assertThat(dp.springLoadedHotseatBarTopMarginPx).isEqualTo(158)
- }
-
- @Test
- fun numShownHotseatIcons() {
- assertThat(dp.numShownHotseatIcons).isEqualTo(4)
- }
-
- @Test
- fun hotseatBorderSpace() {
- assertThat(dp.hotseatBorderSpace).isEqualTo(0)
- }
-
- @Test
- fun isQsbInline() {
- assertThat(dp.isQsbInline).isEqualTo(false)
- }
-
- @Test
- fun qsbWidth() {
- assertThat(dp.qsbWidth).isEqualTo(2221)
- }
-
- @Test
- fun isTaskbarPresent() {
- assertThat(dp.isTaskbarPresent).isEqualTo(false)
- }
-
- @Test
- fun isTaskbarPresentInApps() {
- assertThat(dp.isTaskbarPresentInApps).isEqualTo(false)
- }
-
- @Test
- fun taskbarSize() {
- assertThat(dp.taskbarSize).isEqualTo(0)
- }
-
- @Test
- fun desiredWorkspaceHorizontalMarginPx() {
- assertThat(dp.desiredWorkspaceHorizontalMarginPx).isEqualTo(0)
- }
-
- @Test
- fun workspacePaddingLeft() {
- assertThat(dp.workspacePadding.left).isEqualTo(14)
- }
-
- @Test
- fun workspacePaddingTop() {
- assertThat(dp.workspacePadding.top).isEqualTo(0)
- }
-
- @Test
- fun workspacePaddingRight() {
- assertThat(dp.workspacePadding.right).isEqualTo(266)
- }
-
- @Test
- fun workspacePaddingBottom() {
- assertThat(dp.workspacePadding.bottom).isEqualTo(0)
- }
-
- @Test
- fun iconScale() {
- assertThat(dp.iconScale).isEqualTo(1)
- }
-
- @Test
- fun cellScaleToFit() {
- assertThat(dp.cellScaleToFit).isEqualTo(1.0f)
- }
-
- @Test
- fun workspaceTopPadding() {
- assertThat(dp.workspaceTopPadding).isEqualTo(0)
- }
-
- @Test
- fun workspaceBottomPadding() {
- assertThat(dp.workspaceBottomPadding).isEqualTo(0)
- }
-
- @Test
- fun overviewTaskMarginPx() {
- assertThat(dp.overviewTaskMarginPx).isEqualTo(56)
- }
-
- @Test
- fun overviewTaskMarginGridPx() {
- assertThat(dp.overviewTaskMarginGridPx).isEqualTo(0)
- }
-
- @Test
- fun overviewTaskIconSizePx() {
- assertThat(dp.overviewTaskIconSizePx).isEqualTo(168)
- }
-
- @Test
- fun overviewTaskIconDrawableSizePx() {
- assertThat(dp.overviewTaskIconDrawableSizePx).isEqualTo(154)
- }
-
- @Test
- fun overviewTaskIconDrawableSizeGridPx() {
- assertThat(dp.overviewTaskIconDrawableSizeGridPx).isEqualTo(0)
- }
-
- @Test
- fun overviewTaskThumbnailTopMarginPx() {
- assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(280)
- }
-
- @Test
- fun overviewActionsTopMarginPx() {
- assertThat(dp.overviewActionsTopMarginPx).isEqualTo(42)
- }
-
- @Test
- fun overviewActionsHeight() {
- assertThat(dp.overviewActionsHeight).isEqualTo(168)
- }
-
- @Test
- fun overviewActionsButtonSpacing() {
- assertThat(dp.overviewActionsButtonSpacing).isEqualTo(126)
- }
-
- @Test
- fun overviewPageSpacing() {
- assertThat(dp.overviewPageSpacing).isEqualTo(56)
- }
-
- @Test
- fun overviewRowSpacing() {
- assertThat(dp.overviewRowSpacing).isEqualTo(-112)
- }
-
- @Test
- fun overviewGridSideMargin() {
- assertThat(dp.overviewGridSideMargin).isEqualTo(0)
- }
-
- @Test
- fun dropTargetBarTopMarginPx() {
- assertThat(dp.dropTargetBarTopMarginPx).isEqualTo(21)
- }
-
- @Test
- fun dropTargetBarSizePx() {
- assertThat(dp.dropTargetBarSizePx).isEqualTo(126)
- }
-
- @Test
- fun dropTargetBarBottomMarginPx() {
- assertThat(dp.dropTargetBarBottomMarginPx).isEqualTo(21)
- }
-
- @Test
- fun workspaceSpringLoadedMinNextPageVisiblePx() {
- assertThat(dp.workspaceSpringLoadedMinNextPageVisiblePx).isEqualTo(84)
- }
-
- @Test
- fun getWorkspaceSpringLoadScale() {
- assertThat(dp.workspaceSpringLoadScale).isEqualTo(0.8880597f)
- }
-
- @Test
- fun getCellLayoutHeight() {
- assertThat(dp.cellLayoutHeight).isEqualTo(1340)
- }
-
- @Test
- fun getCellLayoutWidth() {
- assertThat(dp.cellLayoutWidth).isEqualTo(2840)
- }
-
- @Test
- fun getPanelCount() {
- assertThat(dp.panelCount).isEqualTo(1)
- }
-
- @Test
- fun isVerticalBarLayout() {
- assertThat(dp.isVerticalBarLayout).isEqualTo(true)
- }
-
- @Test
- fun getCellLayoutSpringLoadShrunkTop() {
- assertThat(dp.cellLayoutSpringLoadShrunkTop).isEqualTo(168)
- }
-
- @Test
- fun getCellLayoutSpringLoadShrunkBottom() {
- assertThat(dp.cellLayoutSpringLoadShrunkBottom).isEqualTo(1358)
- }
-
- @Test
- fun getQsbOffsetY() {
- assertThat(dp.qsbOffsetY).isEqualTo(147)
- }
-
- @Test
- fun getTaskbarOffsetY() {
- assertThat(dp.taskbarOffsetY).isEqualTo(225)
- }
-
- @Test
- fun getHotseatLayoutPaddingLeft() {
- assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(56)
- }
-
- @Test
- fun getHotseatLayoutPaddingTop() {
- assertThat(dp.getHotseatLayoutPadding(context).top).isEqualTo(0)
- }
-
- @Test
- fun getHotseatLayoutPaddingRight() {
- assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(84)
- }
-
- @Test
- fun getHotseatLayoutPaddingBottom() {
- assertThat(dp.getHotseatLayoutPadding(context).bottom).isEqualTo(165)
- }
-
- @Test
- fun hotseatBarEndOffset() {
- assertThat(dp.hotseatBarEndOffset).isEqualTo(0)
- }
-}
\ No newline at end of file
diff --git a/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java b/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
index 5c2e14f..1129a33 100644
--- a/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
+++ b/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
@@ -46,7 +46,8 @@
runWithShellPermission(() ->
usageStatsManager.registerAppUsageLimitObserver(observerId, packages,
Duration.ofSeconds(600), Duration.ofSeconds(300),
- PendingIntent.getActivity(mTargetContext, -1, new Intent(),
+ PendingIntent.getActivity(mTargetContext, -1, new Intent()
+ .setPackage(mTargetContext.getPackageName()),
PendingIntent.FLAG_MUTABLE)));
mLauncher.goHome();
diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
index f7600ff..a4dbf6a 100644
--- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
+++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
@@ -63,6 +63,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.RuleChain;
@@ -178,6 +179,7 @@
// b/143488140
//@NavigationModeSwitch
+ @Ignore
@Test
public void goToOverviewFromApp() {
startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
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..9afd893
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/FullscreenDrawParamsTest.kt
@@ -0,0 +1,258 @@
+/*
+ * 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.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT
+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 kotlin.math.roundToInt
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
+
+/** 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
+ )
+
+ // Probably unhelpful, but also unclear how to test otherwise ¯\_(ツ)_/¯
+ val fullscreenTaskHeight =
+ dp.heightPx * (1 - (splitBounds.topTaskPercent + splitBounds.dividerHeightPercent))
+ val canvasScreenRatio = canvasHeight / fullscreenTaskHeight
+ val expectedBottomHint = dp.taskbarSize * canvasScreenRatio
+ assertThat(params.mCurrentDrawnInsets.bottom).isWithin(1f).of(expectedBottomHint)
+ }
+
+ @Test
+ fun setFullProgress_currentDrawnInsets_clipTaskbarSizeFromTopForTablets_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_TOP_OR_LEFT)
+
+ 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((0f))
+ }
+
+ @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)
+ }
+}
diff --git a/quickstep/tests/src/com/android/quickstep/HotseatWidthCalculationTest.kt b/quickstep/tests/src/com/android/quickstep/HotseatWidthCalculationTest.kt
new file mode 100644
index 0000000..adbca32
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/HotseatWidthCalculationTest.kt
@@ -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
+
+import android.graphics.Rect
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.DeviceProfileBaseTest
+import com.android.launcher3.util.WindowBounds
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class HotseatWidthCalculationTest : DeviceProfileBaseTest() {
+
+ /**
+ * This is a case when after setting the hotseat, the space needs to be recalculated but it
+ * doesn't need to change QSB width or remove icons
+ */
+ @Test
+ fun distribute_border_space_when_space_is_enough_portrait() {
+ initializeVarsForTablet(isGestureMode = false)
+ windowBounds = WindowBounds(Rect(0, 0, 1800, 2560), Rect(0, 104, 0, 0))
+ val dp = newDP()
+ dp.isTaskbarPresentInApps = true
+
+ assertThat(dp.hotseatBarEndOffset).isEqualTo(510)
+ assertThat(dp.numShownHotseatIcons).isEqualTo(6)
+ assertThat(dp.hotseatBorderSpace).isEqualTo(70)
+
+ assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(150)
+ assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(580)
+
+ assertThat(dp.isQsbInline).isFalse()
+ assertThat(dp.hotseatQsbWidth).isEqualTo(1445)
+ }
+
+ /**
+ * This is a case when after setting the hotseat, and recalculating spaces it still needs to
+ * remove icons for everything to fit
+ */
+ @Test
+ fun decrease_num_of_icons_when_not_enough_space_portrait() {
+ initializeVarsForTablet(isGestureMode = false)
+ windowBounds = WindowBounds(Rect(0, 0, 1300, 2560), Rect(0, 104, 0, 0))
+ val dp = newDP()
+ dp.isTaskbarPresentInApps = true
+
+ assertThat(dp.hotseatBarEndOffset).isEqualTo(510)
+ assertThat(dp.numShownHotseatIcons).isEqualTo(4)
+ assertThat(dp.hotseatBorderSpace).isEqualTo(40)
+
+ assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(150)
+ assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(550)
+
+ assertThat(dp.isQsbInline).isFalse()
+ assertThat(dp.hotseatQsbWidth).isEqualTo(1080)
+ }
+
+ /**
+ * This is a case when after setting the hotseat, the space needs to be recalculated but it
+ * doesn't need to change QSB width or remove icons
+ */
+ @Test
+ fun distribute_border_space_when_space_is_enough_landscape() {
+ initializeVarsForTwoPanel(isGestureMode = false, isLandscape = true)
+ val dp = newDP()
+ dp.isTaskbarPresentInApps = true
+
+ assertThat(dp.hotseatBarEndOffset).isEqualTo(705)
+ assertThat(dp.numShownHotseatIcons).isEqualTo(6)
+ assertThat(dp.hotseatBorderSpace).isEqualTo(54)
+
+ assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(231)
+ assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(759)
+
+ assertThat(dp.isQsbInline).isFalse()
+ assertThat(dp.hotseatQsbWidth).isEqualTo(1468)
+ }
+
+ /**
+ * This is a case when the hotseat spans a certain amount of columns and the nav buttons push
+ * the hotseat to the side, but not enough to change the border space.
+ */
+ @Test
+ fun nav_buttons_dont_interfere_with_required_hotseat_width() {
+ initializeVarsForTablet(isGestureMode = false, isLandscape = true)
+ inv?.apply {
+ hotseatColumnSpan = IntArray(4) { 4 }
+ inlineQsb = BooleanArray(4) { false }
+ }
+ val dp = newDP()
+ dp.isTaskbarPresentInApps = true
+
+ assertThat(dp.hotseatBarEndOffset).isEqualTo(660)
+ assertThat(dp.numShownHotseatIcons).isEqualTo(6)
+ assertThat(dp.hotseatBorderSpace).isEqualTo(100)
+
+ assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(300)
+ assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(1040)
+
+ assertThat(dp.isQsbInline).isFalse()
+ assertThat(dp.hotseatQsbWidth).isEqualTo(1233)
+ }
+
+ /** This is a case when after setting the hotseat, the QSB width needs to be changed to fit */
+ @Test
+ fun decrease_qsb_when_not_enough_space_landscape() {
+ initializeVarsForTablet(isGestureMode = false, isLandscape = true)
+ windowBounds = WindowBounds(Rect(0, 0, 2460, 1600), Rect(0, 104, 0, 0))
+ val dp = newDP()
+ dp.isTaskbarPresentInApps = true
+
+ assertThat(dp.hotseatBarEndOffset).isEqualTo(660)
+ assertThat(dp.numShownHotseatIcons).isEqualTo(6)
+ assertThat(dp.hotseatBorderSpace).isEqualTo(36)
+
+ assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(864)
+ assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(696)
+
+ assertThat(dp.isQsbInline).isTrue()
+ assertThat(dp.hotseatQsbWidth).isEqualTo(528)
+ }
+
+ /**
+ * This is a case when after setting the hotseat, changing QSB width, and recalculating spaces
+ * it still needs to remove icons for everything to fit
+ */
+ @Test
+ fun decrease_num_of_icons_when_not_enough_space_landscape() {
+ initializeVarsForTablet(isGestureMode = false, isLandscape = true)
+ windowBounds = WindowBounds(Rect(0, 0, 2260, 1600), Rect(0, 104, 0, 0))
+ val dp = newDP()
+ dp.isTaskbarPresentInApps = true
+
+ assertThat(dp.hotseatBarEndOffset).isEqualTo(660)
+ assertThat(dp.numShownHotseatIcons).isEqualTo(5)
+ assertThat(dp.hotseatBorderSpace).isEqualTo(36)
+
+ assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(816)
+ assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(700)
+
+ assertThat(dp.isQsbInline).isTrue()
+ assertThat(dp.hotseatQsbWidth).isEqualTo(480)
+ }
+}
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/StartLauncherViaGestureTests.java b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
index 401b967..df5303f 100644
--- a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
+++ b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
@@ -24,6 +24,7 @@
import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -56,6 +57,7 @@
eventProcessor.finishIteration();
}
+ @Ignore
@Test
@NavigationModeSwitch
public void testStressPressHome() {
@@ -68,6 +70,7 @@
}
}
+ @Ignore
@Test
@NavigationModeSwitch
public void testStressSwipeToOverview() {
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index 4bd627a..f2ca844 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -21,6 +21,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
import android.content.Intent;
@@ -38,12 +39,14 @@
import com.android.launcher3.tapl.OverviewActions;
import com.android.launcher3.tapl.OverviewTask;
import com.android.launcher3.ui.TaplTestsLauncher3;
+import com.android.launcher3.util.Wait;
import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
import com.android.quickstep.views.RecentsView;
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -181,6 +184,21 @@
@Test
@PortraitLandscape
+ public void testSplitFromOverview() {
+ assumeTrue(!mLauncher.isTablet());
+
+ startTestActivity(2);
+ startTestActivity(3);
+
+ mLauncher.goHome().switchToOverview().getCurrentTask()
+ .tapMenu()
+ .tapSplitMenuItem()
+ .getTestActivityTask(2)
+ .open();
+ }
+
+ @Test
+ @PortraitLandscape
public void testSplitFromOverviewForTablet() {
assumeTrue(mLauncher.isTablet());
@@ -209,17 +227,20 @@
return launcher.<RecentsView>getOverviewPanel().getBottomRowTaskCountForTablet();
}
+ @Ignore
@Test
@NavigationModeSwitch
@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",
isInState(() -> LauncherState.OVERVIEW));
}
+ @Ignore
@Test
@NavigationModeSwitch
@PortraitLandscape
@@ -243,6 +264,19 @@
return launchedAppState;
}
+ private void quickSwitchToPreviousAppAndAssert(boolean toRight) {
+ final LaunchedAppState launchedAppState = getAndAssertLaunchedApp();
+ if (toRight) {
+ launchedAppState.quickSwitchToPreviousApp();
+ } else {
+ launchedAppState.quickSwitchToPreviousAppSwipeLeft();
+ }
+
+ // While enable shell transition, Launcher can be resumed due to transient launch.
+ waitForLauncherCondition("Launcher shouldn't stay in resume forever",
+ this::isInLaunchedApp, 3000 /* timeout */);
+ }
+
@Test
@PortraitLandscape
public void testAllAppsFromHome() throws Exception {
@@ -269,13 +303,11 @@
startTestActivity(3);
startTestActivity(4);
- LaunchedAppState launchedAppState = getAndAssertLaunchedApp();
- launchedAppState.quickSwitchToPreviousApp();
+ quickSwitchToPreviousAppAndAssert(true /* toRight */);
assertTrue("The first app we should have quick switched to is not running",
isTestActivityRunning(3));
- launchedAppState = getAndAssertLaunchedApp();
- launchedAppState.quickSwitchToPreviousApp();
+ quickSwitchToPreviousAppAndAssert(true /* toRight */);
if (mLauncher.getNavigationModel() == NavigationModel.THREE_BUTTON) {
// 3-button mode toggles between 2 apps, rather than going back further.
assertTrue("Second quick switch should have returned to the first app.",
@@ -284,15 +316,39 @@
assertTrue("The second app we should have quick switched to is not running",
isTestActivityRunning(2));
}
- launchedAppState = getAndAssertLaunchedApp();
- launchedAppState.quickSwitchToPreviousAppSwipeLeft();
+
+ quickSwitchToPreviousAppAndAssert(false /* toRight */);
assertTrue("The 2nd app we should have quick switched to is not running",
isTestActivityRunning(3));
- launchedAppState = getAndAssertLaunchedApp();
+ final LaunchedAppState launchedAppState = getAndAssertLaunchedApp();
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.
+ quickSwitchToPreviousAppAndAssert(true /* toRight */);
+
+ assertTrue("The first app we should have quick switched to is not running",
+ isTestActivityRunning(2));
+ // 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)),
@@ -325,6 +381,7 @@
waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL);
}
+ @Ignore
@Test
@PortraitLandscape
public void testOverviewForTablet() throws Exception {
@@ -389,4 +446,23 @@
launcher -> assertEquals("Still have tasks after dismissing all",
0, getTaskCount(launcher)));
}
+
+ @Test
+ @ScreenRecord // b/242163205
+ public void testDisableRotationCheckForPhone() throws Exception {
+ assumeFalse(mLauncher.isTablet());
+ try {
+ mLauncher.setExpectedRotationCheckEnabled(false);
+ mLauncher.setEnableRotation(false);
+ mLauncher.getDevice().setOrientationLeft();
+ startTestActivity(7);
+ Wait.atMost("Device should not be in natural orientation",
+ () -> !mDevice.isNaturalOrientation(), DEFAULT_UI_TIMEOUT, mLauncher);
+ mLauncher.goHome();
+ } finally {
+ mLauncher.setExpectedRotationCheckEnabled(true);
+ mLauncher.setEnableRotation(true);
+ mLauncher.getDevice().setOrientationNatural();
+ }
+ }
}
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/all_apps_tab_text.xml b/res/color-night-v31/all_apps_tab_text.xml
index 83237b4..54b95ae 100644
--- a/res/color-night-v31/all_apps_tab_text.xml
+++ b/res/color-night-v31/all_apps_tab_text.xml
@@ -14,6 +14,6 @@
limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:color="@android:color/system_neutral1_50" android:state_selected="true"/>
- <item android:color="@android:color/system_neutral2_700"/>
+ <item android:color="@android:color/system_neutral2_700" android:state_selected="true"/>
+ <item android:color="@android:color/system_accent2_100"/>
</selector>
\ No newline at end of file
diff --git a/res/drawable/ic_setup_shadow.xml b/res/color-night-v31/all_apps_tabs_background.xml
similarity index 63%
copy from res/drawable/ic_setup_shadow.xml
copy to res/color-night-v31/all_apps_tabs_background.xml
index 10aeee6..9213274 100644
--- a/res/drawable/ic_setup_shadow.xml
+++ b/res/color-night-v31/all_apps_tabs_background.xml
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- 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
+ 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,
@@ -13,7 +13,6 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<com.android.launcher3.graphics.ShadowDrawable
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_setting"
- android:elevation="@dimen/drop_target_shadow_elevation" />
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@android:color/system_neutral1_800"/>
+</selector>
\ No newline at end of file
diff --git a/res/drawable/ic_setup_shadow.xml b/res/color-night-v31/transient_taskbar_background.xml
similarity index 63%
copy from res/drawable/ic_setup_shadow.xml
copy to res/color-night-v31/transient_taskbar_background.xml
index 10aeee6..40f6494 100644
--- a/res/drawable/ic_setup_shadow.xml
+++ b/res/color-night-v31/transient_taskbar_background.xml
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
+ http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
@@ -13,7 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<com.android.launcher3.graphics.ShadowDrawable
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_setting"
- android:elevation="@dimen/drop_target_shadow_elevation" />
+<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-night-v31/widgets_picker_scrim.xml b/res/color-night-v31/widgets_picker_scrim.xml
deleted file mode 100644
index be7010b..0000000
--- a/res/color-night-v31/widgets_picker_scrim.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 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.
-*/
--->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:color="@android:color/system_neutral1_900" android:alpha="0.8" />
-</selector>
diff --git a/res/drawable/ic_setup_shadow.xml b/res/color-v31/transient_taskbar_background.xml
similarity index 63%
rename from res/drawable/ic_setup_shadow.xml
rename to res/color-v31/transient_taskbar_background.xml
index 10aeee6..bce947d 100644
--- a/res/drawable/ic_setup_shadow.xml
+++ b/res/color-v31/transient_taskbar_background.xml
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
+ http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
@@ -13,7 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<com.android.launcher3.graphics.ShadowDrawable
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_setting"
- android:elevation="@dimen/drop_target_shadow_elevation" />
+<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/color-v31/widgets_picker_scrim.xml b/res/color-v31/widgets_picker_scrim.xml
deleted file mode 100644
index 648824a..0000000
--- a/res/color-v31/widgets_picker_scrim.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 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.
-*/
--->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:color="@android:color/system_neutral1_200" android:alpha="0.8" />
-</selector>
diff --git a/res/color/widgets_picker_scrim.xml b/res/color/widgets_picker_scrim.xml
index 1cf97f6..5d51300 100644
--- a/res/color/widgets_picker_scrim.xml
+++ b/res/color/widgets_picker_scrim.xml
@@ -18,5 +18,5 @@
*/
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:color="#000000" android:alpha="0.32" />
+ <item android:color="#000000" android:alpha="0.65" />
</selector>
diff --git a/res/drawable/bg_all_apps_searchbox.xml b/res/drawable/bg_all_apps_searchbox.xml
index c324927..3c321e4 100644
--- a/res/drawable/bg_all_apps_searchbox.xml
+++ b/res/drawable/bg_all_apps_searchbox.xml
@@ -15,5 +15,6 @@
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<solid android:color="?attr/popupColorPrimary" />
- <corners android:radius="2dp" />
+ <corners android:radius="@dimen/rounded_button_radius" />
+ <stroke android:color="?attr/allappsHeaderProtectionColor" android:width=".5dp" />
</shape>
\ No newline at end of file
diff --git a/res/drawable/ic_all_apps_bg_hand.xml b/res/drawable/ic_all_apps_bg_hand.xml
deleted file mode 100644
index 7f3fe14..0000000
--- a/res/drawable/ic_all_apps_bg_hand.xml
+++ /dev/null
@@ -1,100 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<vector
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="208dp"
- android:height="212dp"
- android:viewportWidth="208.0"
- android:viewportHeight="212.0">
- <path
- android:fillColor="#1A000000"
- android:pathData="M89.4,61.8H85l-1.6-1.5c5.5-6.4,8.8-14.7,8.8-23.7C92.2,16.6,76,0.3,55.9,0.3
- S19.5,16.6,19.5,36.6S35.8,73,55.9,73c9,0,17.3-3.3,23.7-8.8l1.5,1.6v4.4l40.5,40.4l8.3-8.3L89.4,61.8z M54,66.6
- c-13.9,0-28.8-16-28.8-30S41.5,8.9,55.4,8.9S81,22.7,81,36.6S67.9,66.6,54,66.6z"/>
- <path
- android:fillColor="#1A000000"
- android:pathData="M33.4,29.2l-0.3-1.8l-4.2-3.1L18.1,26l-3.1,4.2l0.3,1.8L4.5,33.7L9,62.5
- c0.2,1.5,1.6,2.5,3.1,2.3l34.2-5.3c1.5-0.2,2.5-1.6,2.3-3.1l-4.4-28.8L33.4,29.2z"/>
- <path
- android:fillColor="#3367D6"
- android:pathData="M30.2,27.9l-0.3-1.8l-4.1-3L15,24.7l-3,4.1l0.3,1.8L1.6,32.3L6,60.9
- c0.2,1.5,1.6,2.5,3.1,2.3L43,57.9c1.5-0.2,2.5-1.6,2.3-3.1l-4.4-28.6L30.2,27.9z M26.6,28.4l-10.7,1.6l-0.3-1.8l10.7-1.6L26.6,28.4z"/>
- <group>
- <clip-path
- android:pathData="M25.1,37.7a28.9,28.9 0 1,0 57.8,0a28.9,28.9 0 1,0 -57.8,0"/>
- <path
- android:fillColor="#4285F4"
- android:pathData="M41.7,23l-0.3-2.1l-4.9-3.6l-12.6,1.9l-3.6,4.9l0.3,2.1L8.1,28.1l5.2,33.7
- c0.3,1.7,1.9,2.9,3.6,2.7l40-6.1c1.7-0.3,2.9-1.9,2.7-3.6L54.4,21L41.7,23z M37.5,23.6l-12.6,1.9l-0.3-2.1l12.6-1.9L37.5,23.6z"/>
- </group>
- <path
- android:fillColor="?android:attr/colorControlHighlight"
- android:pathData="M87.5,62.9h-4.4l-1.6-1.5c5.5-6.4,8.8-14.7,8.8-23.7C90.3,17.7,74,1.4,54,1.4
- S17.6,17.7,17.6,37.7S33.9,74.1,54,74.1c9,0,17.3-3.3,23.7-8.8l1.5,1.6v4.4l40.5,40.4l8.3-8.3L87.5,62.9z M54,64.2
- c-14.7,0-26.5-11.8-26.5-26.5S39.3,11.2,54,11.2s26.5,11.8,26.5,26.5S68.6,64.2,54,64.2z"/>
- <path
- android:fillColor="#1A000000"
- android:pathData="M153.4,112.9c-14.9-17.2-38.6-9.1-38.6-9.1l-10.2-11.3c0,0-4.8-5.9-9-3.7
- c-7,3.6-0.6,10.7-0.6,10.7s12.3,15.1,15.4,20.1c2.1,3.4,8.4,4.5,10.1,4.9l17.1,3.7l-0.9-0.7l-1-0.7L153.4,112.9z"/>
- <path
- android:fillColor="#FFDBA6"
- android:pathData="M152.1,113.9c-14.9-17.2-37.6-8-37.6-8l-11.1-12.3c0,0-4.8-5.9-9-3.7
- c-7,3.6-0.6,10.7-0.6,10.7s12.3,15.1,15.4,20.1c2.1,3.4,8.4,4.5,10.1,4.9l19,4.1"/>
- <path
- android:fillColor="#1A000000"
- android:pathData="M148.6,77.9c0.6,0.7,2,2.5,2.1,2.6c1.1,1.7,6.2,13.6,11.8,35.1c0,0.1,1.9,3,1.9,3.1
- c0,0,0.1,0.1,0.1,0.2c0,0,0,0,0-0.1c0.9,1.3,4.4,6.6,8.9,13.7c0.1,0.2,0.3,0.5,0.4,0.7c0,0.1,0.1,0.1,0.1,0.2
- c0.2,0.3,0.4,0.6,0.6,0.9c0.1-0.1,0.2-0.2,0.3-0.3c0.2-0.3,0.6-0.3,0.8,0c2.9,4.8,21.2,35,26.7,49c2.1,5.3,3.2,8.4,3.6,11.6
- c0.3,2.3,0,4.4-1.2,6c1.5-1.9,3.5-6.8-1.5-19c-1.2-2.9-2.8-6.5-4.8-10.5c-7.5-15.2-20-35.6-22.4-39.6c-0.2-0.3-0.6-0.3-0.8,0
- c-0.2,0.2-0.3,0.4-0.5,0.6c-4.5-7.1-8.2-12.6-8.8-13.5c-0.1-0.1-1.9-3-1.9-3.1c-5.7-21.6-10.7-33.4-11.8-35.1
- c-0.1-0.1-1.5-1.9-2.1-2.6l-6.5-8.3c-1.9-2.3-4.2-4.1-6.7-2.3c-2.5,1.8-1.6,4.5-0.1,7.1l3.3,5.2l7-2
- C147.7,77.4,148.1,77.3,148.6,77.9z"/>
- <path
- android:fillColor="#FFDBA6"
- android:pathData="M148.6,77.9l-6.5-8.3c-1.9-2.3-4.2-4.1-6.7-2.3l0,0l0,0c-2.5,1.8-1.6,4.5-0.1,7.1l3.8,6L148.6,77.9C148.6,77.9,148.6,77.9,148.6,77.9z"/>
- <path
- android:fillColor="#1A000000"
- android:pathData="M151.1,92.5l-19.7-25.3c-1.9-2.3-4.2-4.1-6.7-2.3l0,0l0,0c-2.5,1.8-1.6,4.5-0.1,7.1l17.1,27.2L151.1,92.5z"/>
- <path
- android:fillColor="#FFDBA6"
- android:pathData="M149.7,92.9l-19.7-25.3c-1.9-2.3-4.2-4.1-6.7-2.3l0,0l0,0c-2.5,1.8-1.6,4.5-0.1,7.1l17.1,27.2L149.7,92.9z"/>
- <path
- android:fillColor="#1A000000"
- android:pathData="M141.6,94.6l-20.8-26.7c-2.1-2.5-4.4-4.3-7.1-2.5l0,0l0,0c-2.6,1.9-1.7,4.7-0.1,7.5l18,28.6L141.6,94.6z"/>
- <path
- android:fillColor="#FFDBA6"
- android:pathData="M140.1,95l-20.8-26.7c-2.1-2.5-4.4-4.3-7.1-2.5l0,0h0c-2.6,1.9-1.7,4.7-0.1,7.5l18,28.6L140.1,95z"/>
- <path
- android:fillColor="#1A000000"
- android:pathData="M140.4,99.1c-0.5-0.6-2.1-7.5-2.8-7.3l-15.9-6.9c-0.3,0-0.5-0.1-0.7-0.3l-2.3-3.5
- l-0.4-0.6L100,54.5c-1.5-2-3.3-4-5.3-4.3c-0.7-0.1-1.3-0.2-2,0.2v0c-1,0.6-1.2,1.5-1.3,2.4c-0.2,1.8,0.6,3.9,1.6,5.9L108.5,87
- l0.2,0.4l6.6,11.7l0,0c2.5,4.5,4.4,10.5,4.4,10.7L140.4,99.1"/>
- <path
- android:fillColor="#FFDBA6"
- android:pathData="M129.7,125.1c3,0.7,8.1,4,11.8,9.1c2.7,3.7,5.5,8.3,8.2,13
- c7.6-2.3,19.9-6.8,24.9-12.9c-0.2-0.3-0.4-0.6-0.6-0.9c0-0.1-0.1-0.1-0.1-0.2c-0.1-0.2-0.3-0.5-0.4-0.7c-4.5-7.1-8-12.4-8.9-13.7
- c0,0,0,0,0,0.1c0-0.1-0.1-0.1-0.1-0.2c-0.1-0.1-1.9-3-1.9-3.1c-5.7-21.5-10.7-33.4-11.8-35.1c-0.1-0.1-1.5-1.9-2.1-2.6
- c-0.5-0.6-0.9-0.5-1.6-0.3l-26.8,7.7c-0.3,0-0.5-0.1-0.7-0.3l-2.3-3.5l-0.4-0.6L98.5,54.8c-1.5-2-3.3-4-5.3-4.3
- c-0.7-0.1-1.3-0.2-2,0.2c-1,0.6-1.2,1.5-1.3,2.4c-0.2,1.8,0.6,3.9,1.6,5.9L107,87.3l0.2,0.4l6.6,11.7l0,0
- c2.5,4.5,4.4,10.5,4.4,10.7L129.7,125.1"/>
- <path
- android:fillColor="?android:attr/colorForeground"
- android:pathData="M202.3,183.1c-5.4-14.1-23.8-44.3-26.7-49c-0.2-0.3-0.6-0.3-0.8,0
- c-5.1,6.6-19,11.4-26.5,13.6c-0.3,0.1-1,0.1-1.2,0.6c-0.2,0.4,0.1,1,0.2,1.1c7.8,12.9,14.7,27.9,15.3,29.3c0,0.1,0.1,0.1,0.1,0.2
- l9.6,22.9c0,0,0,0,0,0l1.7,4.1c1.4,2.7,3,4.3,5.3,5.1c1.5,0.5,2.1,0.6,3.2,0.6c4.8,0.1,15.2-6.1,20.5-9.4c2.7-1.7,3.3-4.4,2.9-7.6
- C205.5,191.5,204.4,188.4,202.3,183.1z"/>
-</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_all_apps_bg_icon_1.xml b/res/drawable/ic_all_apps_bg_icon_1.xml
deleted file mode 100644
index d226ac6..0000000
--- a/res/drawable/ic_all_apps_bg_icon_1.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
-
- <path
- android:fillColor="#1A000000"
- android:pathData="M44.28,30.96c4.84-10.68,0.09-23.27-10.59-28.11S10.42,2.74,5.58,13.42
- C1,23.54,6.5,35.92,16.62,40.51l0,0l-3.23,7.12C27.84,47,39.79,40.86,44.28,30.96z" />
- <path
- android:fillColor="?android:attr/colorPrimary"
- android:pathData="M41.75,30.05c4.84-10.68,0.09-23.27-10.59-28.11S7.9,1.83,3.06,12.51
- c-4.59,10.12,0.92,22.5,11.03,27.09l0,0l-3.23,7.12C25.31,46.09,37.26,39.94,41.75,30.05z" />
-</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_all_apps_bg_icon_2.xml b/res/drawable/ic_all_apps_bg_icon_2.xml
deleted file mode 100644
index 5966d99..0000000
--- a/res/drawable/ic_all_apps_bg_icon_2.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
-
- <path
- android:fillColor="#1A000000"
- android:pathData="M20.54,44.59c0.57-0.04,1.15-0.38,1.67-1.04l24.23-30.62c0.62-0.78,0.77-1.54,0.52-2.12
- c-0.25-0.58-0.9-0.99-1.89-1.1L6.2,5.99C5.39,5.91,4.74,6.08,4.32,6.44l0,0C3.7,6.97,3.55,7.88,4.01,8.96l14.54,34.09
- C19,44.13,19.75,44.65,20.54,44.59L20.54,44.59z" />
- <path
- android:fillColor="?android:attr/colorPrimary"
- android:pathData="M18.49,43.22c0.57-0.04,1.15-0.38,1.67-1.04l24.23-30.62c0.62-0.78,0.77-1.54,0.52-2.12
- c-0.25-0.58-0.9-0.99-1.89-1.1L4.15,4.62C3.34,4.54,2.69,4.71,2.27,5.08l0,0C1.65,5.6,1.5,6.52,1.96,7.6L16.5,41.69
- C16.96,42.76,17.7,43.28,18.49,43.22L18.49,43.22z" />
-</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_all_apps_bg_icon_3.xml b/res/drawable/ic_all_apps_bg_icon_3.xml
deleted file mode 100644
index b18f8bc..0000000
--- a/res/drawable/ic_all_apps_bg_icon_3.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
-
- <path
- android:fillColor="#1A000000"
- android:pathData="M25.18,1.27c-12.32,0-23.41,9.99-23.41,22.31s11.09,22.31,23.41,22.31
- s22.31-9.99,22.31-22.31S37.5,1.27,25.18,1.27z M25.18,33.55c-5.5,0-14.35-5.1-14.35-10.6s8.32-12.19,13.82-12.19
- c5.5,0,10.49,7.33,10.49,12.83S30.68,33.55,25.18,33.55z" />
- <path
- android:fillColor="?android:attr/colorPrimary"
- android:pathData="M22.93,0.22c-12.32,0-22.31,9.99-22.31,22.31s9.99,22.31,22.31,22.31
- s22.31-9.99,22.31-22.31S35.25,0.22,22.93,0.22z M22.93,32.5c-5.5,0-9.97-4.46-9.97-9.97s4.46-9.97,9.97-9.97
- c5.5,0,9.97,4.46,9.97,9.97S28.43,32.5,22.93,32.5z" />
- <path
- android:fillColor="?android:attr/colorPrimary"
- android:pathData="M14.81,22.53a8.12,8.12 0 1,0 16.24,0a8.12,8.12 0 1,0 -16.24,0z" />
-</vector>
diff --git a/res/drawable/ic_all_apps_bg_icon_4.xml b/res/drawable/ic_all_apps_bg_icon_4.xml
deleted file mode 100644
index 8eb4d90..0000000
--- a/res/drawable/ic_all_apps_bg_icon_4.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
-
- <path
- android:fillColor="#1A000000"
- android:pathData="M11.53,8.02l23.39-5.73c1.61-0.39,3.25,0.6,3.64,2.21l7.64,31.19
- c0.39,1.61-0.6,3.25-2.21,3.64L12.8,46.97c-1.61,0.39-3.25-0.6-3.64-2.21L3.43,21.37L11.53,8.02z" />
- <path
- android:fillColor="?android:attr/colorPrimary"
- android:pathData="M9.2,6.53L32.59,0.8C34.2,0.4,35.84,1.4,36.23,3l7.64,31.19c0.39,1.61-0.6,3.25-2.21,3.64
- l-31.19,7.64c-1.61,0.39-3.25-0.6-3.64-2.21L1.11,19.87L9.2,6.53z" />
- <path
- android:fillColor="#1A000000"
- android:pathData="M9.27,6.47l1.91,7.8c0.4,1.62-0.59,3.24-2.21,3.64l-7.8,1.91L9.27,6.47z" />
-</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_all_apps_button.xml b/res/drawable/ic_all_apps_button.xml
index 5770d3c..4f0b6a8 100644
--- a/res/drawable/ic_all_apps_button.xml
+++ b/res/drawable/ic_all_apps_button.xml
@@ -15,30 +15,41 @@
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- 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:width="29dp"
+ android:height="28dp"
+ android:viewportWidth="29"
+ android:viewportHeight="28">
+ <group
+ android:pivotY="14.5"
+ android:pivotX="22"
+ android:scaleX=".50"
+ android:scaleY=".50">
+ <path
+ android:pathData="M4 7C3.0375 7 2.215 6.65 1.5325 5.9675C0.85 5.285 0.5 4.4625 0.5 3.5C0.5 2.5375 0.85 1.715 1.5325 1.0325C2.215 0.35 3.0375 0 4 0C4.9625 0 5.785 0.35 6.4675 1.0325C7.15 1.715 7.5 2.5375 7.5 3.5C7.5 4.4625 7.15 5.285 6.4675 5.9675C5.785 6.65 4.9625 7 4 7Z"
+ android:fillColor="@color/all_apps_button_color"/>
+ <path
+ android:pathData="M14.5 7C13.5375 7 12.715 6.65 12.0325 5.9675C11.35 5.285 11 4.4625 11 3.5C11 2.5375 11.35 1.715 12.0325 1.0325C12.715 0.35 13.5375 0 14.5 0C15.4625 0 16.285 0.35 16.9675 1.0325C17.65 1.715 18 2.5375 18 3.5C18 4.4625 17.65 5.285 16.9675 5.9675C16.285 6.65 15.4625 7 14.5 7Z"
+ android:fillColor="@color/all_apps_button_color"/>
+ <path
+ android:pathData="M25 7C24.0375 7 23.215 6.65 22.5325 5.9675C21.85 5.285 21.5 4.4625 21.5 3.5C21.5 2.5375 21.85 1.715 22.5325 1.0325C23.215 0.35 24.0375 0 25 0C25.9625 0 26.785 0.35 27.4675 1.0325C28.15 1.715 28.5 2.5375 28.5 3.5C28.5 4.4625 28.15 5.285 27.4675 5.9675C26.785 6.65 25.9625 7 25 7Z"
+ android:fillColor="@color/all_apps_button_color"/>
+ <path
+ android:pathData="M4 17.5C3.0375 17.5 2.215 17.15 1.5325 16.4675C0.85 15.785 0.5 14.9625 0.5 14C0.5 13.0375 0.85 12.215 1.5325 11.5325C2.215 10.85 3.0375 10.5 4 10.5C4.9625 10.5 5.785 10.85 6.4675 11.5325C7.15 12.215 7.5 13.0375 7.5 14C7.5 14.9625 7.15 15.785 6.4675 16.4675C5.785 17.15 4.9625 17.5 4 17.5Z"
+ android:fillColor="@color/all_apps_button_color"/>
+ <path
+ android:pathData="M14.5 17.5C13.5375 17.5 12.715 17.15 12.0325 16.4675C11.35 15.785 11 14.9625 11 14C11 13.0375 11.35 12.215 12.0325 11.5325C12.715 10.85 13.5375 10.5 14.5 10.5C15.4625 10.5 16.285 10.85 16.9675 11.5325C17.65 12.215 18 13.0375 18 14C18 14.9625 17.65 15.785 16.9675 16.4675C16.285 17.15 15.4625 17.5 14.5 17.5Z"
+ android:fillColor="@color/all_apps_button_color"/>
+ <path
+ android:pathData="M25 17.5C24.0375 17.5 23.215 17.15 22.5325 16.4675C21.85 15.785 21.5 14.9625 21.5 14C21.5 13.0375 21.85 12.215 22.5325 11.5325C23.215 10.85 24.0375 10.5 25 10.5C25.9625 10.5 26.785 10.85 27.4675 11.5325C28.15 12.215 28.5 13.0375 28.5 14C28.5 14.9625 28.15 15.785 27.4675 16.4675C26.785 17.15 25.9625 17.5 25 17.5Z"
+ android:fillColor="@color/all_apps_button_color"/>
+ <path
+ android:pathData="M4 28C3.0375 28 2.215 27.65 1.5325 26.9675C0.85 26.285 0.5 25.4625 0.5 24.5C0.5 23.5375 0.85 22.715 1.5325 22.0325C2.215 21.35 3.0375 21 4 21C4.9625 21 5.785 21.35 6.4675 22.0325C7.15 22.715 7.5 23.5375 7.5 24.5C7.5 25.4625 7.15 26.285 6.4675 26.9675C5.785 27.65 4.9625 28 4 28Z"
+ android:fillColor="@color/all_apps_button_color"/>
+ <path
+ android:pathData="M14.5 28C13.5375 28 12.715 27.65 12.0325 26.9675C11.35 26.285 11 25.4625 11 24.5C11 23.5375 11.35 22.715 12.0325 22.0325C12.715 21.35 13.5375 21 14.5 21C15.4625 21 16.285 21.35 16.9675 22.0325C17.65 22.715 18 23.5375 18 24.5C18 25.4625 17.65 26.285 16.9675 26.9675C16.285 27.65 15.4625 28 14.5 28Z"
+ android:fillColor="@color/all_apps_button_color"/>
+ <path
+ android:pathData="M25 28C24.0375 28 23.215 27.65 22.5325 26.9675C21.85 26.285 21.5 25.4625 21.5 24.5C21.5 23.5375 21.85 22.715 22.5325 22.0325C23.215 21.35 24.0375 21 25 21C25.9625 21 26.785 21.35 27.4675 22.0325C28.15 22.715 28.5 23.5375 28.5 24.5C28.5 25.4625 28.15 26.285 27.4675 26.9675C26.785 27.65 25.9625 28 25 28Z"
+ android:fillColor="@color/all_apps_button_color"/>
+ </group>
</vector>
diff --git a/res/drawable/ic_block_shadow.xml b/res/drawable/ic_block_shadow.xml
deleted file mode 100644
index 045fe8d..0000000
--- a/res/drawable/ic_block_shadow.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<com.android.launcher3.graphics.ShadowDrawable
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_block_no_shadow"
- android:elevation="@dimen/drop_target_shadow_elevation" />
diff --git a/res/drawable/ic_remove_shadow.xml b/res/drawable/ic_remove_shadow.xml
deleted file mode 100644
index 48abc10..0000000
--- a/res/drawable/ic_remove_shadow.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<com.android.launcher3.graphics.ShadowDrawable
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_remove_no_shadow"
- android:elevation="@dimen/drop_target_shadow_elevation" />
diff --git a/res/drawable/ic_select_windows.xml b/res/drawable/ic_select_windows.xml
new file mode 100644
index 0000000..cba0fde
--- /dev/null
+++ b/res/drawable/ic_select_windows.xml
@@ -0,0 +1,26 @@
+<!--
+ ~ 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.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48"
+ android:viewportHeight="48"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M7,44Q5.8,44 4.9,43.1Q4,42.2 4,41V21.65Q4,20.45 4.9,19.55Q5.8,18.65 7,18.65H12.75V7Q12.75,5.8 13.65,4.9Q14.55,4 15.75,4H41Q42.2,4 43.1,4.9Q44,5.8 44,7V26.35Q44,27.55 43.1,28.45Q42.2,29.35 41,29.35H35.3V41Q35.3,42.2 34.4,43.1Q33.5,44 32.3,44ZM7,41H32.3Q32.3,41 32.3,41Q32.3,41 32.3,41V24.65H7V41Q7,41 7,41Q7,41 7,41ZM35.3,26.35H41Q41,26.35 41,26.35Q41,26.35 41,26.35V10H15.75V18.65H31.6Q33.2,18.65 34.25,19.7Q35.3,20.75 35.3,22.35Z"/>
+</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_uninstall_shadow.xml b/res/drawable/ic_uninstall_shadow.xml
deleted file mode 100644
index b441b0e..0000000
--- a/res/drawable/ic_uninstall_shadow.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<com.android.launcher3.graphics.ShadowDrawable
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_uninstall_no_shadow"
- android:elevation="@dimen/drop_target_shadow_elevation" />
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.xml b/res/layout/all_apps.xml
index d0d82d4..655c75d 100644
--- a/res/layout/all_apps.xml
+++ b/res/layout/all_apps.xml
@@ -17,42 +17,9 @@
will bake the left/right padding into that view's background itself. -->
<com.android.launcher3.allapps.LauncherAllAppsContainerView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/apps_view"
- android:theme="?attr/allAppsTheme"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="true"
android:clipToPadding="false"
android:focusable="false"
- android:saveEnabled="false">
-
- <include
- layout="@layout/all_apps_bottom_sheet_background"
- android:visibility="gone" />
-
- <include
- layout="@layout/search_results_rv_layout"
- android:visibility="gone" />
-
- <include
- layout="@layout/all_apps_rv_layout"
- android:visibility="gone" />
-
- <com.android.launcher3.allapps.FloatingHeaderView
- android:id="@+id/all_apps_header"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:clipToPadding="false"
- android:paddingTop="@dimen/all_apps_header_top_padding"
- android:paddingBottom="@dimen/all_apps_header_bottom_padding"
- android:orientation="vertical" >
-
- <include layout="@layout/floating_header_content" />
-
- <include layout="@layout/all_apps_personal_work_tabs" />
-
- </com.android.launcher3.allapps.FloatingHeaderView>
-
- <include layout="@layout/search_container_all_apps" />
-
- <include layout="@layout/all_apps_fast_scroller" />
-</com.android.launcher3.allapps.LauncherAllAppsContainerView>
\ No newline at end of file
+ android:saveEnabled="false" />
\ No newline at end of file
diff --git a/res/layout/all_apps_bottom_sheet_background.xml b/res/layout/all_apps_bottom_sheet_background.xml
index 3e47690..b0157c9 100644
--- a/res/layout/all_apps_bottom_sheet_background.xml
+++ b/res/layout/all_apps_bottom_sheet_background.xml
@@ -16,8 +16,7 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/bottom_sheet_background"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@drawable/bg_rounded_corner_bottom_sheet">
+ android:layout_height="match_parent">
<View
android:id="@+id/bottom_sheet_handle_area"
diff --git a/res/layout/all_apps_content.xml b/res/layout/all_apps_content.xml
new file mode 100644
index 0000000..925f4d9
--- /dev/null
+++ b/res/layout/all_apps_content.xml
@@ -0,0 +1,49 @@
+<?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.
+-->
+<!-- This file is used by multiple all_apps.xml. Layout consists of all contents
+ showed in all apps screen
+-->
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <include
+ layout="@layout/all_apps_bottom_sheet_background"
+ android:visibility="gone" />
+
+ <include
+ layout="@layout/search_results_rv_layout"
+ android:visibility="gone" />
+
+ <include
+ layout="@layout/all_apps_rv_layout"
+ android:visibility="gone" />
+
+ <com.android.launcher3.allapps.FloatingHeaderView
+ android:id="@+id/all_apps_header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipToPadding="false"
+ android:layout_below="@id/search_container_all_apps"
+ android:paddingTop="@dimen/all_apps_header_top_padding"
+ android:paddingBottom="@dimen/all_apps_header_bottom_padding"
+ android:orientation="vertical" >
+
+ <include layout="@layout/floating_header_content" />
+
+ <include layout="@layout/all_apps_personal_work_tabs" />
+
+ </com.android.launcher3.allapps.FloatingHeaderView>
+
+ <include layout="@layout/all_apps_fast_scroller" />
+</merge>
\ 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..e04b207 100644
--- a/res/layout/all_apps_personal_work_tabs.xml
+++ b/res/layout/all_apps_personal_work_tabs.xml
@@ -16,19 +16,23 @@
<com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:launcher="http://schemas.android.com/apk/res-auto"
android:id="@+id/tabs"
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">
+ style="@style/TextHeadline"
+ launcher:alignOnIcon="true">
<Button
android:id="@+id/tab_personal"
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 +45,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/all_apps_search_market.xml b/res/layout/all_apps_search_market.xml
deleted file mode 100644
index 6f2dd3d..0000000
--- a/res/layout/all_apps_search_market.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 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.
--->
-<TextView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/search_market_text"
- android:layout_width="match_parent"
- android:layout_height="48dp"
- android:gravity="center"
- android:paddingLeft="@dimen/dynamic_grid_edge_margin"
- android:paddingRight="@dimen/dynamic_grid_edge_margin"
- android:fontFamily="sans-serif-medium"
- android:textSize="14sp"
- android:textColor="?android:attr/colorAccent"
- android:text="@string/all_apps_search_market_message"
- android:textAllCaps="true"
- android:focusable="true"
- android:background="?android:selectableItemBackground" />
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..db218c3 100644
--- a/res/layout/search_container_all_apps.xml
+++ b/res/layout/search_container_all_apps.xml
@@ -21,11 +21,11 @@
android:layout_centerHorizontal="true"
android:layout_gravity="top|center_horizontal"
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/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 bdc51be..f48f3c0 100644
--- a/res/layout/secondary_launcher.xml
+++ b/res/layout/secondary_launcher.xml
@@ -42,8 +42,7 @@
android:contentDescription="@string/all_apps_button_label"
android:onClick="onAppsButtonClicked" />
- <view
- class="com.android.launcher3.allapps.SecondaryLauncherAllAppsContainerView"
+ <com.android.launcher3.allapps.SecondaryLauncherAllAppsContainerView
android:id="@+id/apps_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -53,56 +52,5 @@
android:saveEnabled="false"
android:layout_gravity="bottom|end"
android:background="@drawable/round_rect_primary"
- android:visibility="invisible" >
-
- <include
- layout="@layout/all_apps_bottom_sheet_background"
- android:visibility="gone" />
-
- <include
- layout="@layout/search_results_rv_layout"
- android:visibility="gone" />
-
- <include
- layout="@layout/all_apps_rv_layout"
- android:visibility="gone" />
-
- <com.android.launcher3.allapps.FloatingHeaderView
- android:id="@+id/all_apps_header"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_below="@id/search_container_all_apps"
- android:clipToPadding="false"
- android:paddingTop="@dimen/all_apps_header_top_padding"
- android:orientation="vertical" >
-
- <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
- android:id="@id/search_container_all_apps"
- android:layout_width="match_parent"
- 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:inputType="text|textNoSuggestions|textCapWords"
- android:maxLines="1"
- android:padding="8dp"
- android:saveEnabled="false"
- android:scrollHorizontally="true"
- android:singleLine="true"
- android:textColor="?android:attr/textColorSecondary"
- android:textColorHint="@drawable/all_apps_search_hint"
- android:textSize="16sp" />
-
- <include layout="@layout/all_apps_fast_scroller" />
- </view>
+ android:visibility="invisible" />
</com.android.launcher3.secondarydisplay.SecondaryDragLayer>
\ No newline at end of file
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_paged_view.xml b/res/layout/widgets_full_sheet_paged_view.xml
index 098c9b0..3635c73 100644
--- a/res/layout/widgets_full_sheet_paged_view.xml
+++ b/res/layout/widgets_full_sheet_paged_view.xml
@@ -89,8 +89,7 @@
android:gravity="center_horizontal"
android:orientation="horizontal"
android:paddingVertical="8dp"
- android:paddingLeft="@dimen/widget_tabs_horizontal_padding"
- android:paddingRight="@dimen/widget_tabs_horizontal_padding"
+ android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin"
android:background="?android:attr/colorBackground"
style="@style/TextHeadline"
launcher:layout_sticky="true">
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..c116c12 100644
--- a/res/layout/work_mode_fab.xml
+++ b/res/layout/work_mode_fab.xml
@@ -12,23 +12,35 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<com.android.launcher3.allapps.WorkModeSwitch xmlns:android="http://schemas.android.com/apk/res/android"
- style="@style/TextHeadline"
+<com.android.launcher3.allapps.WorkModeSwitch
+ xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/work_mode_toggle"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:layout_height="@dimen/work_fab_height"
android:layout_width="wrap_content"
- android:gravity="center"
- android:includeFontPadding="false"
- android:textDirection="locale"
- android:drawableTint="@color/all_apps_tab_text"
- android:textColor="@color/all_apps_tab_text"
- android:textSize="14sp"
+ android:minHeight="@dimen/work_fab_height"
+ android:gravity="center_vertical"
android:background="@drawable/work_apps_toggle_background"
- android:drawablePadding="8dp"
- android:drawableStart="@drawable/ic_corp_off"
- android:layout_marginBottom="@dimen/work_fab_margin_bottom"
- android:paddingLeft="@dimen/work_mode_fab_padding"
- android:paddingRight="@dimen/work_mode_fab_padding"
- android:text="@string/work_apps_pause_btn_text" />
\ No newline at end of file
+ android:forceHasOverlappingRendering="false"
+ android:contentDescription="@string/work_apps_pause_btn_text"
+ android:animateLayoutChanges="true">
+ <ImageView
+ android:id="@+id/work_icon"
+ android:layout_width="@dimen/work_fab_icon_size"
+ android:layout_height="@dimen/work_fab_icon_size"
+ android:importantForAccessibility="no"
+ android:src="@drawable/ic_corp_off"
+ android:scaleType="center"/>
+ <TextView
+ android:id="@+id/pause_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="@color/all_apps_tab_text"
+ android:textSize="14sp"
+ android:includeFontPadding="false"
+ android:textDirection="locale"
+ android:text="@string/work_apps_pause_btn_text"
+ android:layout_marginStart="@dimen/work_fab_text_start_margin"
+ style="@style/TextHeadline"/>
+</com.android.launcher3.allapps.WorkModeSwitch>
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index 445bfda..500fce2 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -59,7 +59,6 @@
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Deursoek programme"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"Laai tans programme …"</string>
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Kon geen programme kry wat by \"<xliff:g id="QUERY">%1$s</xliff:g>\" pas nie"</string>
- <string name="all_apps_search_market_message" msgid="1366263386197059176">"Soek meer programme"</string>
<string name="label_application" msgid="8531721983832654978">"Program"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Alle programme"</string>
<string name="notifications_header" msgid="1404149926117359025">"Kennisgewings"</string>
@@ -75,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-am/strings.xml b/res/values-am/strings.xml
index 474cd9f..b7d4a7d 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -59,7 +59,6 @@
<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_search_market_message" msgid="1366263386197059176">"ተጨማሪ መተግበሪያዎች ይፈልጉ"</string>
<string name="label_application" msgid="8531721983832654978">"መተግበሪያ"</string>
<string name="all_apps_label" msgid="5015784846527570951">"ሁሉም መተግበሪያዎች"</string>
<string name="notifications_header" msgid="1404149926117359025">"ማሳወቂያዎች"</string>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index a4f7c03..42c51de 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -38,7 +38,7 @@
<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_to_home_screen" msgid="9168649446635919791">"إضافة التطبيق المصغّر إلى الشاشة الرئيسية"</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="widgets_count" msgid="6467746476364652096">"{count,plural, =1{تطبيق مصغّر واحد}zero{# تطبيق مصغّر}two{تطبيقان مصغّران}few{# تطبيقات مصغّرة}many{# تطبيقًا مصغّرًا}other{# تطبيق مصغّر}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{اختصار واحد}zero{# اختصار}two{اختصاران}few{# اختصارات}many{# اختصارًا}other{# اختصار}}"</string>
@@ -59,7 +59,6 @@
<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_search_market_message" msgid="1366263386197059176">"البحث عن مزيد من التطبيقات"</string>
<string name="label_application" msgid="8531721983832654978">"تطبيق"</string>
<string name="all_apps_label" msgid="5015784846527570951">"جميع التطبيقات"</string>
<string name="notifications_header" msgid="1404149926117359025">"الإشعارات"</string>
@@ -102,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 beb9619..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>
@@ -59,7 +59,6 @@
<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_search_market_message" msgid="1366263386197059176">"আৰু অধিক এপবোৰ সন্ধান কৰক"</string>
<string name="label_application" msgid="8531721983832654978">"এপ্"</string>
<string name="all_apps_label" msgid="5015784846527570951">"আটাইবোৰ এপ্"</string>
<string name="notifications_header" msgid="1404149926117359025">"জাননীসমূহ"</string>
@@ -73,9 +72,9 @@
<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="app_info_drop_target_label" msgid="692894985365717661">"এপ সম্পৰ্কীয় তথ্য"</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>
@@ -110,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>
@@ -120,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-az/strings.xml b/res/values-az/strings.xml
index 93e6f08..2e07405 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -59,7 +59,6 @@
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Tətbiqləri axtarın"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"Tətbiqlər yüklənir…"</string>
<string name="all_apps_no_search_results" msgid="3200346862396363786">"<xliff:g id="QUERY">%1$s</xliff:g> sorğusuna uyğun tətbiq tapılmadı"</string>
- <string name="all_apps_search_market_message" msgid="1366263386197059176">"Daha çox tətbiq üçün axtarış edin"</string>
<string name="label_application" msgid="8531721983832654978">"Tətbiq"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Bütün tətbiqlər"</string>
<string name="notifications_header" msgid="1404149926117359025">"Bildirişlər"</string>
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index ded4dc9..cc6e908 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -59,7 +59,6 @@
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Pretražite aplikacije"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"Aplikacije se učitavaju…"</string>
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Nije pronađena nijedna aplikacija za „<xliff:g id="QUERY">%1$s</xliff:g>“"</string>
- <string name="all_apps_search_market_message" msgid="1366263386197059176">"Pretraži još aplikacija"</string>
<string name="label_application" msgid="8531721983832654978">"Aplikacija"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Sve aplikacije"</string>
<string name="notifications_header" msgid="1404149926117359025">"Obaveštenja"</string>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index 7fa8341..6886b70 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -59,7 +59,6 @@
<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_search_market_message" msgid="1366263386197059176">"Шукаць іншыя праграмы"</string>
<string name="label_application" msgid="8531721983832654978">"Праграма"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Усе праграмы"</string>
<string name="notifications_header" msgid="1404149926117359025">"Апавяшчэнні"</string>
@@ -73,7 +72,7 @@
<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="app_info_drop_target_label" msgid="692894985365717661">"Пра праграму"</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="pin_prediction" msgid="4196423321649756498">"Замацаваць прапанаваную праграму"</string>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index 62b7796..549c8ea 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -59,7 +59,6 @@
<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_search_market_message" msgid="1366263386197059176">"Търсене на още приложения"</string>
<string name="label_application" msgid="8531721983832654978">"Приложение"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Всички приложения"</string>
<string name="notifications_header" msgid="1404149926117359025">"Известия"</string>
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
index 1be272b..9683182 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -59,7 +59,6 @@
<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_search_market_message" msgid="1366263386197059176">"আরও অ্যাপ্লিকেশানের জন্য খুঁজুন"</string>
<string name="label_application" msgid="8531721983832654978">"অ্যাপ"</string>
<string name="all_apps_label" msgid="5015784846527570951">"সব অ্যাপ"</string>
<string name="notifications_header" msgid="1404149926117359025">"বিজ্ঞপ্তি"</string>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index 639b6b3..f99525f 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -59,7 +59,6 @@
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Pretražite aplikacije"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"Aplikacije se učitavaju…"</string>
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Nije pronađena nijedna aplikacija za upit \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
- <string name="all_apps_search_market_message" msgid="1366263386197059176">"Pretraži više aplikacija"</string>
<string name="label_application" msgid="8531721983832654978">"Aplikacija"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Sve aplikacije"</string>
<string name="notifications_header" msgid="1404149926117359025">"Obavještenja"</string>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index ee35fd3..d8ede15 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -59,7 +59,6 @@
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Cerca aplicacions"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"S\'estan carregant les aplicacions…"</string>
<string name="all_apps_no_search_results" msgid="3200346862396363786">"No s\'ha trobat cap aplicació que coincideixi amb \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
- <string name="all_apps_search_market_message" msgid="1366263386197059176">"Cerca més aplicacions"</string>
<string name="label_application" msgid="8531721983832654978">"Aplicació"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Totes les aplicacions"</string>
<string name="notifications_header" msgid="1404149926117359025">"Notificacions"</string>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 1b418c6..600d66f 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -59,7 +59,6 @@
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Hledat v aplikacích"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"Načítání aplikací…"</string>
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Dotazu „<xliff:g id="QUERY">%1$s</xliff:g>“ neodpovídají žádné aplikace"</string>
- <string name="all_apps_search_market_message" msgid="1366263386197059176">"Vyhledat další aplikace"</string>
<string name="label_application" msgid="8531721983832654978">"Aplikace"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Všechny aplikace"</string>
<string name="notifications_header" msgid="1404149926117359025">"Oznámení"</string>
@@ -75,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-da/strings.xml b/res/values-da/strings.xml
index 5cdadcd..4da7b84 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -59,7 +59,6 @@
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Søg efter apps"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"Indlæser apps…"</string>
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Der blev ikke fundet nogen apps, som matcher \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
- <string name="all_apps_search_market_message" msgid="1366263386197059176">"Søg efter flere apps"</string>
<string name="label_application" msgid="8531721983832654978">"App"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Alle apps"</string>
<string name="notifications_header" msgid="1404149926117359025">"Notifikationer"</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index b75ef9d..3c2514f 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -59,7 +59,6 @@
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Apps finden"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"Apps werden geladen…"</string>
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Keine Apps für \"<xliff:g id="QUERY">%1$s</xliff:g>\" gefunden"</string>
- <string name="all_apps_search_market_message" msgid="1366263386197059176">"Weitere Apps suchen"</string>
<string name="label_application" msgid="8531721983832654978">"App"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Alle Apps"</string>
<string name="notifications_header" msgid="1404149926117359025">"Benachrichtigungen"</string>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 99c9e23..ae9a8fd 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -59,7 +59,6 @@
<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_search_market_message" msgid="1366263386197059176">"Αναζήτηση περισσότερων εφαρμογών"</string>
<string name="label_application" msgid="8531721983832654978">"Εφαρμογή"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Όλες οι εφαρμογές"</string>
<string name="notifications_header" msgid="1404149926117359025">"Ειδοποιήσεις"</string>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index 97f0528..4f15eb5 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -59,7 +59,6 @@
<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_search_market_message" msgid="1366263386197059176">"Search for more apps"</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>
diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml
index 97f0528..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,19 +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_search_market_message" msgid="1366263386197059176">"Search for more 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="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>
@@ -76,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>
@@ -102,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>
@@ -114,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>
@@ -126,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>
@@ -139,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>
@@ -147,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>
@@ -162,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-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index 97f0528..4f15eb5 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -59,7 +59,6 @@
<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_search_market_message" msgid="1366263386197059176">"Search for more apps"</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>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index 97f0528..4f15eb5 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -59,7 +59,6 @@
<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_search_market_message" msgid="1366263386197059176">"Search for more apps"</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>
diff --git a/res/values-en-rXC/strings.xml b/res/values-en-rXC/strings.xml
index 4977beb..e644c23 100644
--- a/res/values-en-rXC/strings.xml
+++ b/res/values-en-rXC/strings.xml
@@ -59,7 +59,6 @@
<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_search_market_message" msgid="1366263386197059176">"Search for more apps"</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>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 3ed312b..0bc8f4f 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -59,7 +59,6 @@
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Buscar apps"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"Cargando apps…"</string>
<string name="all_apps_no_search_results" msgid="3200346862396363786">"No hay apps que coincidan con \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
- <string name="all_apps_search_market_message" msgid="1366263386197059176">"Buscar más apps"</string>
<string name="label_application" msgid="8531721983832654978">"App"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Todas las apps"</string>
<string name="notifications_header" msgid="1404149926117359025">"Notificaciones"</string>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 567ff0d..4045ad3 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -59,7 +59,6 @@
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Buscar aplicaciones"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"Cargando aplicaciones…"</string>
<string name="all_apps_no_search_results" msgid="3200346862396363786">"No se han encontrado aplicaciones que contengan \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
- <string name="all_apps_search_market_message" msgid="1366263386197059176">"Buscar más aplicaciones"</string>
<string name="label_application" msgid="8531721983832654978">"Aplicación"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Todas las aplicaciones"</string>
<string name="notifications_header" msgid="1404149926117359025">"Notificaciones"</string>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index c34a003..effd140 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -59,7 +59,6 @@
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Otsige rakendusi"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"Rakenduste laadimine …"</string>
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Päringule „<xliff:g id="QUERY">%1$s</xliff:g>” ei vastanud ükski rakendus"</string>
- <string name="all_apps_search_market_message" msgid="1366263386197059176">"Otsi rohkem rakendusi"</string>
<string name="label_application" msgid="8531721983832654978">"Rakendus"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Kõik rakendused"</string>
<string name="notifications_header" msgid="1404149926117359025">"Märguanded"</string>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index 55e0c3c..8b944fe 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -59,7 +59,6 @@
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Bilatu aplikazioetan"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"Aplikazioak kargatzen…"</string>
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Ez da aurkitu \"<xliff:g id="QUERY">%1$s</xliff:g>\" bilaketaren emaitzarik"</string>
- <string name="all_apps_search_market_message" msgid="1366263386197059176">"Bilatu aplikazio gehiago"</string>
<string name="label_application" msgid="8531721983832654978">"Aplikazioa"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Aplikazio guztiak"</string>
<string name="notifications_header" msgid="1404149926117359025">"Jakinarazpenak"</string>
@@ -125,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-fa/strings.xml b/res/values-fa/strings.xml
index 12db5bd..a1acda0 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -59,7 +59,6 @@
<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_search_market_message" msgid="1366263386197059176">"جستجوی برنامههای بیشتر"</string>
<string name="label_application" msgid="8531721983832654978">"برنامه"</string>
<string name="all_apps_label" msgid="5015784846527570951">"همه برنامهها"</string>
<string name="notifications_header" msgid="1404149926117359025">"اعلانها"</string>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index 135c452..89e4106 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -59,7 +59,6 @@
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Hae sovelluksia"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"Ladataan sovelluksia…"</string>
<string name="all_apps_no_search_results" msgid="3200346862396363786">"<xliff:g id="QUERY">%1$s</xliff:g> ei palauttanut sovelluksia."</string>
- <string name="all_apps_search_market_message" msgid="1366263386197059176">"Hae lisää sovelluksia"</string>
<string name="label_application" msgid="8531721983832654978">"Sovellus"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Kaikki sovellukset"</string>
<string name="notifications_header" msgid="1404149926117359025">"Ilmoitukset"</string>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index 446a507..448569d 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -59,7 +59,6 @@
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Rechercher dans les applications"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"Chargement des applications en cours…"</string>
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Aucune application trouvée correspondant à « <xliff:g id="QUERY">%1$s</xliff:g> »"</string>
- <string name="all_apps_search_market_message" msgid="1366263386197059176">"Rechercher plus d\'applications"</string>
<string name="label_application" msgid="8531721983832654978">"Application"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Toutes les applications"</string>
<string name="notifications_header" msgid="1404149926117359025">"Notifications"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 1d623b1..b371067 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -59,7 +59,6 @@
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Rechercher dans les applications"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"Chargement des applications…"</string>
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Aucune application ne correspond à la requête \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
- <string name="all_apps_search_market_message" msgid="1366263386197059176">"Rechercher plus d\'applications"</string>
<string name="label_application" msgid="8531721983832654978">"Application"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Toutes les applis"</string>
<string name="notifications_header" msgid="1404149926117359025">"Notifications"</string>
@@ -158,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-gl/strings.xml b/res/values-gl/strings.xml
index 256c4d3..045d8d7 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -59,7 +59,6 @@
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Buscar aplicacións"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"Cargando aplicacións…"</string>
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Non se atoparon aplicacións que coincidan con \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
- <string name="all_apps_search_market_message" msgid="1366263386197059176">"Buscar máis aplicacións"</string>
<string name="label_application" msgid="8531721983832654978">"Aplicación"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Todas as aplicacións"</string>
<string name="notifications_header" msgid="1404149926117359025">"Notificacións"</string>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index f1e1996..7288570 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -59,7 +59,6 @@
<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_search_market_message" msgid="1366263386197059176">"વધુ ઍપ્લિકેશનો શોધો"</string>
<string name="label_application" msgid="8531721983832654978">"ઍપ"</string>
<string name="all_apps_label" msgid="5015784846527570951">"બધી ઍપ"</string>
<string name="notifications_header" msgid="1404149926117359025">"નોટિફિકેશન"</string>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 1b05040..e90e63a 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -59,7 +59,6 @@
<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_search_market_message" msgid="1366263386197059176">"और ऐप सर्च करें"</string>
<string name="label_application" msgid="8531721983832654978">"ऐप्लिकेशन"</string>
<string name="all_apps_label" msgid="5015784846527570951">"सभी ऐप्लिकेशन"</string>
<string name="notifications_header" msgid="1404149926117359025">"सूचनाएं"</string>
@@ -159,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-hr/strings.xml b/res/values-hr/strings.xml
index 4a0fbe8..a1369e8 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -59,7 +59,6 @@
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Pretraži aplikacije"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"Učitavanje aplikacija…"</string>
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Nema aplikacija podudarnih s upitom \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
- <string name="all_apps_search_market_message" msgid="1366263386197059176">"Traži više aplikacija"</string>
<string name="label_application" msgid="8531721983832654978">"Aplikacija"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Sve aplikacije"</string>
<string name="notifications_header" msgid="1404149926117359025">"Obavijesti"</string>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index c1e527d..a5d4835 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -59,7 +59,6 @@
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Alkalmazások keresése"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"Alkalmazások betöltése…"</string>
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Nem található alkalmazás a(z) „<xliff:g id="QUERY">%1$s</xliff:g>” lekérdezésre"</string>
- <string name="all_apps_search_market_message" msgid="1366263386197059176">"További alkalmazások keresése"</string>
<string name="label_application" msgid="8531721983832654978">"Alkalmazás"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Összes alkalmazás"</string>
<string name="notifications_header" msgid="1404149926117359025">"Értesítések"</string>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index f0d01f1..b8f1643 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -59,7 +59,6 @@
<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_search_market_message" msgid="1366263386197059176">"Որոնել այլ հավելվածներ"</string>
<string name="label_application" msgid="8531721983832654978">"Հավելված"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Բոլոր հավելվածները"</string>
<string name="notifications_header" msgid="1404149926117359025">"Ծանուցումներ"</string>
@@ -75,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-in/strings.xml b/res/values-in/strings.xml
index 1a97a45..e46026e 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -59,7 +59,6 @@
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Telusuri aplikasi"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"Memuat aplikasi…"</string>
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Tidak ditemukan aplikasi yang cocok dengan \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
- <string name="all_apps_search_market_message" msgid="1366263386197059176">"Telusuri aplikasi lainnya"</string>
<string name="label_application" msgid="8531721983832654978">"Aplikasi"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Semua aplikasi"</string>
<string name="notifications_header" msgid="1404149926117359025">"Notifikasi"</string>
diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml
index a1959f8..92be25b 100644
--- a/res/values-is/strings.xml
+++ b/res/values-is/strings.xml
@@ -59,7 +59,6 @@
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Leita í forritum"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"Hleður forrit…"</string>
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Ekki fundust forrit sem samsvara „<xliff:g id="QUERY">%1$s</xliff:g>“"</string>
- <string name="all_apps_search_market_message" msgid="1366263386197059176">"Leita að fleiri forritum"</string>
<string name="label_application" msgid="8531721983832654978">"Forrit"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Öll forrit"</string>
<string name="notifications_header" msgid="1404149926117359025">"Tilkynningar"</string>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 302efcb..b5b7add 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -59,7 +59,6 @@
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Cerca nelle app"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"Caricamento delle app…"</string>
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Nessuna app trovata corrispondente a \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
- <string name="all_apps_search_market_message" msgid="1366263386197059176">"Cerca altre app"</string>
<string name="label_application" msgid="8531721983832654978">"App"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Tutte le app"</string>
<string name="notifications_header" msgid="1404149926117359025">"Notifiche"</string>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 15e200f..d79c022 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -40,8 +40,8 @@
<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="widgets_count" msgid="6467746476364652096">"{count,plural, =1{ווידג\'ט אחד}two{# ווידג\'טים}many{# ווידג\'טים}other{# ווידג\'טים}}"</string>
- <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{קיצור דרך אחד}two{# קיצורי דרך}many{# קיצורי דרך}other{# קיצורי דרך}}"</string>
+ <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{ווידג\'ט אחד}one{# ווידג\'טים}two{# ווידג\'טים}other{# ווידג\'טים}}"</string>
+ <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{קיצור דרך אחד}one{# קיצורי דרך}two{# קיצורי דרך}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>
<string name="widget_button_text" msgid="2880537293434387943">"ווידג\'טים"</string>
<string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"חיפוש"</string>
@@ -59,7 +59,6 @@
<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_search_market_message" msgid="1366263386197059176">"חיפוש אפליקציות נוספות"</string>
<string name="label_application" msgid="8531721983832654978">"אפליקציה"</string>
<string name="all_apps_label" msgid="5015784846527570951">"כל האפליקציות"</string>
<string name="notifications_header" msgid="1404149926117359025">"התראות"</string>
@@ -90,7 +89,7 @@
<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>
- <string name="dotted_app_label" msgid="1865617679843363410">"{count,plural, =1{לאפליקציה {app_name} יש התראה אחת}two{לאפליקציה {app_name} יש # התראות}many{לאפליקציה {app_name} יש # התראות}other{לאפליקציה {app_name} יש # התראות}}"</string>
+ <string name="dotted_app_label" msgid="1865617679843363410">"{count,plural, =1{לאפליקציה {app_name} יש התראה אחת}one{לאפליקציה {app_name} יש # התראות}two{לאפליקציה {app_name} יש # התראות}other{לאפליקציה {app_name} יש # התראות}}"</string>
<string name="default_scroll_format" msgid="7475544710230993317">"דף %1$d מתוך %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"מסך הבית %1$d מתוך %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"מסך הבית חדש"</string>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 2fd3ab5..1a442a8 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -59,7 +59,6 @@
<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_search_market_message" msgid="1366263386197059176">"他のアプリを検索"</string>
<string name="label_application" msgid="8531721983832654978">"アプリ"</string>
<string name="all_apps_label" msgid="5015784846527570951">"すべてのアプリ"</string>
<string name="notifications_header" msgid="1404149926117359025">"通知"</string>
@@ -75,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>
@@ -171,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-ka/strings.xml b/res/values-ka/strings.xml
index 24f95dd..8437dd8 100644
--- a/res/values-ka/strings.xml
+++ b/res/values-ka/strings.xml
@@ -59,7 +59,6 @@
<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_search_market_message" msgid="1366263386197059176">"მეტი აპის პოვნა"</string>
<string name="label_application" msgid="8531721983832654978">"აპი"</string>
<string name="all_apps_label" msgid="5015784846527570951">"ყველა აპი"</string>
<string name="notifications_header" msgid="1404149926117359025">"შეტყობინებები"</string>
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
index 8ed7ec5..ec3ee18 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -59,7 +59,6 @@
<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_search_market_message" msgid="1366263386197059176">"Қосымша қолданбалар іздеу"</string>
<string name="label_application" msgid="8531721983832654978">"Қолданба"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Барлық қолданба"</string>
<string name="notifications_header" msgid="1404149926117359025">"Хабарландырулар"</string>
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
index 306449c..f1c1d08 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -59,7 +59,6 @@
<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_search_market_message" msgid="1366263386197059176">"ស្វែងរកកម្មវិធីច្រើនទៀត"</string>
<string name="label_application" msgid="8531721983832654978">"កម្មវិធី"</string>
<string name="all_apps_label" msgid="5015784846527570951">"កម្មវិធីទាំងអស់"</string>
<string name="notifications_header" msgid="1404149926117359025">"ការជូនដំណឹង"</string>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index a9b89b5..72eaeac 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -59,7 +59,6 @@
<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_search_market_message" msgid="1366263386197059176">"ಮತ್ತಷ್ಟು ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ಹುಡುಕಿ"</string>
<string name="label_application" msgid="8531721983832654978">"ಆ್ಯಪ್"</string>
<string name="all_apps_label" msgid="5015784846527570951">"ಎಲ್ಲಾ ಆ್ಯಪ್ಗಳು"</string>
<string name="notifications_header" msgid="1404149926117359025">"ಅಧಿಸೂಚನೆಗಳು"</string>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index 0935aa1..f6b8ff7 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -59,7 +59,6 @@
<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_search_market_message" msgid="1366263386197059176">"더 많은 앱 검색"</string>
<string name="label_application" msgid="8531721983832654978">"앱"</string>
<string name="all_apps_label" msgid="5015784846527570951">"모든 앱"</string>
<string name="notifications_header" msgid="1404149926117359025">"알림"</string>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index 755e274..46358cd 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -53,13 +53,12 @@
<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="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Виджеттин жөндөөлөрүн өзгөртүү үчүн таптап коюңуз"</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>
+ <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_search_market_message" msgid="1366263386197059176">"Көбүрөөк колдонмолорду издөө"</string>
<string name="label_application" msgid="8531721983832654978">"Колдонмо"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Бардык колдонмолор"</string>
<string name="notifications_header" msgid="1404149926117359025">"Билдирмелер"</string>
@@ -75,18 +74,18 @@
<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>
- <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>
- <string name="gadget_complete_setup_text" msgid="309040266978007925">"Жөндөп бүтүү үчүн таптап коюңуз"</string>
+ <string name="gadget_setup_text" msgid="8348374825537681407">"Виджеттин параметрлери"</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>
@@ -103,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-lo/strings.xml b/res/values-lo/strings.xml
index 9e75492..d8b55f8 100644
--- a/res/values-lo/strings.xml
+++ b/res/values-lo/strings.xml
@@ -59,7 +59,6 @@
<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_search_market_message" msgid="1366263386197059176">"ຊອກຫາແອັບເພີ່ມເຕີມ"</string>
<string name="label_application" msgid="8531721983832654978">"ແອັບ"</string>
<string name="all_apps_label" msgid="5015784846527570951">"ແອັບທັງໝົດ"</string>
<string name="notifications_header" msgid="1404149926117359025">"ການແຈ້ງເຕືອນ"</string>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index bbe0cfa..e68362d 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -59,7 +59,6 @@
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Paieškos programos"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"Įkeliamos programos…"</string>
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Nerasta jokių užklausą „<xliff:g id="QUERY">%1$s</xliff:g>“ atitinkančių programų"</string>
- <string name="all_apps_search_market_message" msgid="1366263386197059176">"Ieškoti daugiau programų"</string>
<string name="label_application" msgid="8531721983832654978">"Programa"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Visos programos"</string>
<string name="notifications_header" msgid="1404149926117359025">"Pranešimai"</string>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index b5836ef..75f6d1a 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -59,7 +59,6 @@
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Meklēt lietotnes"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"Notiek lietotņu ielāde…"</string>
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Vaicājumam “<xliff:g id="QUERY">%1$s</xliff:g>” neatbilda neviena lietotne"</string>
- <string name="all_apps_search_market_message" msgid="1366263386197059176">"Meklēt citas lietotnes"</string>
<string name="label_application" msgid="8531721983832654978">"Lietotne"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Visas lietotnes"</string>
<string name="notifications_header" msgid="1404149926117359025">"Paziņojumi"</string>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index d452eba..fef9bbe 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -59,7 +59,6 @@
<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_search_market_message" msgid="1366263386197059176">"Пребарај други апликации"</string>
<string name="label_application" msgid="8531721983832654978">"Апликација"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Сите апликации"</string>
<string name="notifications_header" msgid="1404149926117359025">"Известувања"</string>
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
index df532c0..802ea81 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -59,7 +59,6 @@
<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_search_market_message" msgid="1366263386197059176">"കൂടുതൽ ആപ്പുകൾക്ക് തിരയുക"</string>
<string name="label_application" msgid="8531721983832654978">"ആപ്പ്"</string>
<string name="all_apps_label" msgid="5015784846527570951">"എല്ലാ ആപ്പുകളും"</string>
<string name="notifications_header" msgid="1404149926117359025">"അറിയിപ്പുകൾ"</string>
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
index c71aaff..f091cb8 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -59,7 +59,6 @@
<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_search_market_message" msgid="1366263386197059176">"Бусад апп-г хайх"</string>
<string name="label_application" msgid="8531721983832654978">"Апп"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Бүх апп"</string>
<string name="notifications_header" msgid="1404149926117359025">"Мэдэгдэл"</string>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index e871073..b553c90 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -59,7 +59,6 @@
<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_search_market_message" msgid="1366263386197059176">"अधिक अॅप्स शोधा"</string>
<string name="label_application" msgid="8531721983832654978">"ॲप"</string>
<string name="all_apps_label" msgid="5015784846527570951">"सर्व अॅप्स"</string>
<string name="notifications_header" msgid="1404149926117359025">"सूचना"</string>
@@ -121,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>
@@ -161,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-ms/strings.xml b/res/values-ms/strings.xml
index cbfb551..edd0530 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -59,7 +59,6 @@
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Cari apl"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"Memuatkan apl…"</string>
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Tiada apl yang ditemui sepadan dengan \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
- <string name="all_apps_search_market_message" msgid="1366263386197059176">"Cari lagi apl"</string>
<string name="label_application" msgid="8531721983832654978">"Apl"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Semua apl"</string>
<string name="notifications_header" msgid="1404149926117359025">"Pemberitahuan"</string>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index 984ec4c..69157bc 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -54,12 +54,11 @@
<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>
<string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" နှင့်ကိုက်ညီသည့် အပ်ပ်များကို မတွေ့ပါ"</string>
- <string name="all_apps_search_market_message" msgid="1366263386197059176">"နောက်ထပ် အက်ပ်များကို ရှာပါ"</string>
<string name="label_application" msgid="8531721983832654978">"အက်ပ်"</string>
<string name="all_apps_label" msgid="5015784846527570951">"အက်ပ်အားလုံး"</string>
<string name="notifications_header" msgid="1404149926117359025">"အကြောင်းကြားချက်များ"</string>
@@ -75,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>
@@ -115,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>
@@ -167,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-nb/strings.xml b/res/values-nb/strings.xml
index b29ee77..3e08d10 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -59,7 +59,6 @@
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Søk etter apper"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"Laster inn appene …"</string>
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Fant ingen apper som samsvarer med «<xliff:g id="QUERY">%1$s</xliff:g>»"</string>
- <string name="all_apps_search_market_message" msgid="1366263386197059176">"Søk etter flere apper"</string>
<string name="label_application" msgid="8531721983832654978">"App"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Alle apper"</string>
<string name="notifications_header" msgid="1404149926117359025">"Varsler"</string>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index fcc1ece..6d48969 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -59,7 +59,6 @@
<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_search_market_message" msgid="1366263386197059176">"थप एपहरू खोज्नुहोस्"</string>
<string name="label_application" msgid="8531721983832654978">"एप"</string>
<string name="all_apps_label" msgid="5015784846527570951">"सबै एप"</string>
<string name="notifications_header" msgid="1404149926117359025">"सूचनाहरू"</string>
@@ -75,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-night-v31/colors.xml b/res/values-night-v31/colors.xml
index eefe8c5..54d6d88 100644
--- a/res/values-night-v31/colors.xml
+++ b/res/values-night-v31/colors.xml
@@ -25,8 +25,5 @@
<color name="home_settings_track_on_color">@android:color/system_accent2_700</color>
<color name="home_settings_track_off_color">@android:color/system_neutral1_700</color>
- <color name="all_apps_button_bg_color">@android:color/system_neutral1_800</color>
- <color name="all_apps_button_color_1">@android:color/system_accent1_300</color>
- <color name="all_apps_button_color_3">@android:color/system_accent1_100</color>
- <color name="all_apps_button_color_4">@android:color/system_accent2_100</color>
+ <color name="all_apps_button_color">?android:attr/textColorSecondary</color>
</resources>
\ No newline at end of file
diff --git a/res/values-night/colors.xml b/res/values-night/colors.xml
index ce272ce..17fe419 100644
--- a/res/values-night/colors.xml
+++ b/res/values-night/colors.xml
@@ -17,9 +17,5 @@
-->
<resources>
- <color name="all_apps_button_bg_color">#2E3132</color>
- <color name="all_apps_button_color_1">#33B9DB</color>
- <color name="all_apps_button_color_2">#EFFBFF</color>
- <color name="all_apps_button_color_3">#B1EBFF</color>
- <color name="all_apps_button_color_4">#DEE0FF</color>
+ <color name="all_apps_button_color">#BFC8CC</color>
</resources>
\ No newline at end of file
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index bd41952..4b203b7 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -59,7 +59,6 @@
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Apps zoeken"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"Apps laden…"</string>
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Er zijn geen apps gevonden die overeenkomen met \'<xliff:g id="QUERY">%1$s</xliff:g>\'"</string>
- <string name="all_apps_search_market_message" msgid="1366263386197059176">"Zoeken naar meer apps"</string>
<string name="label_application" msgid="8531721983832654978">"App"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Alle apps"</string>
<string name="notifications_header" msgid="1404149926117359025">"Meldingen"</string>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index 95fa000..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_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="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="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,37 +52,36 @@
<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>
<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_search_market_message" msgid="1366263386197059176">"ଅଧିକ ଆପ୍ ଖୋଜନ୍ତୁ"</string>
<string name="label_application" msgid="8531721983832654978">"ଆପ୍"</string>
<string name="all_apps_label" msgid="5015784846527570951">"ସବୁ ଆପ"</string>
<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>
@@ -92,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>
@@ -105,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>
@@ -115,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>
@@ -131,9 +130,9 @@
<string name="dialog_remove" msgid="6510806469849709407">"କାଢ଼ି ଦିଅନ୍ତୁ"</string>
<string name="widgets_list" msgid="796804551140113767">"ୱିଜେଟ୍ ତାଲିକା"</string>
<string name="widgets_list_closed" msgid="6141506579418771922">"ୱିଜେଟ୍ ତାଲିକା ବନ୍ଦ ହୋଇଛି"</string>
- <string name="action_add_to_workspace" msgid="215894119683164916">"ମୂଳସ୍କ୍ରିନରେ ଯୋଗ କରନ୍ତୁ"</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>
@@ -146,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>
@@ -161,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 01fbee0..8e55b87 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -59,7 +59,6 @@
<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_search_market_message" msgid="1366263386197059176">"ਹੋਰ ਐਪਾਂ ਖੋਜੋ"</string>
<string name="label_application" msgid="8531721983832654978">"ਐਪ"</string>
<string name="all_apps_label" msgid="5015784846527570951">"ਸਾਰੀਆਂ ਐਪਾਂ"</string>
<string name="notifications_header" msgid="1404149926117359025">"ਸੂਚਨਾਵਾਂ"</string>
@@ -168,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 4b46f0b..8669949 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -59,7 +59,6 @@
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Wyszukaj aplikacje"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"Ładuję aplikacje…"</string>
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Nie znaleziono aplikacji pasujących do zapytania „<xliff:g id="QUERY">%1$s</xliff:g>”"</string>
- <string name="all_apps_search_market_message" msgid="1366263386197059176">"Wyszukaj więcej aplikacji"</string>
<string name="label_application" msgid="8531721983832654978">"Aplikacja"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Wszystkie aplikacje"</string>
<string name="notifications_header" msgid="1404149926117359025">"Powiadomienia"</string>
@@ -107,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 dbfa0cb..4dda225 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -59,7 +59,6 @@
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Pesquisar aplicações"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"A carregar aplicações…"</string>
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Nenhuma app correspondente a \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
- <string name="all_apps_search_market_message" msgid="1366263386197059176">"Pesquisar mais aplicações"</string>
<string name="label_application" msgid="8531721983832654978">"Aplicação"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Todas as apps"</string>
<string name="notifications_header" msgid="1404149926117359025">"Notificações"</string>
@@ -163,12 +162,12 @@
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Perfil de trabalho"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"As apps de trabalho têm um emblema e estão visíveis para o seu administrador de TI"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
- <string name="work_apps_paused_title" msgid="3040901117349444598">"As apps de trabalho estão em pausa"</string>
+ <string name="work_apps_paused_title" msgid="3040901117349444598">"As apps de trabalho estão suspensas."</string>
<string name="work_apps_paused_body" msgid="261634750995824906">"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_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-pt/strings.xml b/res/values-pt/strings.xml
index 3a5ed5b..d97ba0f 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -32,7 +32,7 @@
<string name="split_screen_position_left" msgid="7537793098851830883">"Dividir para a esquerda"</string>
<string name="split_screen_position_right" msgid="1569377524925193369">"Dividir para a direita"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Informações do app %1$s"</string>
- <string name="long_press_widget_to_add" msgid="3587712543577675817">"Toque e mantenha pressionado para mover um widget."</string>
+ <string name="long_press_widget_to_add" msgid="3587712543577675817">"Toque e pressione para mover um widget."</string>
<string name="long_accessible_way_to_add" msgid="2733588281439571974">"Toque duas vezes e mantenha a tela pressionada para mover um widget ou usar ações personalizadas."</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d de largura por %2$d de altura"</string>
@@ -59,7 +59,6 @@
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Pesquisar apps"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"Carregando apps…"</string>
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Nenhum app encontrado que corresponda a \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
- <string name="all_apps_search_market_message" msgid="1366263386197059176">"Pesquisar mais apps"</string>
<string name="label_application" msgid="8531721983832654978">"App"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Todos os apps"</string>
<string name="notifications_header" msgid="1404149926117359025">"Notificações"</string>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 6061ea5..794609e 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -59,7 +59,6 @@
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Caută aplicații"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"Se încarcă aplicații…"</string>
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Nu s-a găsit nicio aplicație pentru „<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
- <string name="all_apps_search_market_message" msgid="1366263386197059176">"Caută mai multe aplicații"</string>
<string name="label_application" msgid="8531721983832654978">"Aplicație"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Toate aplicațiile"</string>
<string name="notifications_header" msgid="1404149926117359025">"Notificări"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 0ad12bd..5987321 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -59,7 +59,6 @@
<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_search_market_message" msgid="1366263386197059176">"Искать другие приложения"</string>
<string name="label_application" msgid="8531721983832654978">"Приложение"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Все приложения"</string>
<string name="notifications_header" msgid="1404149926117359025">"Уведомления"</string>
diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml
index cf3dc64..e030432 100644
--- a/res/values-si/strings.xml
+++ b/res/values-si/strings.xml
@@ -59,7 +59,6 @@
<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_search_market_message" msgid="1366263386197059176">"තව යෙදුම් සඳහා සොයන්න"</string>
<string name="label_application" msgid="8531721983832654978">"යෙදුම"</string>
<string name="all_apps_label" msgid="5015784846527570951">"සියලු යෙදුම්"</string>
<string name="notifications_header" msgid="1404149926117359025">"දැනුම්දීම්"</string>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index e3bcc52..69fa362 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -59,7 +59,6 @@
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Hľadať aplikácie"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"Načítavajú sa aplikácie…"</string>
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Nenašli sa žiadne aplikácie zodpovedajúce dopytu <xliff:g id="QUERY">%1$s</xliff:g>"</string>
- <string name="all_apps_search_market_message" msgid="1366263386197059176">"Hľadať ďalšie aplikácie"</string>
<string name="label_application" msgid="8531721983832654978">"Aplikácia"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Všetky aplikácie"</string>
<string name="notifications_header" msgid="1404149926117359025">"Upozornenia"</string>
@@ -115,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-sl/strings.xml b/res/values-sl/strings.xml
index 5f952cf..b1f93e8 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -59,7 +59,6 @@
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Iskanje programov"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"Nalaganje aplikacij …"</string>
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Ni aplikacij, ki bi ustrezale poizvedbi »<xliff:g id="QUERY">%1$s</xliff:g>«"</string>
- <string name="all_apps_search_market_message" msgid="1366263386197059176">"Iskanje več aplikacij"</string>
<string name="label_application" msgid="8531721983832654978">"Aplikacija"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Vse aplikacije"</string>
<string name="notifications_header" msgid="1404149926117359025">"Obvestila"</string>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index 9f29a87..99bcbaa 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -59,7 +59,6 @@
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Kërko për aplikacione"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"Po ngarkon aplikacionet..."</string>
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Nuk u gjet asnjë aplikacion që përputhet me \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
- <string name="all_apps_search_market_message" msgid="1366263386197059176">"Kërko për më shumë aplikacione"</string>
<string name="label_application" msgid="8531721983832654978">"Aplikacioni"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Të gjitha aplikacionet"</string>
<string name="notifications_header" msgid="1404149926117359025">"Njoftimet"</string>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index 948d9b6..460a73e 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -59,7 +59,6 @@
<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_search_market_message" msgid="1366263386197059176">"Претражи још апликација"</string>
<string name="label_application" msgid="8531721983832654978">"Апликација"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Све апликације"</string>
<string name="notifications_header" msgid="1404149926117359025">"Обавештења"</string>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index c35adb0..6ec274b 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -59,7 +59,6 @@
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Sök efter appar"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"Läser in appar …"</string>
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Inga appar som matchar <xliff:g id="QUERY">%1$s</xliff:g> hittades"</string>
- <string name="all_apps_search_market_message" msgid="1366263386197059176">"Sök efter fler appar"</string>
<string name="label_application" msgid="8531721983832654978">"App"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Alla appar"</string>
<string name="notifications_header" msgid="1404149926117359025">"Aviseringar"</string>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index c233cc4..147c6e1 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -59,7 +59,6 @@
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Tafuta programu"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"Inapakia programu..."</string>
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Haikupata programu zozote zinazolingana na \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
- <string name="all_apps_search_market_message" msgid="1366263386197059176">"Tafuta programu zaidi"</string>
<string name="label_application" msgid="8531721983832654978">"Programu"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Programu zote"</string>
<string name="notifications_header" msgid="1404149926117359025">"Arifa"</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-ta/strings.xml b/res/values-ta/strings.xml
index bfec460..d8eacf5 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -59,7 +59,6 @@
<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_search_market_message" msgid="1366263386197059176">"கூடுதல் பயன்பாடுகளைத் தேடு"</string>
<string name="label_application" msgid="8531721983832654978">"ஆப்ஸ்"</string>
<string name="all_apps_label" msgid="5015784846527570951">"அனைத்து ஆப்ஸும்"</string>
<string name="notifications_header" msgid="1404149926117359025">"அறிவிப்புகள்"</string>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index 1d25371..7fddcf4 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -58,8 +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_search_market_message" msgid="1366263386197059176">"మరిన్ని యాప్ల కోసం సెర్చ్ చేయండి"</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>
@@ -75,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>
@@ -142,17 +141,17 @@
<string name="move_to_hotseat_position" msgid="6295412897075147808">"ఇష్టమైనవిలో <xliff:g id="NUMBER">%1$s</xliff:g>వ స్థానానికి తరలించు"</string>
<string name="item_moved" msgid="4606538322571412879">"అంశం తరలించబడింది"</string>
<string name="add_to_folder" msgid="9040534766770853243">"ఈ ఫోల్డర్కి జోడించండి: <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="add_to_folder_with_app" msgid="4534929978967147231">"<xliff:g id="NAME">%1$s</xliff:g> గల ఫోల్డర్కు జోడించు"</string>
+ <string name="add_to_folder_with_app" msgid="4534929978967147231">"<xliff:g id="NAME">%1$s</xliff:g> గల ఫోల్డర్కు జోడించండి"</string>
<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_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 f633b5f..ffeb20c 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -59,7 +59,6 @@
<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_search_market_message" msgid="1366263386197059176">"ค้นหาแอปเพิ่มเติม"</string>
<string name="label_application" msgid="8531721983832654978">"แอป"</string>
<string name="all_apps_label" msgid="5015784846527570951">"แอปทั้งหมด"</string>
<string name="notifications_header" msgid="1404149926117359025">"การแจ้งเตือน"</string>
@@ -103,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-tl/strings.xml b/res/values-tl/strings.xml
index de4f952..a4cd808 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -59,7 +59,6 @@
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Maghanap ng mga app"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"Naglo-load ng mga app…"</string>
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Walang nahanap na app na tumutugma sa \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
- <string name="all_apps_search_market_message" msgid="1366263386197059176">"Maghanap ng higit pang mga app"</string>
<string name="label_application" msgid="8531721983832654978">"App"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Lahat ng app"</string>
<string name="notifications_header" msgid="1404149926117359025">"Mga Notification"</string>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index f41284b..c30c2e5 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -59,7 +59,6 @@
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Uygulamalarda ara"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"Uygulamalar yükleniyor…"</string>
<string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" ile eşleşen uygulama bulunamadı"</string>
- <string name="all_apps_search_market_message" msgid="1366263386197059176">"Başka uygulamalar ara"</string>
<string name="label_application" msgid="8531721983832654978">"Uygulama"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Tüm uygulamalar"</string>
<string name="notifications_header" msgid="1404149926117359025">"Bildirimler"</string>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index 360950e..2f79f23 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -59,7 +59,6 @@
<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_search_market_message" msgid="1366263386197059176">"Шукати ще додатки"</string>
<string name="label_application" msgid="8531721983832654978">"Додаток"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Усі додатки"</string>
<string name="notifications_header" msgid="1404149926117359025">"Сповіщення"</string>
@@ -115,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-ur/strings.xml b/res/values-ur/strings.xml
index e68e43e..3d90187 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -59,7 +59,6 @@
<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_search_market_message" msgid="1366263386197059176">"مزید ایپس تلاش کریں"</string>
<string name="label_application" msgid="8531721983832654978">"ایپ"</string>
<string name="all_apps_label" msgid="5015784846527570951">"سبھی ایپس"</string>
<string name="notifications_header" msgid="1404149926117359025">"اطلاعات"</string>
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
index 9c7ed56..04d07ec 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -59,7 +59,6 @@
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Ilovalarni qidirish"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"Ilovalar yuklanmoqda…"</string>
<string name="all_apps_no_search_results" msgid="3200346862396363786">"“<xliff:g id="QUERY">%1$s</xliff:g>” bilan mos hech qanday ilova topilmadi"</string>
- <string name="all_apps_search_market_message" msgid="1366263386197059176">"Boshqa ilovalarni qidirish"</string>
<string name="label_application" msgid="8531721983832654978">"Ilova"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Barcha ilovalar"</string>
<string name="notifications_header" msgid="1404149926117359025">"Bildirishnomalar"</string>
diff --git a/res/values-v31/colors.xml b/res/values-v31/colors.xml
index 7a75ddb..cf4f000 100644
--- a/res/values-v31/colors.xml
+++ b/res/values-v31/colors.xml
@@ -61,4 +61,6 @@
<color name="preload_icon_background_color_light">@android:color/system_accent2_200</color>
<color name="preload_icon_accent_color_dark">@android:color/system_accent1_300</color>
<color name="preload_icon_background_color_dark">@android:color/system_neutral2_700</color>
+
+ <color name="all_apps_button_color">?android:attr/textColorSecondary</color>
</resources>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index 30167fa..2329992 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -59,7 +59,6 @@
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Tìm kiếm ứng dụng"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"Đang tải ứng dụng…"</string>
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Không tìm thấy ứng dụng nào phù hợp với \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
- <string name="all_apps_search_market_message" msgid="1366263386197059176">"Tìm kiếm thêm ứng dụng"</string>
<string name="label_application" msgid="8531721983832654978">"Ứng dụng"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Tất cả ứng dụng"</string>
<string name="notifications_header" msgid="1404149926117359025">"Thông báo"</string>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 141d165..929cffa 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -59,7 +59,6 @@
<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_search_market_message" msgid="1366263386197059176">"搜索更多应用"</string>
<string name="label_application" msgid="8531721983832654978">"应用"</string>
<string name="all_apps_label" msgid="5015784846527570951">"所有应用"</string>
<string name="notifications_header" msgid="1404149926117359025">"通知"</string>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index 2ff233b..1aea757 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -59,7 +59,6 @@
<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_search_market_message" msgid="1366263386197059176">"搜尋更多應用程式"</string>
<string name="label_application" msgid="8531721983832654978">"應用程式"</string>
<string name="all_apps_label" msgid="5015784846527570951">"所有應用程式"</string>
<string name="notifications_header" msgid="1404149926117359025">"通知"</string>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 91cfd82..25a7d21 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -59,7 +59,6 @@
<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_search_market_message" msgid="1366263386197059176">"搜尋更多應用程式"</string>
<string name="label_application" msgid="8531721983832654978">"應用程式"</string>
<string name="all_apps_label" msgid="5015784846527570951">"所有應用程式"</string>
<string name="notifications_header" msgid="1404149926117359025">"通知"</string>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index 12e8568..151ce27 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -59,7 +59,6 @@
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Sesha izinhlelo zokusebenza"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"Ilayisha izinhlelo zokusebenza..."</string>
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Azikho izinhlelo zokusebenza ezitholiwe ezifana ne-\"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
- <string name="all_apps_search_market_message" msgid="1366263386197059176">"Sesha izinhlelo zokusebenza eziningi"</string>
<string name="label_application" msgid="8531721983832654978">"Uhlelo lokusebenza"</string>
<string name="all_apps_label" msgid="5015784846527570951">"Zonke izinhlelo zokusebenza"</string>
<string name="notifications_header" msgid="1404149926117359025">"Izaziso"</string>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 3b71585..08561d5 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,
@@ -158,9 +155,6 @@
<!-- numHotseatIcons defaults to numColumns, if not specified -->
<attr name="numHotseatIcons" format="integer" />
- <!-- Number of icons to use when shrinking the hotseat size,
- defaults to numHotseatIcons / 2 -->
- <attr name="numShrunkenHotseatIcons" format="integer" />
<!-- Number of icons to use when extending the hotseat size,
defaults to 2 * numHotseatIcons -->
<attr name="numExtendedHotseatIcons" format="integer" />
@@ -182,12 +176,11 @@
<attr name="dbFile" format="string" />
<attr name="defaultLayoutId" format="reference" />
- <attr name="defaultSplitDisplayLayoutId" format="reference" />
<attr name="demoModeLayoutId" format="reference" />
<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 -->
@@ -196,6 +189,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">
@@ -208,6 +213,10 @@
<attr name="c" format="float|dimension" />
</declare-styleable>
+ <declare-styleable name="PersonalWorkSlidingTabStrip">
+ <attr name="alignOnIcon" format="boolean" />
+ </declare-styleable>
+
<declare-styleable name="ProfileDisplayOption">
<attr name="name" />
<attr name="minWidthDps" format="float" />
@@ -284,6 +293,8 @@
<!-- defaults to iconImageSize, if not specified -->
<attr name="allAppsIconSize" format="float" />
<!-- defaults to allAppsIconSize, if not specified -->
+ <attr name="allAppsIconSizeLandscape" format="float" />
+ <!-- defaults to allAppsIconSize, if not specified -->
<attr name="allAppsIconSizeTwoPanelPortrait" format="float" />
<!-- defaults to allAppsIconSize, if not specified -->
<attr name="allAppsIconSizeTwoPanelLandscape" format="float" />
@@ -328,15 +339,6 @@
if not specified -->
<attr name="allAppsBorderSpaceTwoPanelLandscapeVertical" format="float" />
- <!-- defaults to borderSpaceDps, if not specified -->
- <attr name="hotseatBorderSpace" format="float" />
- <!-- defaults to hotseatBorderSpace, if not specified -->
- <attr name="hotseatBorderSpaceLandscape" format="float" />
- <!-- defaults to hotseatBorderSpace, if not specified -->
- <attr name="hotseatBorderSpaceTwoPanelLandscape" format="float" />
- <!-- defaults to hotseatBorderSpace, if not specified -->
- <attr name="hotseatBorderSpaceTwoPanelPortrait" format="float" />
-
<!-- defaults to res.hotseat_bar_bottom_space_default, if not specified -->
<attr name="hotseatBarBottomSpace" format="float" />
<!-- defaults to hotseatBarBottomSpace, if not specified -->
@@ -383,19 +385,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/colors.xml b/res/values/colors.xml
index 309a1c5..9d6927b 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -80,11 +80,7 @@
<color name="workspace_accent_color_light">#ff8df5e3</color>
<color name="workspace_accent_color_dark">#ff3d665f</color>
- <color name="all_apps_button_bg_color">#F7F9FA</color>
- <color name="all_apps_button_color_1">#00677E</color>
- <color name="all_apps_button_color_2">#00677E</color>
- <color name="all_apps_button_color_3">#5F757E</color>
- <color name="all_apps_button_color_4">#005A6E</color>
+ <color name="all_apps_button_color">#40484B</color>
<color name="preload_icon_accent_color_light">#00668B</color>
<color name="preload_icon_background_color_light">#B5CAD7</color>
diff --git a/res/values/config.xml b/res/values/config.xml
index 91f0e5d..016420b 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -49,9 +49,6 @@
<!-- View tag key used to determine if we should fade in the child views.. -->
<string name="popup_container_iterate_children" translatable="false">popup_container_iterate_children</string>
- <!-- config used to determine if header protection is supported in AllApps -->
- <bool name="config_header_protection_supported">false</bool>
-
<!-- Workspace -->
<!-- The duration (in ms) of the fade animation on the object outlines, used when
we are dragging objects around on the home screen. -->
@@ -86,6 +83,7 @@
<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>
+ <string name="widget_holder_factory_class" translatable="false"></string>
<!-- View ID to use for QSB widget -->
<item type="id" name="qsb_widget" />
@@ -157,6 +155,7 @@
<item name="swipe_up_rect_scale_damping_ratio" type="dimen" format="float">0.75</item>
<item name="swipe_up_rect_scale_stiffness" type="dimen" format="float">200</item>
+ <item name="swipe_up_rect_scale_higher_stiffness" type="dimen" format="float">400</item>
<item name="swipe_up_rect_xy_fling_friction" type="dimen" format="float">1.5</item>
@@ -192,4 +191,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 b19e739..d041dfe 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -108,15 +108,14 @@
<dimen name="all_apps_search_bar_content_overlap">24dp</dimen>
<dimen name="all_apps_search_bar_bottom_padding">30dp</dimen>
<dimen name="all_apps_empty_search_message_top_offset">40dp</dimen>
- <dimen name="all_apps_empty_search_bg_top_offset">144dp</dimen>
- <dimen name="all_apps_background_canvas_width">700dp</dimen>
- <dimen name="all_apps_background_canvas_height">475dp</dimen>
<dimen name="all_apps_header_pill_height">48dp</dimen>
<dimen name="all_apps_header_pill_corner_radius">12dp</dimen>
<dimen name="all_apps_header_tab_height">48dp</dimen>
<dimen name="all_apps_tabs_indicator_height">2dp</dimen>
<dimen name="all_apps_header_top_margin">33dp</dimen>
<dimen name="all_apps_header_top_padding">36dp</dimen>
+ <!-- Additional top padding to add when Floating Searchbar is enabled. -->
+ <dimen name="all_apps_additional_top_padding_floating_search">16dp</dimen>
<dimen name="all_apps_header_bottom_padding">14dp</dimen>
<dimen name="all_apps_header_top_adjustment">6dp</dimen>
<dimen name="all_apps_header_bottom_adjustment">4dp</dimen>
@@ -124,6 +123,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>
@@ -131,9 +131,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>
@@ -149,6 +149,8 @@
<!-- Floating action button inside work tab to toggle work profile -->
<dimen name="work_fab_height">56dp</dimen>
<dimen name="work_fab_radius">16dp</dimen>
+ <dimen name="work_fab_icon_size">24dp</dimen>
+ <dimen name="work_fab_text_start_margin">8dp</dimen>
<dimen name="work_card_padding_horizontal">10dp</dimen>
<dimen name="work_card_button_height">52dp</dimen>
<dimen name="work_fab_margin">16dp</dimen>
@@ -229,6 +231,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>
@@ -249,12 +252,15 @@
<!-- Folders -->
<dimen name="page_indicator_dot_size">8dp</dimen>
+ <dimen name="page_indicator_dot_size_v2">6dp</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>
@@ -332,7 +338,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,18 +363,35 @@
<dimen name="taskbar_stashed_size">0dp</dimen>
<dimen name="qsb_widget_height">0dp</dimen>
<dimen name="qsb_shadow_height">0dp</dimen>
- <dimen name="taskbar_icon_size">44dp</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="min_hotseat_icon_space">18dp</dimen>
+ <dimen name="max_hotseat_icon_space">50dp</dimen>
+ <dimen name="min_hotseat_qsb_width">0dp</dimen>
+ <dimen name="taskbar_icon_size">0dp</dimen>
+ <dimen name="transient_taskbar_icon_size">0dp</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>
+ <dimen name="transient_taskbar_clamped_offset_bound">0dp</dimen>
+ <dimen name="taskbar_icon_spacing">0dp</dimen>
<dimen name="taskbar_nav_buttons_size">0dp</dimen>
<dimen name="taskbar_contextual_button_margin">0dp</dimen>
<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_margin_5_5">0dp</dimen>
+ <dimen name="taskbar_button_space_inbetween_phone">0dp</dimen>
+ <dimen name="taskbar_button_margin_split">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>
+ <dimen name="taskbar_nav_threshold_v2">0dp</dimen>
+ <dimen name="taskbar_app_window_threshold_v2">0dp</dimen>
+ <dimen name="taskbar_home_overview_threshold_v2">0dp</dimen>
<!-- Size of the maximum radius for the enforced rounded rectangles. -->
<dimen name="enforced_rounded_corner_max_radius">16dp</dimen>
@@ -381,7 +404,6 @@
<dimen name="task_thumbnail_icon_drawable_size">0dp</dimen>
<dimen name="task_thumbnail_icon_drawable_size_grid">0dp</dimen>
<dimen name="overview_task_margin">0dp</dimen>
- <dimen name="overview_task_margin_grid">0dp</dimen>
<dimen name="overview_actions_height">0dp</dimen>
<dimen name="overview_actions_button_spacing">0dp</dimen>
<dimen name="overview_actions_margin_gesture">0dp</dimen>
@@ -424,4 +446,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/id.xml b/res/values/id.xml
index 9fc0ff8..52a7e98 100644
--- a/res/values/id.xml
+++ b/res/values/id.xml
@@ -37,4 +37,6 @@
<item type="id" name="quick_settings_button" />
<item type="id" name="notifications_button" />
<item type="id" name="cache_entry_tag_id" />
+
+ <item type="id" name="saved_clip_children_tag_id" />
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 2addf50..3eb08ba 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -128,8 +128,6 @@
<string name="all_apps_loading_message">Loading apps…</string>
<!-- No-search-results text. [CHAR_LIMIT=50] -->
<string name="all_apps_no_search_results">No apps found matching \"<xliff:g id="query" example="Android">%1$s</xliff:g>\"</string>
- <!-- Label for the button which allows the user to get app search results. [CHAR_LIMIT=50] -->
- <string name="all_apps_search_market_message">Search for more apps</string>
<!-- Label for an icon representing any generic app. [CHAR_LIMIT=50] -->
<string name="label_application">App</string>
<!-- Label for the header text of the All Apps section in All Apps view, used to separate Predicted Apps and Actions section from All Apps section. [CHAR_LIMIT=50] -->
diff --git a/res/values/styles.xml b/res/values/styles.xml
index a8cbf08..7582a30 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -20,6 +20,7 @@
<resources>
<!-- Launcher theme -->
<style name="BaseLauncherTheme" parent="@android:style/Theme.DeviceDefault.Light">
+ <item name="disabledIconAlpha">.54</item>
<item name="android:colorBackgroundCacheHint">@null</item>
<item name="android:colorEdgeEffect">#FF757575</item>
<item name="android:windowActionBar">false</item>
@@ -33,7 +34,6 @@
<item name="allAppsScrimColor">?android:attr/colorBackgroundFloating</item>
<item name="allappsHeaderProtectionColor">@color/popup_color_tertiary_light</item>
<item name="allAppsNavBarScrimColor">#66FFFFFF</item>
- <item name="allAppsTheme">@style/AllAppsTheme</item>
<item name="popupColorPrimary">@color/popup_color_primary_light</item>
<item name="popupColorSecondary">@color/popup_color_secondary_light</item>
<item name="popupColorTertiary">@color/popup_color_tertiary_light</item>
@@ -61,7 +61,6 @@
<item name="iconOnlyShortcutColor">?android:attr/textColorSecondary</item>
<item name="workProfileOverlayTextColor">#FF212121</item>
<item name="eduHalfSheetBGColor">?android:attr/colorAccent</item>
- <item name="disabledIconAlpha">.54</item>
<item name="workspaceAccentColor">@color/workspace_accent_color_light</item>
<item name="dropTargetHoverTextColor">@color/workspace_text_color_dark</item>
<item name="overviewScrimColor">@color/overview_scrim</item>
@@ -73,14 +72,9 @@
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:statusBarColor">#00000000</item>
<item name="android:navigationBarColor">#00000000</item>
-
-
</style>
- <style name="LauncherTheme.DarkMainColor" parent="@style/LauncherTheme">
- <item name="disabledIconAlpha">.54</item>
-
- </style>
+ <style name="LauncherTheme.DarkMainColor" parent="@style/LauncherTheme" />
<style name="LauncherTheme.DarkText" parent="@style/LauncherTheme">
<item name="workspaceTextColor">@color/workspace_text_color_dark</item>
@@ -102,7 +96,6 @@
<item name="android:colorPrimary">#FF212121</item>
<item name="allAppsScrimColor">?android:attr/colorBackgroundFloating</item>
<item name="allAppsNavBarScrimColor">#80000000</item>
- <item name="allAppsTheme">@style/AllAppsTheme.Dark</item>
<item name="popupColorPrimary">@color/popup_color_primary_dark</item>
<item name="popupColorSecondary">@color/popup_color_secondary_dark</item>
<item name="popupColorTertiary">@color/popup_color_tertiary_dark</item>
@@ -129,9 +122,7 @@
<item name="preloadIconBackgroundColor">@color/preload_icon_background_color_dark</item>
</style>
- <style name="LauncherTheme.Dark.DarkMainColor" parent="@style/LauncherTheme.Dark">
- <item name="disabledIconAlpha">.54</item>
- </style>
+ <style name="LauncherTheme.Dark.DarkMainColor" parent="@style/LauncherTheme.Dark"/>
<style name="LauncherTheme.Dark.DarkText" parent="@style/LauncherTheme.Dark">
<item name="android:colorControlHighlight">#19212121</item>
@@ -207,22 +198,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>
-
- <style name="AllAppsTheme.Dark">
- <item name="disabledIconAlpha">.54</item>
- </style>
-
<style name="BaseIconRoot" parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle"/>
<style name="BaseIconUnBounded" parent="BaseIconRoot">
@@ -292,17 +267,6 @@
<style name="TextTitle" parent="@android:style/TextAppearance.DeviceDefault" />
- <style name="AllAppsEmptySearchBackground">
- <item name="android:colorPrimary">#E0E0E0</item>
- <item name="android:colorControlHighlight">#19BDBDBD</item>
- <item name="android:colorForeground">@color/all_apps_bg_hand_fill</item>
- </style>
- <style name="AllAppsEmptySearchBackground.Dark">
- <item name="android:colorPrimary">#9AA0A6</item>
- <item name="android:colorControlHighlight">#19DFE1E5</item>
- <item name="android:colorForeground">@color/all_apps_bg_hand_fill_dark</item>
- </style>
-
<style name="Button.TopRounded.Bordered" parent="@android:style/Widget.Material.Button">
<item name="android:background">@drawable/button_top_rounded_bordered_ripple</item>
<item name="android:stateListAnimator">@null</item>
@@ -328,4 +292,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/res/xml/default_test2_workspace.xml b/res/xml/default_test2_workspace.xml
new file mode 100644
index 0000000..c560104
--- /dev/null
+++ b/res/xml/default_test2_workspace.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Split display specific version of Launcher3/res/xml/default_workspace_4x4.xml -->
+<favorites xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3" >
+
+ <!-- Hotseat (We use the screen as the position of the item in the hotseat) -->
+ <!-- Dialer Messaging Chrome Camera -->
+ <favorite
+ launcher:container="-101"
+ launcher:screen="0"
+ launcher:x="0"
+ launcher:y="0"
+ launcher:className="com.google.android.dialer.extensions.GoogleDialtactsActivity"
+ launcher:packageName="com.google.android.dialer" />
+
+ <favorite
+ launcher:container="-101"
+ launcher:screen="1"
+ launcher:x="1"
+ launcher:y="0"
+ launcher:className="com.google.android.apps.messaging.ui.ConversationListActivity"
+ launcher:packageName="com.google.android.apps.messaging" />
+
+ <favorite
+ launcher:container="-101"
+ launcher:screen="2"
+ launcher:x="2"
+ launcher:y="0"
+ launcher:className="com.google.android.apps.chrome.Main"
+ launcher:packageName="com.android.chrome" />
+
+ <favorite
+ launcher:container="-101"
+ launcher:screen="3"
+ launcher:x="3"
+ launcher:y="0"
+ launcher:className="com.android.camera.CameraLauncher"
+ launcher:packageName="com.google.android.GoogleCamera" />
+
+ <!-- Bottom row -->
+ <!-- Maps [space] [space] Play -->
+ <favorite
+ launcher:className="com.google.android.maps.MapsActivity"
+ launcher:packageName="com.google.android.apps.maps"
+ launcher:screen="0"
+ launcher:x="0"
+ launcher:y="-1" />
+
+ <favorite
+ launcher:className="com.android.vending.AssetBrowserActivity"
+ launcher:packageName="com.android.vending"
+ launcher:screen="0"
+ launcher:x="3"
+ launcher:y="-1" />
+
+ <!-- TODO: Place weather widget when it's available -->
+
+</favorites>
diff --git a/res/xml/device_profiles.xml b/res/xml/device_profiles.xml
index 0238e7d..c9a44a1 100644
--- a/res/xml/device_profiles.xml
+++ b/res/xml/device_profiles.xml
@@ -57,8 +57,9 @@
launcher:numFolderRows="3"
launcher:numFolderColumns="4"
launcher:numHotseatIcons="4"
+ launcher:numExtendedHotseatIcons="6"
launcher:dbFile="launcher_4_by_4.db"
- launcher:inlineNavButtonsEndSpacing="@dimen/taskbar_button_margin_4_4"
+ launcher:inlineNavButtonsEndSpacing="@dimen/taskbar_button_margin_split"
launcher:defaultLayoutId="@xml/default_workspace_4x4"
launcher:deviceCategory="phone|multi_display" >
@@ -121,8 +122,9 @@
launcher:numFolderRows="4"
launcher:numFolderColumns="4"
launcher:numHotseatIcons="5"
+ launcher:numExtendedHotseatIcons="6"
launcher:dbFile="launcher.db"
- launcher:inlineNavButtonsEndSpacing="@dimen/taskbar_button_margin_5_5"
+ launcher:inlineNavButtonsEndSpacing="@dimen/taskbar_button_margin_split"
launcher:defaultLayoutId="@xml/default_workspace_5x5"
launcher:deviceCategory="phone|multi_display" >
@@ -200,8 +202,6 @@
launcher:allAppsBorderSpaceHorizontal="8"
launcher:allAppsBorderSpaceVertical="16"
launcher:allAppsBorderSpaceLandscape="16"
- launcher:hotseatBorderSpace="58"
- launcher:hotseatBorderSpaceLandscape="50.4"
launcher:hotseatBarBottomSpace="76"
launcher:hotseatBarBottomSpaceLandscape="40"
launcher:canBeDefault="true" />
diff --git a/res/xml/paddings_handhelds.xml b/res/xml/paddings_handhelds.xml
new file mode 100644
index 0000000..b9549a6
--- /dev/null
+++ b/res/xml/paddings_handhelds.xml
@@ -0,0 +1,33 @@
+<?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.
+ -->
+
+<!-- Putting in the test/xml folder gives an error that maxEmptySpace doesn't exist -->
+<device-paddings xmlns:launcher="http://schemas.android.com/apk/res-auto" >
+
+ <device-padding
+ launcher:maxEmptySpace="9999dp">
+ <workspaceTopPadding
+ launcher:a="0.48"
+ launcher:b="0"/>
+ <workspaceBottomPadding
+ launcher:a="0.52"
+ launcher:b="0"/>
+ <hotseatBottomPadding
+ launcher:a="0"
+ launcher:b="0"/>
+ </device-padding>
+</device-paddings>
\ No newline at end of file
diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java
index 21dbc5f..49afc1f 100644
--- a/src/com/android/launcher3/AbstractFloatingView.java
+++ b/src/com/android/launcher3/AbstractFloatingView.java
@@ -63,11 +63,13 @@
TYPE_TASK_MENU,
TYPE_OPTIONS_POPUP,
TYPE_ICON_SURFACE,
+ TYPE_OPTIONS_POPUP_DIALOG,
TYPE_PIN_WIDGET_FROM_EXTERNAL_POPUP,
TYPE_WIDGETS_EDUCATION_DIALOG,
TYPE_TASKBAR_EDUCATION_DIALOG,
TYPE_TASKBAR_ALL_APPS,
- TYPE_OPTIONS_POPUP_DIALOG
+ TYPE_ADD_TO_HOME_CONFIRMATION,
+ TYPE_TASKBAR_OVERLAY_PROXY
})
@Retention(RetentionPolicy.SOURCE)
public @interface FloatingViewType {}
@@ -87,13 +89,14 @@
public static final int TYPE_TASK_MENU = 1 << 11;
public static final int TYPE_OPTIONS_POPUP = 1 << 12;
public static final int TYPE_ICON_SURFACE = 1 << 13;
- public static final int TYPE_OPTIONS_POPUP_DIALOG = 1 << 18;
+ public static final int TYPE_OPTIONS_POPUP_DIALOG = 1 << 14;
- public static final int TYPE_PIN_WIDGET_FROM_EXTERNAL_POPUP = 1 << 14;
- public static final int TYPE_WIDGETS_EDUCATION_DIALOG = 1 << 15;
- 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_PIN_WIDGET_FROM_EXTERNAL_POPUP = 1 << 15;
+ public static final int TYPE_WIDGETS_EDUCATION_DIALOG = 1 << 16;
+ public static final int TYPE_TASKBAR_EDUCATION_DIALOG = 1 << 17;
+ public static final int TYPE_TASKBAR_ALL_APPS = 1 << 18;
+ public static final int TYPE_ADD_TO_HOME_CONFIRMATION = 1 << 19;
+ public static final int TYPE_TASKBAR_OVERLAY_PROXY = 1 << 20;
public static final int TYPE_ALL = TYPE_FOLDER | TYPE_ACTION_POPUP
| TYPE_WIDGETS_BOTTOM_SHEET | TYPE_WIDGET_RESIZE_FRAME | TYPE_WIDGETS_FULL_SHEET
@@ -101,17 +104,15 @@
| TYPE_OPTIONS_POPUP | TYPE_SNACKBAR | TYPE_LISTENER | TYPE_ALL_APPS_EDU
| TYPE_ICON_SURFACE | TYPE_DRAG_DROP_POPUP | TYPE_PIN_WIDGET_FROM_EXTERNAL_POPUP
| TYPE_WIDGETS_EDUCATION_DIALOG | TYPE_TASKBAR_EDUCATION_DIALOG | TYPE_TASKBAR_ALL_APPS
- | TYPE_OPTIONS_POPUP_DIALOG | TYPE_ADD_TO_HOME_CONFIRMATION;
+ | TYPE_OPTIONS_POPUP_DIALOG | TYPE_ADD_TO_HOME_CONFIRMATION
+ | TYPE_TASKBAR_OVERLAY_PROXY;
// Type of popups which should be kept open during launcher rebind
public static final int TYPE_REBIND_SAFE = TYPE_WIDGETS_FULL_SHEET
| TYPE_WIDGETS_BOTTOM_SHEET | TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE
| TYPE_ALL_APPS_EDU | TYPE_ICON_SURFACE | TYPE_WIDGETS_EDUCATION_DIALOG
- | TYPE_TASKBAR_EDUCATION_DIALOG | TYPE_TASKBAR_ALL_APPS | TYPE_OPTIONS_POPUP_DIALOG;
-
- // 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;
+ | TYPE_TASKBAR_EDUCATION_DIALOG | TYPE_TASKBAR_ALL_APPS | TYPE_OPTIONS_POPUP_DIALOG
+ | TYPE_TASKBAR_OVERLAY_PROXY;
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..85bd2d3 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,10 +266,12 @@
}
}
- 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;
+ lp.setCellX(widgetInfo.cellX);
+ lp.setTmpCellX(widgetInfo.cellX);
+ lp.setCellY(widgetInfo.cellY);
+ lp.setTmpCellY(widgetInfo.cellY);
lp.cellHSpan = widgetInfo.spanX;
lp.cellVSpan = widgetInfo.spanY;
lp.isLockedToGrid = true;
@@ -405,7 +408,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,12 +423,12 @@
mDirectionVector[0] = 0;
mDirectionVector[1] = 0;
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) wlp;
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) wlp;
int spanX = lp.cellHSpan;
int spanY = lp.cellVSpan;
- int cellX = lp.useTmpCoords ? lp.tmpCellX : lp.cellX;
- int cellY = lp.useTmpCoords ? lp.tmpCellY : lp.cellY;
+ int cellX = lp.useTmpCoords ? lp.getTmpCellX() : lp.getCellX();
+ int cellY = lp.useTmpCoords ? lp.getTmpCellY() : lp.getCellY();
// For each border, we bound the resizing based on the minimum width, and the maximum
// expandability.
@@ -466,8 +469,8 @@
mLauncher.getString(R.string.widget_resized, spanX, spanY));
}
- lp.tmpCellX = cellX;
- lp.tmpCellY = cellY;
+ lp.setTmpCellX(cellX);
+ lp.setTmpCellY(cellY);
lp.cellHSpan = spanX;
lp.cellVSpan = spanY;
mRunningVInc += vSpanDelta;
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..55ede6c 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;
@@ -32,7 +31,6 @@
import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.Log;
-import android.util.Pair;
import android.util.Patterns;
import android.util.Xml;
@@ -46,8 +44,9 @@
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.qsb.QsbContainerView;
import com.android.launcher3.util.IntArray;
-import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.util.Partner;
import com.android.launcher3.util.Thunk;
+import com.android.launcher3.widget.LauncherWidgetHolder;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -74,21 +73,18 @@
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());
- if (customizationApkInfo == null) {
+ Partner partner = Partner.get(context.getPackageManager(), ACTION_LAUNCHER_CUSTOMIZATION);
+ if (partner == null) {
return null;
}
- String pkg = customizationApkInfo.first;
- Resources targetRes = customizationApkInfo.second;
InvariantDeviceProfile grid = LauncherAppState.getIDP(context);
// Try with grid size and hotseat count
String layoutName = String.format(Locale.ENGLISH, FORMATTED_LAYOUT_RES_WITH_HOSTEAT,
grid.numColumns, grid.numRows, grid.numDatabaseHotseatIcons);
- int layoutId = targetRes.getIdentifier(layoutName, "xml", pkg);
+ int layoutId = partner.getXmlResId(layoutName);
// Try with only grid size
if (layoutId == 0) {
@@ -96,21 +92,21 @@
+ " not found. Trying layout without hosteat");
layoutName = String.format(Locale.ENGLISH, FORMATTED_LAYOUT_RES,
grid.numColumns, grid.numRows);
- layoutId = targetRes.getIdentifier(layoutName, "xml", pkg);
+ layoutId = partner.getXmlResId(layoutName);
}
// Try the default layout
if (layoutId == 0) {
Log.d(TAG, "Formatted layout: " + layoutName + " not found. Trying the default layout");
- layoutId = targetRes.getIdentifier(LAYOUT_RES, "xml", pkg);
+ layoutId = partner.getXmlResId(LAYOUT_RES);
}
if (layoutId == 0) {
- Log.e(TAG, "Layout definition not found in package: " + pkg);
+ Log.e(TAG, "Layout definition not found in package: " + partner.getPackageName());
return null;
}
- return new AutoInstallsLayout(context, appWidgetHost, callback, targetRes, layoutId,
- TAG_WORKSPACE);
+ return new AutoInstallsLayout(context, appWidgetHolder, callback, partner.getResources(),
+ layoutId, TAG_WORKSPACE);
}
// Object Tags
@@ -156,7 +152,7 @@
@Thunk
final Context mContext;
@Thunk
- final AppWidgetHost mAppWidgetHost;
+ final LauncherWidgetHolder mAppWidgetHolder;
protected final LayoutParserCallback mCallback;
protected final PackageManager mPackageManager;
@@ -174,17 +170,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..e71391f 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -25,15 +25,18 @@
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;
+import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.ScrimView;
import java.io.PrintWriter;
@@ -44,8 +47,7 @@
/**
* Launcher BaseActivity
*/
-public abstract class BaseActivity extends Activity implements AppLauncher,
- DeviceProfileListenable {
+public abstract class BaseActivity extends Activity implements ActivityContext {
private static final String TAG = "BaseActivity";
@@ -172,6 +174,12 @@
}
@Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ registerBackDispatcher();
+ }
+
+ @Override
protected void onStart() {
addActivityFlags(ACTIVITY_STATE_STARTED);
super.onStart();
@@ -179,8 +187,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 +218,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
@@ -232,6 +239,17 @@
}
+ protected void registerBackDispatcher() {
+ if (Utilities.ATLEAST_T) {
+ getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
+ OnBackInvokedDispatcher.PRIORITY_DEFAULT,
+ () -> {
+ onBackPressed();
+ TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onBackInvoked");
+ });
+ }
+ }
+
public boolean isStarted() {
return (mActivityFlags & ACTIVITY_STATE_STARTED) != 0;
}
@@ -243,6 +261,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/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
index 6de3884..ca1fe40 100644
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -34,9 +34,6 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.launcher3.allapps.ActivityAllAppsContainerView;
-import com.android.launcher3.allapps.search.DefaultSearchAdapterProvider;
-import com.android.launcher3.allapps.search.SearchAdapterProvider;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.util.ActivityOptionsWrapper;
@@ -212,16 +209,6 @@
return new WindowBounds(new Rect(0, 0, mwSize.x, mwSize.y), new Rect());
}
- /**
- * Creates and returns {@link SearchAdapterProvider} for build variant specific search result
- * views
- */
- @Override
- public SearchAdapterProvider<?> createSearchAdapterProvider(
- ActivityAllAppsContainerView<?> allApps) {
- return new DefaultSearchAdapterProvider(this);
- }
-
@Override
public boolean isAppBlockedForSafeMode() {
return mIsSafeModeEnabled;
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 5fb8925..edbce10 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -16,11 +16,14 @@
package com.android.launcher3;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_DOWNLOAD_APP_UX_V2;
import static com.android.launcher3.config.FeatureFlags.ENABLE_ICON_LABEL_AUTO_SCALING;
import static com.android.launcher3.graphics.PreloadIconDrawable.newPendingIcon;
import static com.android.launcher3.icons.BitmapInfo.FLAG_NO_BADGE;
import static com.android.launcher3.icons.BitmapInfo.FLAG_THEMED;
import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
+import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_INCREMENTAL_DOWNLOAD_ACTIVE;
+import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -291,7 +294,7 @@
@UiThread
public void applyFromWorkspaceItem(WorkspaceItemInfo info, boolean animate, int staggerIndex) {
- applyFromWorkspaceItem(info, false);
+ applyFromWorkspaceItem(info, null);
}
/**
@@ -320,10 +323,10 @@
}
@UiThread
- public void applyFromWorkspaceItem(WorkspaceItemInfo info, boolean promiseStateChanged) {
+ public void applyFromWorkspaceItem(WorkspaceItemInfo info, PreloadIconDrawable icon) {
applyIconAndLabel(info);
setItemInfo(info);
- applyLoadingState(promiseStateChanged);
+ applyLoadingState(icon);
applyDotState(info, false /* animate */);
setDownloadStateContentDescription(info, info.getProgressLevel());
}
@@ -607,15 +610,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());
}
}
@@ -709,23 +713,23 @@
* If this app is installed and downloading incrementally, the progress bar will be updated
* with the total download progress.
*/
- public void applyLoadingState(boolean promiseStateChanged) {
+ public void applyLoadingState(PreloadIconDrawable icon) {
if (getTag() instanceof ItemInfoWithIcon) {
WorkspaceItemInfo info = (WorkspaceItemInfo) getTag();
- if ((info.runtimeStatusFlags & ItemInfoWithIcon.FLAG_INCREMENTAL_DOWNLOAD_ACTIVE)
- != 0) {
- updateProgressBarUi(info.getProgressLevel() == 100);
- } else if (info.hasPromiseIconUi() || (info.runtimeStatusFlags
- & ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0) {
- updateProgressBarUi(promiseStateChanged);
+ if ((info.runtimeStatusFlags & FLAG_INCREMENTAL_DOWNLOAD_ACTIVE) != 0
+ || info.hasPromiseIconUi()
+ || (info.runtimeStatusFlags & FLAG_INSTALL_SESSION_ACTIVE) != 0
+ || (ENABLE_DOWNLOAD_APP_UX_V2.get() && icon != null)) {
+ updateProgressBarUi(icon);
}
}
}
- private void updateProgressBarUi(boolean maybePerformFinishedAnimation) {
+ private void updateProgressBarUi(PreloadIconDrawable oldIcon) {
+ FastBitmapDrawable originalIcon = mIcon;
PreloadIconDrawable preloadDrawable = applyProgressLevel();
- if (preloadDrawable != null && maybePerformFinishedAnimation) {
- preloadDrawable.maybePerformFinishedAnimation();
+ if (preloadDrawable != null && oldIcon != null) {
+ preloadDrawable.maybePerformFinishedAnimation(oldIcon, () -> setIcon(originalIcon));
}
}
@@ -823,12 +827,12 @@
!= 0) {
String percentageString = NumberFormat.getPercentInstance()
.format(progressLevel * 0.01);
- if ((info.runtimeStatusFlags & ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0) {
+ if ((info.runtimeStatusFlags & FLAG_INSTALL_SESSION_ACTIVE) != 0) {
setContentDescription(getContext()
.getString(
R.string.app_installing_title, info.title, percentageString));
} else if ((info.runtimeStatusFlags
- & ItemInfoWithIcon.FLAG_INCREMENTAL_DOWNLOAD_ACTIVE) != 0) {
+ & FLAG_INCREMENTAL_DOWNLOAD_ACTIVE) != 0) {
setContentDescription(getContext()
.getString(
R.string.app_downloading_title, info.title, percentageString));
@@ -942,6 +946,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..ee3e278 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,64 @@
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 (isTextTruncated(availableWidth)) {
+ textSize -= step;
+ if (textSize < minSize) {
+ textSize = minSize;
+ setTextSize(textSize);
+ break;
+ }
+ setTextSize(textSize);
+ }
+ return textSize;
}
}
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 52960a9..05b225c 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, -1);
}
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();
@@ -496,8 +494,8 @@
// Draw reorder drag target.
debugPaint.setColor(Color.RED);
- canvas.drawCircle(cellCenter[0], cellCenter[1], getReorderRadius(targetCell),
- debugPaint);
+ canvas.drawCircle(cellCenter[0], cellCenter[1],
+ getReorderRadius(targetCell, 1, 1), debugPaint);
// Draw folder creation drag target.
if (canCreateFolder) {
@@ -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,14 +617,14 @@
}
}
- 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;
mVisualizeGridPaint.setAlpha(255);
- int x = mDragOutlines[i].cellX;
- int y = mDragOutlines[i].cellY;
+ int x = mDragOutlines[i].getCellX();
+ int y = mDragOutlines[i].getCellY();
int spanX = mDragOutlines[i].cellHSpan;
int spanY = mDragOutlines[i].cellVSpan;
@@ -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) {
@@ -752,7 +764,8 @@
// Generate an id for each view, this assumes we have at most 256x256 cells
// per workspace screen
- if (lp.cellX >= 0 && lp.cellX <= mCountX - 1 && lp.cellY >= 0 && lp.cellY <= mCountY - 1) {
+ if (lp.getCellX() >= 0 && lp.getCellX() <= mCountX - 1
+ && lp.getCellY() >= 0 && lp.getCellY() <= mCountY - 1) {
// If the horizontal or vertical span is set to -1, it is taken to
// mean that it spans the extent of the CellLayout
if (lp.cellHSpan < 0) lp.cellHSpan = mCountX;
@@ -912,21 +925,21 @@
DeviceProfile grid = mActivity.getDeviceProfile();
float iconVisibleRadius = ICON_VISIBLE_AREA_FACTOR * grid.iconSizePx / 2;
// Halfway between reorder radius and icon.
- return (getReorderRadius(targetCell) + iconVisibleRadius) / 2;
+ return (getReorderRadius(targetCell, 1, 1) + iconVisibleRadius) / 2;
}
/**
* Returns the max distance from the center of a cell that will start to reorder on drag over.
*/
- public float getReorderRadius(int[] targetCell) {
+ public float getReorderRadius(int[] targetCell, int spanX, int spanY) {
int[] centerPoint = mTmpPoint;
getWorkspaceCellVisualCenter(targetCell[0], targetCell[1], centerPoint);
Rect cellBoundsWithSpacing = mTempRect;
- cellToRect(targetCell[0], targetCell[1], 1, 1, cellBoundsWithSpacing);
+ 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 +949,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 +1060,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;
@@ -1059,7 +1073,7 @@
if (adjustOccupied) {
GridOccupancy occupied = permanent ? mOccupied : mTmpOccupied;
- occupied.markCells(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, false);
+ occupied.markCells(lp.getCellX(), lp.getCellY(), lp.cellHSpan, lp.cellVSpan, false);
occupied.markCells(cellX, cellY, lp.cellHSpan, lp.cellVSpan, true);
}
@@ -1070,11 +1084,11 @@
final int oldY = lp.y;
lp.isLockedToGrid = true;
if (permanent) {
- lp.cellX = info.cellX = cellX;
- lp.cellY = info.cellY = cellY;
+ lp.setCellX(info.cellX = cellX);
+ lp.setCellY(info.cellY = cellY);
} else {
- lp.tmpCellX = cellX;
- lp.tmpCellY = cellY;
+ lp.setTmpCellX(cellX);
+ lp.setTmpCellY(cellY);
}
clc.setupLp(child);
final int newX = lp.x;
@@ -1153,9 +1167,9 @@
mDragOutlineAnims[oldIndex].animateOut();
mDragOutlineCurrent = (oldIndex + 1) % mDragOutlines.length;
- LayoutParams cell = mDragOutlines[mDragOutlineCurrent];
- cell.cellX = cellX;
- cell.cellY = cellY;
+ CellLayoutLayoutParams cell = mDragOutlines[mDragOutlineCurrent];
+ cell.setCellX(cellX);
+ cell.setCellY(cellY);
cell.cellHSpan = spanX;
cell.cellVSpan = spanY;
@@ -1234,21 +1248,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 +1267,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 +1336,6 @@
hitMaxY |= ySize >= spanY;
incX = !incX;
}
- incX = true;
- hitMaxX = xSize >= spanX;
- hitMaxY = ySize >= spanY;
}
final int[] cellXY = mTmpPoint;
cellToCenterPoint(x, y, cellXY);
@@ -1349,8 +1343,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 +1373,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,11 +1383,11 @@
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;
- lp.tmpCellY = c.cellY;
+ lp.setTmpCellX(c.cellX);
+ lp.setTmpCellY(c.cellY);
lp.cellHSpan = c.spanX;
lp.cellVSpan = c.spanY;
mTmpOccupied.markCells(c, true);
@@ -2082,10 +1431,10 @@
!= 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);
+ mode, lp.getCellX(), lp.getCellY(), c.cellX, c.cellY, c.spanX, c.spanY);
rha.animate();
}
}
@@ -2273,17 +1622,19 @@
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.
if (info != null && child != dragView) {
- final boolean requiresDbUpdate = (info.cellX != lp.tmpCellX
- || info.cellY != lp.tmpCellY || info.spanX != lp.cellHSpan
+ final boolean requiresDbUpdate = (info.cellX != lp.getTmpCellX()
+ || info.cellY != lp.getTmpCellY() || info.spanX != lp.cellHSpan
|| info.spanY != lp.cellVSpan);
- info.cellX = lp.cellX = lp.tmpCellX;
- info.cellY = lp.cellY = lp.tmpCellY;
+ lp.setCellX(lp.getTmpCellX());
+ info.cellX = lp.getTmpCellX();
+ info.cellY = lp.getTmpCellY();
+ lp.setCellY(lp.getTmpCellY());
info.spanX = lp.cellHSpan;
info.spanY = lp.cellVSpan;
@@ -2298,13 +1649,25 @@
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;
}
}
- private ItemConfiguration findConfigurationNoShuffle(int pixelX, int pixelY, int minSpanX, int minSpanY,
- int spanX, int spanY, View dragView, ItemConfiguration solution) {
+ /**
+ * 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
+ */
+ private ItemConfiguration closestEmptySpaceReorder(int pixelX, int pixelY, int minSpanX,
+ int minSpanY, int spanX, int spanY) {
+ ItemConfiguration solution = new ItemConfiguration();
int[] result = new int[2];
int[] resultSpan = new int[2];
findNearestVacantArea(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY, result,
@@ -2322,54 +1685,6 @@
return solution;
}
- /* 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
- the vector between? The center of the dragView and its desired destination? Not quite, as
- this doesn't necessarily coincide with the interaction of the dragView and items occupying
- those cells. Instead we use some heuristics to often lock the vector to up, down, left
- or right, which helps make pushing feel right.
- */
- private void getDirectionVectorForDrop(int dragViewCenterX, int dragViewCenterY, int spanX,
- int spanY, View dragView, int[] resultDirection) {
-
- //TODO(adamcohen) b/151776141 use the items visual center for the direction vector
- int[] targetDestination = new int[2];
-
- findNearestArea(dragViewCenterX, dragViewCenterY, spanX, spanY, targetDestination);
- Rect dragRect = new Rect();
- cellToRect(targetDestination[0], targetDestination[1], spanX, spanY, dragRect);
- dragRect.offset(dragViewCenterX - dragRect.centerX(), dragViewCenterY - dragRect.centerY());
-
- Rect dropRegionRect = new Rect();
- getViewsIntersectingRegion(targetDestination[0], targetDestination[1], spanX, spanY,
- dragView, dropRegionRect, mIntersectingViews);
-
- int dropRegionSpanX = dropRegionRect.width();
- int dropRegionSpanY = dropRegionRect.height();
-
- cellToRect(dropRegionRect.left, dropRegionRect.top, dropRegionRect.width(),
- dropRegionRect.height(), dropRegionRect);
-
- int deltaX = (dropRegionRect.centerX() - dragViewCenterX) / spanX;
- int deltaY = (dropRegionRect.centerY() - dragViewCenterY) / spanY;
-
- if (dropRegionSpanX == mCountX || spanX == mCountX) {
- deltaX = 0;
- }
- if (dropRegionSpanY == mCountY || spanY == mCountY) {
- deltaY = 0;
- }
-
- if (deltaX == 0 && deltaY == 0) {
- // No idea what to do, give a random direction.
- resultDirection[0] = 1;
- resultDirection[1] = 0;
- } else {
- computeDirectionVector(deltaX, deltaY, resultDirection);
- }
- }
-
// 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) {
@@ -2383,8 +1698,10 @@
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);
+ CellLayoutLayoutParams
+ lp = (CellLayoutLayoutParams) child.getLayoutParams();
+ r1.set(lp.getCellX(), lp.getCellY(), lp.getCellX() + lp.cellHSpan,
+ lp.getCellY() + lp.cellVSpan);
if (Rect.intersects(r0, r1)) {
mIntersectingViews.add(child);
if (boundingRect != null) {
@@ -2396,7 +1713,7 @@
boolean isNearestDropLocationOccupied(int pixelX, int pixelY, int spanX, int spanY,
View dragView, int[] result) {
- result = findNearestArea(pixelX, pixelY, spanX, spanY, result);
+ result = findNearestAreaIgnoreOccupied(pixelX, pixelY, spanX, spanY, result);
getViewsIntersectingRegion(result[0], result[1], spanX, spanY, dragView, null,
mIntersectingViews);
return !mIntersectingViews.isEmpty();
@@ -2408,12 +1725,13 @@
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);
+ CellLayoutLayoutParams
+ lp = (CellLayoutLayoutParams) child.getLayoutParams();
+ if (lp.getTmpCellX() != lp.getCellX() || lp.getTmpCellY() != lp.getCellY()) {
+ lp.setTmpCellX(lp.getCellX());
+ lp.setTmpCellY(lp.getCellY());
+ animateChildToPosition(child, lp.getCellX(), lp.getCellY(),
+ REORDER_ANIMATION_DURATION, 0, false, false);
}
}
setItemPlacementDirty(false);
@@ -2451,108 +1769,855 @@
return swapSolution.isSolution;
}
- 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
- result = findNearestArea(pixelX, pixelY, spanX, spanY, result);
+ /**
+ * 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;
- if (resultSpan == null) {
- resultSpan = new int[2];
- }
+ final int countX = mCountX;
+ final int countY = mCountY;
- // 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;
+ 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;
+ }
}
- } else {
- getDirectionVectorForDrop(pixelX, pixelY, spanX, spanY, dragView, mDirectionVector);
- mPreviousReorderDirection[0] = mDirectionVector[0];
- mPreviousReorderDirection[1] = mDirectionVector[1];
}
+ // 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
+ the vector between? The center of the dragView and its desired destination? Not quite, as
+ this doesn't necessarily coincide with the interaction of the dragView and items occupying
+ those cells. Instead we use some heuristics to often lock the vector to up, down, left
+ or right, which helps make pushing feel right.
+ */
+ private void getDirectionVectorForDrop(int dragViewCenterX, int dragViewCenterY, int spanX,
+ int spanY, View dragView, int[] resultDirection) {
+
+ //TODO(adamcohen) b/151776141 use the items visual center for the direction vector
+ int[] targetDestination = new int[2];
+
+ findNearestAreaIgnoreOccupied(dragViewCenterX, dragViewCenterY, spanX, spanY,
+ targetDestination);
+ Rect dragRect = new Rect();
+ cellToRect(targetDestination[0], targetDestination[1], spanX, spanY, dragRect);
+ dragRect.offset(dragViewCenterX - dragRect.centerX(), dragViewCenterY - dragRect.centerY());
+
+ Rect dropRegionRect = new Rect();
+ getViewsIntersectingRegion(targetDestination[0], targetDestination[1], spanX, spanY,
+ dragView, dropRegionRect, mIntersectingViews);
+
+ int dropRegionSpanX = dropRegionRect.width();
+ int dropRegionSpanY = dropRegionRect.height();
+
+ cellToRect(dropRegionRect.left, dropRegionRect.top, dropRegionRect.width(),
+ dropRegionRect.height(), dropRegionRect);
+
+ int deltaX = (dropRegionRect.centerX() - dragViewCenterX) / spanX;
+ int deltaY = (dropRegionRect.centerY() - dragViewCenterY) / spanY;
+
+ if (dropRegionSpanX == mCountX || spanX == mCountX) {
+ deltaX = 0;
+ }
+ if (dropRegionSpanY == mCountY || spanY == mCountY) {
+ deltaY = 0;
+ }
+
+ if (deltaX == 0 && deltaY == 0) {
+ // No idea what to do, give a random direction.
+ resultDirection[0] = 1;
+ resultDirection[1] = 0;
+ } else {
+ computeDirectionVector(deltaX, deltaY, resultDirection);
+ }
+ }
+
+ 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 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();
+ 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);
+ }
+ }
+
+ 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;
+ }
+
+ 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 = findNearestAreaIgnoreOccupied(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);
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
+ CellAndSpan c;
+ if (temp) {
+ c = new CellAndSpan(lp.getTmpCellX(), lp.getTmpCellY(), lp.cellHSpan, lp.cellVSpan);
+ } else {
+ c = new CellAndSpan(lp.getCellX(), lp.getCellY(), lp.cellHSpan, lp.cellVSpan);
+ }
+ solution.add(child, c);
+ }
+ }
+
+ /**
+ * Returns a "reorder" if there is empty space without rearranging anything.
+ *
+ * @param pixelX X coordinate in pixels in the screen
+ * @param pixelY Y coordinate in pixels in the screen
+ * @param spanX horizontal cell span
+ * @param spanY vertical cell span
+ * @param dragView view being dragged in reorder
+ * @return the configuration that represents the found reorder
+ */
+ public ItemConfiguration dropInPlaceSolution(int pixelX, int pixelY, int spanX,
+ int spanY, View dragView) {
+ int[] result = new int[2];
+ if (isNearestDropLocationOccupied(pixelX, pixelY, spanX, spanY, dragView, result)) {
+ result[0] = result[1] = -1;
+ }
+ 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 dropInPlaceSolution = dropInPlaceSolution(pixelX, pixelY, spanX, spanY,
+ dragView);
+
// Find a solution involving pushing / displacing any items in the way
ItemConfiguration swapSolution = findReorderSolution(pixelX, pixelY, minSpanX, minSpanY,
spanX, spanY, mDirectionVector, dragView, true, new ItemConfiguration());
// We attempt the approach which doesn't shuffle views at all
- ItemConfiguration noShuffleSolution = findConfigurationNoShuffle(pixelX, pixelY, minSpanX,
- minSpanY, spanX, spanY, dragView, new ItemConfiguration());
-
- ItemConfiguration finalSolution = null;
+ ItemConfiguration closestSpaceSolution = closestEmptySpaceReorder(pixelX, pixelY, minSpanX,
+ minSpanY, spanX, spanY);
// 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;
- } else if (noShuffleSolution.isSolution) {
- finalSolution = noShuffleSolution;
+ if (swapSolution.isSolution && swapSolution.area() >= closestSpaceSolution.area()) {
+ return swapSolution;
+ } else if (closestSpaceSolution.isSolution) {
+ return closestSpaceSolution;
+ } else if (dropInPlaceSolution.isSolution) {
+ return dropInPlaceSolution;
+ }
+ 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) {
@@ -2619,7 +2684,8 @@
* @return The X, Y cell of a vacant area that can contain this object,
* nearest the requested location.
*/
- public int[] findNearestArea(int pixelX, int pixelY, int spanX, int spanY, int[] result) {
+ public int[] findNearestAreaIgnoreOccupied(int pixelX, int pixelY, int spanX, int spanY,
+ int[] result) {
return findNearestArea(pixelX, pixelY, spanX, spanY, spanX, spanY, true, result, null);
}
@@ -2654,6 +2720,7 @@
*/
void onDragEnter() {
mDragging = true;
+ mPreviousSolution = null;
}
/**
@@ -2668,6 +2735,7 @@
}
// Invalidate the drag data
+ mPreviousSolution = null;
mDragCell[0] = mDragCell[1] = -1;
mDragCellSpan[0] = mDragCellSpan[1] = -1;
mDragOutlineAnims[mDragOutlineCurrent].animateOut();
@@ -2685,7 +2753,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,8 +2796,9 @@
return;
}
if (view == null || view.getParent() != mShortcutsAndWidgets) return;
- LayoutParams lp = (LayoutParams) view.getLayoutParams();
- mOccupied.markCells(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, true);
+ CellLayoutLayoutParams
+ lp = (CellLayoutLayoutParams) view.getLayoutParams();
+ mOccupied.markCells(lp.getCellX(), lp.getCellY(), lp.cellHSpan, lp.cellVSpan, true);
}
public void markCellsAsUnoccupiedForView(View view) {
@@ -2739,8 +2809,9 @@
return;
}
if (view == null || view.getParent() != mShortcutsAndWidgets) return;
- LayoutParams lp = (LayoutParams) view.getLayoutParams();
- mOccupied.markCells(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, false);
+ CellLayoutLayoutParams
+ lp = (CellLayoutLayoutParams) view.getLayoutParams();
+ mOccupied.markCells(lp.getCellX(), lp.getCellY(), lp.cellHSpan, lp.cellVSpan, false);
}
public int getDesiredWidth() {
@@ -2763,165 +2834,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/CheckLongPressHelper.java b/src/com/android/launcher3/CheckLongPressHelper.java
index c707df0..3e4e96b 100644
--- a/src/com/android/launcher3/CheckLongPressHelper.java
+++ b/src/com/android/launcher3/CheckLongPressHelper.java
@@ -16,12 +16,16 @@
package com.android.launcher3;
+import android.os.Handler;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
+import com.android.launcher3.util.TouchUtil;
+
/**
- * Utility class to handle tripper long press on a view with custom timeout and stylus event
+ * Utility class to handle tripper long press or right click on a view with custom timeout and
+ * stylus event
*/
public class CheckLongPressHelper {
@@ -34,6 +38,7 @@
private float mLongPressTimeoutFactor = DEFAULT_LONG_PRESS_TIMEOUT_FACTOR;
private boolean mHasPerformedLongPress;
+ private boolean mIsInMouseRightClick;
private Runnable mPendingCheckForLongPress;
@@ -59,6 +64,26 @@
// start fresh on touch down.
cancelLongPress();
+ // Mouse right click should immediately trigger a long press
+ if (TouchUtil.isMouseRightClickDownOrMove(ev)) {
+ mIsInMouseRightClick = true;
+ triggerLongPress();
+ final Handler handler = mView.getHandler();
+ if (handler != null) {
+ // Send an ACTION_UP to end this click gesture to avoid user dragging with
+ // mouse's right button. Note that we need to call
+ // {@link Handler#postAtFrontOfQueue()} instead of {@link View#post()} to
+ // make sure ACTION_UP is sent before any ACTION_MOVE if user is dragging.
+ final MotionEvent actionUpEvent = MotionEvent.obtain(ev);
+ actionUpEvent.setAction(MotionEvent.ACTION_UP);
+ handler.postAtFrontOfQueue(() -> {
+ mView.getRootView().dispatchTouchEvent(actionUpEvent);
+ actionUpEvent.recycle();
+ });
+ }
+ break;
+ }
+
postCheckForLongPress();
if (isStylusButtonPressed(ev)) {
triggerLongPress();
@@ -70,7 +95,8 @@
cancelLongPress();
break;
case MotionEvent.ACTION_MOVE:
- if (!Utilities.pointInView(mView, ev.getX(), ev.getY(), mSlop)) {
+ if (mIsInMouseRightClick
+ || !Utilities.pointInView(mView, ev.getX(), ev.getY(), mSlop)) {
cancelLongPress();
} else if (mPendingCheckForLongPress != null && isStylusButtonPressed(ev)) {
// Only trigger long press if it has not been cancelled before
@@ -98,9 +124,10 @@
}
/**
- * Cancels any pending long press
+ * Cancels any pending long press and right click
*/
public void cancelLongPress() {
+ mIsInMouseRightClick = false;
mHasPerformedLongPress = false;
clearCallbacks();
}
diff --git a/src/com/android/launcher3/DefaultLayoutParser.java b/src/com/android/launcher3/DefaultLayoutParser.java
index 4daca8b..c69ae4d 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;
@@ -20,7 +19,9 @@
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.shortcuts.ShortcutKey;
+import com.android.launcher3.util.Partner;
import com.android.launcher3.util.Thunk;
+import com.android.launcher3.widget.LauncherWidgetHolder;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -51,13 +52,16 @@
private static final String ATTR_SHORTCUT_ID = "shortcutId";
private static final String ATTR_PACKAGE_NAME = "packageName";
+ public static final String RES_PARTNER_FOLDER = "partner_folder";
+ public static final String RES_PARTNER_DEFAULT_LAYOUT = "partner_default_layout";
+
// TODO: Remove support for this broadcast, instead use widget options to send bind time options
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
@@ -278,10 +282,9 @@
// Folder contents come from an external XML resource
final Partner partner = Partner.get(mPackageManager);
if (partner != null) {
- final Resources partnerRes = partner.getResources();
- final int resId = partnerRes.getIdentifier(Partner.RES_FOLDER,
- "xml", partner.getPackageName());
+ final int resId = partner.getXmlResId(RES_PARTNER_FOLDER);
if (resId != 0) {
+ final Resources partnerRes = partner.getResources();
final XmlPullParser partnerParser = partnerRes.getXml(resId);
beginDocument(partnerParser, TAG_FOLDER);
@@ -336,11 +339,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 +352,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 a19a8e8..8391b91 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -20,39 +20,54 @@
import static com.android.launcher3.InvariantDeviceProfile.INDEX_LANDSCAPE;
import static com.android.launcher3.InvariantDeviceProfile.INDEX_TWO_PANEL_LANDSCAPE;
import static com.android.launcher3.InvariantDeviceProfile.INDEX_TWO_PANEL_PORTRAIT;
-import static com.android.launcher3.testing.shared.ResourceUtils.pxFromDp;
import static com.android.launcher3.Utilities.dpiFromPx;
import static com.android.launcher3.Utilities.pxFromSp;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_MULTI_DISPLAY_PARTIAL_DEPTH;
import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.ICON_OVERLAP_FACTOR;
+import static com.android.launcher3.icons.GraphicsUtils.getShapePath;
+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.graphics.Path;
+import android.content.res.TypedArray;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.util.DisplayMetrics;
+import android.util.SparseArray;
import android.view.Surface;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
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.GraphicsUtils;
import com.android.launcher3.icons.IconNormalizer;
+import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.uioverrides.ApiWrapper;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.DisplayController.Info;
import com.android.launcher3.util.WindowBounds;
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;
+
// Ratio of empty space, qsb should take up to appear visually centered.
private final float mQsbCenterFactor;
@@ -64,6 +79,7 @@
public final boolean isTablet;
public final boolean isPhone;
public final boolean transposeLayoutWithOrientation;
+ public final boolean isMultiDisplay;
public final boolean isTwoPanels;
public final boolean isQsbInline;
@@ -108,11 +124,10 @@
public final int edgeMarginPx;
public final float workspaceContentScale;
- private float mWorkspaceSpringLoadShrunkTop;
- private float mWorkspaceSpringLoadShrunkBottom;
public final int workspaceSpringLoadedMinNextPageVisiblePx;
private final int extraSpace;
+ private int maxEmptySpace;
public int workspaceTopPadding;
public int workspaceBottomPadding;
@@ -137,12 +152,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;
@@ -156,7 +171,7 @@
public int folderChildDrawablePaddingPx;
// Hotseat
- public final int numShownHotseatIcons;
+ public int numShownHotseatIcons;
public int hotseatCellHeightPx;
public final boolean areNavButtonsInline;
// In portrait: size = height, in landscape: size = width
@@ -168,18 +183,29 @@
// Start is the side next to the nav bar, end is the side next to the workspace
public final int hotseatBarSidePaddingStartPx;
public final int hotseatBarSidePaddingEndPx;
+ public int hotseatQsbWidth; // only used when isQsbInline
public final int hotseatQsbHeight;
public final int hotseatQsbVisualHeight;
private final int hotseatQsbShadowHeight;
public int hotseatBorderSpace;
+ private int minHotseatIconSpacePx;
+ private int minHotseatQsbWidthPx;
+ private final int maxHotseatIconSpacePx;
+ private int inlineNavButtonsEndSpacing;
- public int qsbWidth; // only used when isQsbInline
+ // 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;
@@ -191,7 +217,6 @@
// Overview
public int overviewTaskMarginPx;
- public int overviewTaskMarginGridPx;
public int overviewTaskIconSizePx;
public int overviewTaskIconDrawableSizePx;
public int overviewTaskIconDrawableSizeGridPx;
@@ -203,8 +228,11 @@
public int overviewRowSpacing;
public int overviewGridSideMargin;
+ // Split staging
+ public int splitPlaceholderInset;
+
// Widgets
- public final PointF appWidgetScale = new PointF(1.0f, 1.0f);
+ private final ViewScaleProvider mViewScaleProvider;
// Drop Target
public int dropTargetBarSizePx;
@@ -220,33 +248,36 @@
// Insets
private final Rect mInsets = new Rect();
public final Rect workspacePadding = new Rect();
- private final Rect mHotseatBarPadding = new Rect();
// When true, nav bar is on the left side of the screen.
private boolean mIsSeascape;
// Notification dots
- public DotRenderer mDotRendererWorkSpace;
- public DotRenderer mDotRendererAllApps;
+ public final DotRenderer mDotRendererWorkSpace;
+ public final DotRenderer mDotRendererAllApps;
// Taskbar
public boolean isTaskbarPresent;
// Whether Taskbar will inset the bottom of apps by taskbarSize.
public boolean isTaskbarPresentInApps;
public int taskbarSize;
+ public int transientTaskbarSize;
public int stashedTaskbarSize;
+ public int transientTaskbarMargin;
// DragController
public int flingToDeleteThresholdVelocity;
/** TODO: Once we fully migrate to staged split, remove "isMultiWindowMode" */
DeviceProfile(Context context, InvariantDeviceProfile inv, Info info, WindowBounds windowBounds,
- boolean isMultiWindowMode, boolean transposeLayoutWithOrientation,
- boolean useTwoPanels, boolean isGestureMode) {
+ SparseArray<DotRenderer> dotRendererCache, boolean isMultiWindowMode,
+ boolean transposeLayoutWithOrientation, boolean isMultiDisplay, boolean isGestureMode,
+ @NonNull final ViewScaleProvider viewScaleProvider) {
this.inv = inv;
this.isLandscape = windowBounds.isLandscape();
this.isMultiWindowMode = isMultiWindowMode;
this.transposeLayoutWithOrientation = transposeLayoutWithOrientation;
+ this.isMultiDisplay = isMultiDisplay;
this.isGestureMode = isGestureMode;
windowX = windowBounds.bounds.left;
windowY = windowBounds.bounds.top;
@@ -258,7 +289,7 @@
mInfo = info;
isTablet = info.isTablet(windowBounds);
isPhone = !isTablet;
- isTwoPanels = isTablet && useTwoPanels;
+ isTwoPanels = isTablet && isMultiDisplay;
isTaskbarPresent = isTablet && ApiWrapper.TASKBAR_DRAWN_IN_PROCESS;
// Some more constants.
@@ -294,8 +325,16 @@
}
if (isTaskbarPresent) {
- taskbarSize = res.getDimensionPixelSize(R.dimen.taskbar_size);
- stashedTaskbarSize = res.getDimensionPixelSize(R.dimen.taskbar_stashed_size);
+ transientTaskbarSize = res.getDimensionPixelSize(R.dimen.transient_taskbar_size);
+ transientTaskbarMargin = res.getDimensionPixelSize(R.dimen.transient_taskbar_margin);
+ if (DisplayController.isTransientTaskbar(context)) {
+ taskbarSize = transientTaskbarSize;
+ stashedTaskbarSize =
+ res.getDimensionPixelSize(R.dimen.transient_taskbar_stashed_size);
+ } 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);
@@ -311,24 +350,57 @@
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;
+ if (isMultiDisplay && !ENABLE_MULTI_DISPLAY_PARTIAL_DEPTH.get()) {
+ // TODO(b/259893832): Revert to use maxWallpaperScale to calculate bottomSheetDepth
+ // when screen recorder bug is fixed.
+ bottomSheetDepth = 1f;
+ } else {
+ // 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;
+ }
- allAppsTopPadding = isTablet ? bottomSheetTopPadding : 0;
- allAppsShiftRange = isTablet
- ? heightPx - allAppsTopPadding
- : res.getDimensionPixelSize(R.dimen.all_apps_starting_vertical_translate);
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);
@@ -365,17 +437,11 @@
|| inv.inlineQsb[INDEX_TWO_PANEL_LANDSCAPE]
: inv.inlineQsb[INDEX_DEFAULT] || inv.inlineQsb[INDEX_LANDSCAPE])
&& hotseatQsbHeight > 0;
- isQsbInline = inv.inlineQsb[mTypeIndex] && canQsbInline;
+ isQsbInline = isScalableGrid && inv.inlineQsb[mTypeIndex] && canQsbInline;
- // We shrink hotseat sizes regardless of orientation, if nav buttons are inline and QSB
- // might be inline in either orientations, to keep hotseat size consistent across rotation.
areNavButtonsInline = isTaskbarPresent && !isGestureMode;
- if (areNavButtonsInline && canQsbInline) {
- numShownHotseatIcons = inv.numShrunkenHotseatIcons;
- } else {
- numShownHotseatIcons =
- isTwoPanels ? inv.numDatabaseHotseatIcons : inv.numShownHotseatIcons;
- }
+ numShownHotseatIcons =
+ isTwoPanels ? inv.numDatabaseHotseatIcons : inv.numShownHotseatIcons;
numShownAllAppsColumns =
isTwoPanels ? inv.numDatabaseAllAppsColumns : inv.numAllAppsColumns;
@@ -410,51 +476,48 @@
// Add a bit of space between nav bar and hotseat in vertical bar layout.
hotseatBarSidePaddingStartPx = isVerticalBarLayout() ? workspacePageIndicatorHeight : 0;
updateHotseatSizes(pxFromDp(inv.iconSize[INDEX_DEFAULT], mMetrics));
- if (areNavButtonsInline) {
+ if (areNavButtonsInline && !isPhone) {
+ inlineNavButtonsEndSpacing = res.getDimensionPixelSize(inv.inlineNavButtonsEndSpacing);
/*
* 3 nav buttons +
* Spacing between nav buttons +
- * Little space at the end for contextual buttons +
- * Little space between icons and nav buttons
+ * Space at the end for contextual buttons
*/
hotseatBarEndOffset = 3 * res.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size)
+ 2 * res.getDimensionPixelSize(R.dimen.taskbar_button_space_inbetween)
- + res.getDimensionPixelSize(inv.inlineNavButtonsEndSpacing)
- + res.getDimensionPixelSize(R.dimen.taskbar_hotseat_nav_spacing);
+ + inlineNavButtonsEndSpacing;
} else {
+ inlineNavButtonsEndSpacing = 0;
hotseatBarEndOffset = 0;
}
overviewTaskMarginPx = res.getDimensionPixelSize(R.dimen.overview_task_margin);
- overviewTaskMarginGridPx = res.getDimensionPixelSize(R.dimen.overview_task_margin_grid);
overviewTaskIconSizePx = res.getDimensionPixelSize(R.dimen.task_thumbnail_icon_size);
overviewTaskIconDrawableSizePx =
res.getDimensionPixelSize(R.dimen.task_thumbnail_icon_drawable_size);
overviewTaskIconDrawableSizeGridPx =
res.getDimensionPixelSize(R.dimen.task_thumbnail_icon_drawable_size_grid);
- overviewTaskThumbnailTopMarginPx = overviewTaskIconSizePx + overviewTaskMarginPx * 2;
+ overviewTaskThumbnailTopMarginPx = overviewTaskIconSizePx + overviewTaskMarginPx;
overviewActionsTopMarginPx = res.getDimensionPixelSize(R.dimen.overview_actions_top_margin);
overviewPageSpacing = res.getDimensionPixelSize(R.dimen.overview_page_spacing);
overviewActionsButtonSpacing = res.getDimensionPixelSize(
R.dimen.overview_actions_button_spacing);
overviewActionsHeight = res.getDimensionPixelSize(R.dimen.overview_actions_height);
- // Grid task's top margin is only overviewTaskIconSizePx + overviewTaskMarginGridPx, but
- // overviewTaskThumbnailTopMarginPx is applied to all TaskThumbnailView, so exclude the
- // extra margin when calculating row spacing.
- int extraTopMargin = overviewTaskThumbnailTopMarginPx - overviewTaskIconSizePx
- - overviewTaskMarginGridPx;
- overviewRowSpacing = res.getDimensionPixelSize(R.dimen.overview_grid_row_spacing)
- - extraTopMargin;
+ 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);
// Now that we have all of the variables calculated, we can tune certain sizes.
- if (isScalableGrid && inv.devicePaddings != null) {
+ if (isScalableGrid && inv.devicePaddingId != INVALID_RESOURCE_HANDLE) {
// Paddings were created assuming no scaling, so we first unscale the extra space.
int unscaledExtraSpace = (int) (extraSpace / cellScaleToFit);
- DevicePadding padding = inv.devicePaddings.getDevicePadding(unscaledExtraSpace);
+ DevicePaddings devicePaddings = new DevicePaddings(context, inv.devicePaddingId);
+ DevicePadding padding = devicePaddings.getDevicePadding(unscaledExtraSpace);
+ maxEmptySpace = padding.getMaxEmptySpacePx();
int paddingWorkspaceTop = padding.getWorkspaceTopPadding(unscaledExtraSpace);
int paddingWorkspaceBottom = padding.getWorkspaceBottomPadding(unscaledExtraSpace);
@@ -471,25 +534,54 @@
cellLayoutPadding);
updateWorkspacePadding();
+ minHotseatIconSpacePx = res.getDimensionPixelSize(R.dimen.min_hotseat_icon_space);
+ minHotseatQsbWidthPx = res.getDimensionPixelSize(R.dimen.min_hotseat_qsb_width);
+ maxHotseatIconSpacePx = areNavButtonsInline
+ ? res.getDimensionPixelSize(R.dimen.max_hotseat_icon_space) : Integer.MAX_VALUE;
// Hotseat and QSB width depends on updated cellSize and workspace padding
- hotseatBorderSpace = calculateHotseatBorderSpace();
- qsbWidth = calculateQsbWidth();
+ recalculateHotseatWidthAndBorderSpace();
+
+ // AllApps height calculation depends on updated cellSize
+ if (isTablet) {
+ int collapseHandleHeight =
+ res.getDimensionPixelOffset(R.dimen.bottom_sheet_handle_area_height);
+ int contentHeight = heightPx - collapseHandleHeight - hotseatQsbHeight;
+ int targetContentHeight = (int) (allAppsCellHeightPx * ALL_APPS_TABLET_MAX_ROWS);
+ allAppsTopPadding = Math.max(mInsets.top, contentHeight - targetContentHeight);
+ allAppsShiftRange = heightPx - allAppsTopPadding;
+ } else {
+ allAppsTopPadding = 0;
+ 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);
+ mViewScaleProvider = viewScaleProvider;
+
// This is done last, after iconSizePx is calculated above.
- Path dotPath = GraphicsUtils.getShapePath(DEFAULT_DOT_SIZE);
- mDotRendererWorkSpace = new DotRenderer(iconSizePx, dotPath, DEFAULT_DOT_SIZE);
- mDotRendererAllApps = iconSizePx == allAppsIconSizePx ? mDotRendererWorkSpace :
- new DotRenderer(allAppsIconSizePx, dotPath, DEFAULT_DOT_SIZE);
+ mDotRendererWorkSpace = createDotRenderer(iconSizePx, dotRendererCache);
+ mDotRendererAllApps = createDotRenderer(allAppsIconSizePx, dotRendererCache);
+ }
+
+ private static DotRenderer createDotRenderer(
+ int size, @NonNull SparseArray<DotRenderer> cache) {
+ DotRenderer renderer = cache.get(size);
+ if (renderer == null) {
+ renderer = new DotRenderer(size, getShapePath(DEFAULT_DOT_SIZE), DEFAULT_DOT_SIZE);
+ cache.put(size, renderer);
+ }
+ return renderer;
}
/**
* QSB width is always calculated because when in 3 button nav the width doesn't follow the
* width of the hotseat.
*/
- private int calculateQsbWidth() {
+ private int calculateQsbWidth(int hotseatBorderSpace) {
if (isQsbInline) {
int columns = getPanelCount() * inv.numColumns;
return getIconToIconWidthForColumns(columns)
@@ -504,7 +596,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) {
@@ -536,6 +628,59 @@
}
}
+ /**
+ * Calculates the width of the hotseat, changing spaces between the icons and removing icons if
+ * necessary.
+ */
+ public void recalculateHotseatWidthAndBorderSpace() {
+ if (!isScalableGrid) return;
+
+ int columns = inv.hotseatColumnSpan[mTypeIndex];
+ float hotseatWidthPx = getIconToIconWidthForColumns(columns);
+ hotseatBorderSpace = calculateHotseatBorderSpace(hotseatWidthPx, /* numExtraBorder= */ 0);
+ hotseatQsbWidth = calculateQsbWidth(hotseatBorderSpace);
+ // Spaces should be correct when the nav buttons are not inline
+ if (!areNavButtonsInline) {
+ return;
+ }
+
+ // The side space with inline buttons should be what is defined in InvariantDeviceProfile
+ int sideSpace = inlineNavButtonsEndSpacing;
+ int maxHotseatWidth = availableWidthPx - sideSpace - hotseatBarEndOffset;
+ int maxHotseatIconsWidth = maxHotseatWidth - (isQsbInline ? hotseatQsbWidth : 0);
+ hotseatBorderSpace = calculateHotseatBorderSpace(maxHotseatIconsWidth,
+ (isQsbInline ? 1 : 0) + /* border between nav buttons and first icon */ 1);
+
+ if (hotseatBorderSpace >= minHotseatIconSpacePx) {
+ return;
+ }
+
+ // Border space can't be less than the minimum
+ hotseatBorderSpace = minHotseatIconSpacePx;
+ int requiredWidth = getHotseatRequiredWidth();
+
+ // If there is an inline qsb, change its size
+ if (isQsbInline) {
+ hotseatQsbWidth -= requiredWidth - maxHotseatWidth;
+ if (hotseatQsbWidth >= minHotseatQsbWidthPx) {
+ return;
+ }
+
+ // QSB can't be less than the minimum
+ hotseatQsbWidth = minHotseatQsbWidthPx;
+ }
+
+ maxHotseatIconsWidth = maxHotseatWidth - (isQsbInline ? hotseatQsbWidth : 0);
+
+ // If it still doesn't fit, start removing icons
+ do {
+ numShownHotseatIcons--;
+ hotseatBorderSpace = calculateHotseatBorderSpace(maxHotseatIconsWidth,
+ (isQsbInline ? 1 : 0) + /* border between nav buttons and first icon */ 1);
+ } while (hotseatBorderSpace < minHotseatIconSpacePx && numShownHotseatIcons > 1);
+
+ }
+
private Point getCellLayoutBorderSpace(InvariantDeviceProfile idp) {
return getCellLayoutBorderSpace(idp, 1f);
}
@@ -576,10 +721,16 @@
widthPx, heightPx, availableWidthPx, availableHeightPx, rotationHint);
bounds.bounds.offsetTo(windowX, windowY);
bounds.insets.set(mInsets);
+
+ SparseArray<DotRenderer> dotRendererCache = new SparseArray<>();
+ dotRendererCache.put(iconSizePx, mDotRendererWorkSpace);
+ dotRendererCache.put(allAppsIconSizePx, mDotRendererAllApps);
+
return new Builder(context, inv, mInfo)
.setWindowBounds(bounds)
- .setUseTwoPanels(isTwoPanels)
+ .setIsMultiDisplay(isMultiDisplay)
.setMultiWindowMode(isMultiWindowMode)
+ .setDotRendererCache(dotRendererCache)
.setGestureMode(isGestureMode);
}
@@ -596,13 +747,18 @@
.setMultiWindowMode(true)
.build();
- profile.hideWorkspaceLabelsIfNotEnoughSpace();
-
// We use these scales to measure and layout the widgets using their full invariant profile
// sizes and then draw them scaled and centered to fit in their multi-window mode cellspans.
float appWidgetScaleX = (float) profile.getCellSize().x / getCellSize().x;
float appWidgetScaleY = (float) profile.getCellSize().y / getCellSize().y;
- profile.appWidgetScale.set(appWidgetScaleX, appWidgetScaleY);
+ if (appWidgetScaleX != 1 || appWidgetScaleY != 1) {
+ final PointF p = new PointF(appWidgetScaleX, appWidgetScaleY);
+ profile = profile.toBuilder(context)
+ .setViewScaleProvider(i -> p)
+ .build();
+ }
+
+ profile.hideWorkspaceLabelsIfNotEnoughSpace();
return profile;
}
@@ -657,6 +813,11 @@
* Returns the amount of extra (or unused) vertical space.
*/
private int updateAvailableDimensions(Resources res) {
+ float invIconSizeDp = inv.iconSize[mTypeIndex];
+ float invIconTextSizeSp = inv.iconTextSize[mTypeIndex];
+ iconSizePx = Math.max(1, pxFromDp(invIconSizeDp, mMetrics));
+ iconTextSizePx = pxFromSp(invIconTextSizeSp, mMetrics);
+
updateIconSize(1f, res);
updateWorkspacePadding();
@@ -713,20 +874,64 @@
// Workspace
final boolean isVerticalLayout = isVerticalBarLayout();
- float invIconSizeDp = inv.iconSize[mTypeIndex];
- float invIconTextSizeSp = inv.iconTextSize[mTypeIndex];
-
- iconSizePx = Math.max(1, pxFromDp(invIconSizeDp, mMetrics, iconScale));
- iconTextSizePx = (int) (pxFromSp(invIconTextSizeSp, mMetrics) * iconScale);
iconDrawablePaddingPx = (int) (iconDrawablePaddingOriginalPx * iconScale);
-
cellLayoutBorderSpacePx = getCellLayoutBorderSpace(inv, scale);
if (isScalableGrid) {
cellWidthPx = pxFromDp(inv.minCellSize[mTypeIndex].x, mMetrics, scale);
cellHeightPx = pxFromDp(inv.minCellSize[mTypeIndex].y, mMetrics, scale);
- int cellContentHeight = iconSizePx + iconDrawablePaddingPx
- + Utilities.calculateTextHeight(iconTextSizePx);
+
+ if (cellWidthPx < iconSizePx) {
+ // If cellWidth no longer fit iconSize, reduce borderSpace to make cellWidth bigger.
+ int numColumns = getPanelCount() * inv.numColumns;
+ int numBorders = numColumns - 1;
+ int extraWidthRequired = (iconSizePx - cellWidthPx) * numColumns;
+ if (cellLayoutBorderSpacePx.x * numBorders >= extraWidthRequired) {
+ cellWidthPx = iconSizePx;
+ cellLayoutBorderSpacePx.x -= extraWidthRequired / numBorders;
+ } else {
+ // If it still doesn't fit, set borderSpace to 0 and distribute the space for
+ // cellWidth, and reduce iconSize.
+ cellWidthPx = (cellWidthPx * numColumns
+ + cellLayoutBorderSpacePx.x * numBorders) / numColumns;
+ iconSizePx = Math.min(iconSizePx, cellWidthPx);
+ cellLayoutBorderSpacePx.x = 0;
+ }
+ }
+
+ int cellTextAndPaddingHeight =
+ iconDrawablePaddingPx + Utilities.calculateTextHeight(iconTextSizePx);
+ int cellContentHeight = iconSizePx + cellTextAndPaddingHeight;
+ if (cellHeightPx < cellContentHeight) {
+ // If cellHeight no longer fit iconSize, reduce borderSpace to make cellHeight
+ // bigger.
+ int numBorders = inv.numRows - 1;
+ int extraHeightRequired = (cellContentHeight - cellHeightPx) * inv.numRows;
+ if (cellLayoutBorderSpacePx.y * numBorders >= extraHeightRequired) {
+ cellHeightPx = cellContentHeight;
+ cellLayoutBorderSpacePx.y -= extraHeightRequired / numBorders;
+ } else {
+ // If it still doesn't fit, set borderSpace to 0 to recover space.
+ cellHeightPx = (cellHeightPx * inv.numRows
+ + cellLayoutBorderSpacePx.y * numBorders) / inv.numRows;
+ cellLayoutBorderSpacePx.y = 0;
+ // Reduce iconDrawablePaddingPx to make cellContentHeight smaller.
+ int cellContentWithoutPadding = cellContentHeight - iconDrawablePaddingPx;
+ if (cellContentWithoutPadding <= cellHeightPx) {
+ iconDrawablePaddingPx = cellContentHeight - cellHeightPx;
+ } else {
+ // If it still doesn't fit, set iconDrawablePaddingPx to 0 to recover space,
+ // then proportional reduce iconSizePx and iconTextSizePx to fit.
+ iconDrawablePaddingPx = 0;
+ float ratio = cellHeightPx / (float) cellContentWithoutPadding;
+ iconSizePx = (int) (iconSizePx * ratio);
+ iconTextSizePx = (int) (iconTextSizePx * ratio);
+ }
+ cellTextAndPaddingHeight =
+ iconDrawablePaddingPx + Utilities.calculateTextHeight(iconTextSizePx);
+ }
+ cellContentHeight = iconSizePx + cellTextAndPaddingHeight;
+ }
cellYPaddingPx = Math.max(0, cellHeightPx - cellContentHeight) / 2;
desiredWorkspaceHorizontalMarginPx =
(int) (desiredWorkspaceHorizontalMarginOriginalPx * scale);
@@ -757,20 +962,14 @@
}
/**
- * Hotseat width spans a certain number of columns on scalable grids.
- * This method calculates the space between the icons to achieve that width.
+ * This method calculates the space between the icons to achieve a certain width.
*/
- private int calculateHotseatBorderSpace() {
- if (!isScalableGrid) return 0;
- //TODO(http://b/228998082) remove this when 3 button spaces are fixed
- if (areNavButtonsInline) {
- return pxFromDp(inv.hotseatBorderSpaces[mTypeIndex], mMetrics);
- } else {
- int columns = inv.hotseatColumnSpan[mTypeIndex];
- float hotseatWidthPx = getIconToIconWidthForColumns(columns);
- float hotseatIconsTotalPx = iconSizePx * numShownHotseatIcons;
- return (int) (hotseatWidthPx - hotseatIconsTotalPx) / (numShownHotseatIcons - 1);
- }
+ private int calculateHotseatBorderSpace(float hotseatWidthPx, int numExtraBorder) {
+ float hotseatIconsTotalPx = iconSizePx * numShownHotseatIcons;
+ int hotseatBorderSpace =
+ (int) (hotseatWidthPx - hotseatIconsTotalPx)
+ / (numShownHotseatIcons - 1 + numExtraBorder);
+ return Math.min(hotseatBorderSpace, maxHotseatIconSpacePx);
}
@@ -783,17 +982,41 @@
pxFromDp(inv.allAppsBorderSpaces[mTypeIndex].y, mMetrics, scale));
// AllApps cells don't have real space between cells,
// so we add the border space to the cell height
- allAppsCellHeightPx = pxFromDp(inv.allAppsCellSize[mTypeIndex].y, mMetrics, scale)
+ allAppsCellHeightPx = pxFromDp(inv.allAppsCellSize[mTypeIndex].y, mMetrics)
+ allAppsBorderSpacePx.y;
// but width is just the cell,
// the border is added in #updateAllAppsContainerWidth
if (isScalableGrid) {
- allAppsIconSizePx =
- pxFromDp(inv.allAppsIconSize[mTypeIndex], mMetrics, scale);
- allAppsIconTextSizePx =
- pxFromSp(inv.allAppsIconTextSize[mTypeIndex], mMetrics, scale);
+ allAppsIconSizePx = pxFromDp(inv.allAppsIconSize[mTypeIndex], mMetrics);
+ allAppsIconTextSizePx = pxFromSp(inv.allAppsIconTextSize[mTypeIndex], mMetrics);
allAppsIconDrawablePaddingPx = iconDrawablePaddingOriginalPx;
allAppsCellWidthPx = pxFromDp(inv.allAppsCellSize[mTypeIndex].x, mMetrics, scale);
+
+ if (allAppsCellWidthPx < allAppsIconSizePx) {
+ // If allAppsCellWidth no longer fit allAppsIconSize, reduce allAppsBorderSpace to
+ // make allAppsCellWidth bigger.
+ int numBorders = inv.numAllAppsColumns - 1;
+ int extraWidthRequired =
+ (allAppsIconSizePx - allAppsCellWidthPx) * inv.numAllAppsColumns;
+ if (allAppsBorderSpacePx.x * numBorders >= extraWidthRequired) {
+ allAppsCellWidthPx = allAppsIconSizePx;
+ allAppsBorderSpacePx.x -= extraWidthRequired / numBorders;
+ } else {
+ // If it still doesn't fit, set allAppsBorderSpace to 0 and distribute the space
+ // for allAppsCellWidth, and reduce allAppsIconSize.
+ allAppsCellWidthPx = (allAppsCellWidthPx * inv.numAllAppsColumns
+ + allAppsBorderSpacePx.x * numBorders) / inv.numAllAppsColumns;
+ allAppsIconSizePx = Math.min(allAppsIconSizePx, allAppsCellWidthPx);
+ allAppsBorderSpacePx.x = 0;
+ }
+ }
+
+ int cellContentHeight = allAppsIconSizePx
+ + Utilities.calculateTextHeight(allAppsIconTextSizePx) + allAppsBorderSpacePx.y;
+ if (allAppsCellHeightPx < cellContentHeight) {
+ // Increase allAppsCellHeight to fit its content.
+ allAppsCellHeightPx = cellContentHeight;
+ }
} else {
float invIconSizeDp = inv.allAppsIconSize[mTypeIndex];
float invIconTextSizeSp = inv.allAppsIconTextSize[mTypeIndex];
@@ -813,22 +1036,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;
@@ -840,27 +1061,21 @@
}
private void updateFolderCellSize(float scale, Resources res) {
- float invIconSizeDp = isVerticalBarLayout()
- ? inv.iconSize[INDEX_LANDSCAPE]
- : inv.iconSize[INDEX_DEFAULT];
+ float invIconSizeDp = inv.iconSize[mTypeIndex];
folderChildIconSizePx = Math.max(1, pxFromDp(invIconSizeDp, mMetrics, scale));
- folderChildTextSizePx =
- pxFromSp(inv.iconTextSize[INDEX_DEFAULT], mMetrics, scale);
- folderLabelTextSizePx = (int) (folderChildTextSizePx * folderLabelTextScale);
+ folderChildTextSizePx = pxFromSp(inv.iconTextSize[mTypeIndex], mMetrics, scale);
+ 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);
@@ -869,6 +1084,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,
@@ -908,6 +1127,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() {
@@ -918,43 +1144,42 @@
* Gets the space in px from the bottom of last item in the vertical-bar hotseat to the
* bottom of the screen.
*/
- private int getVerticalHotseatLastItemBottomOffset() {
+ private int getVerticalHotseatLastItemBottomOffset(Context context) {
+ Rect hotseatBarPadding = getHotseatLayoutPadding(context);
int cellHeight = calculateCellHeight(
- heightPx - mHotseatBarPadding.top - mHotseatBarPadding.bottom, hotseatBorderSpace,
+ heightPx - hotseatBarPadding.top - hotseatBarPadding.bottom, hotseatBorderSpace,
numShownHotseatIcons);
int extraIconEndSpacing = (cellHeight - iconSizePx) / 2;
- return extraIconEndSpacing + mHotseatBarPadding.bottom;
+ return extraIconEndSpacing + hotseatBarPadding.bottom;
}
/**
* Gets the scaled top of the workspace in px for the spring-loaded edit state.
*/
public float getCellLayoutSpringLoadShrunkTop() {
- mWorkspaceSpringLoadShrunkTop = mInsets.top + dropTargetBarTopMarginPx + dropTargetBarSizePx
+ return mInsets.top + dropTargetBarTopMarginPx + dropTargetBarSizePx
+ dropTargetBarBottomMarginPx;
- return mWorkspaceSpringLoadShrunkTop;
}
/**
* Gets the scaled bottom of the workspace in px for the spring-loaded edit state.
*/
- public float getCellLayoutSpringLoadShrunkBottom() {
+ public float getCellLayoutSpringLoadShrunkBottom(Context context) {
int topOfHotseat = hotseatBarSizePx + springLoadedHotseatBarTopMarginPx;
- mWorkspaceSpringLoadShrunkBottom =
- heightPx - (isVerticalBarLayout() ? getVerticalHotseatLastItemBottomOffset()
- : topOfHotseat);
- return mWorkspaceSpringLoadShrunkBottom;
+ return heightPx - (isVerticalBarLayout()
+ ? getVerticalHotseatLastItemBottomOffset(context) : topOfHotseat);
}
/**
* Gets the scale of the workspace for the spring-loaded edit state.
*/
- public float getWorkspaceSpringLoadScale() {
- float scale = (getCellLayoutSpringLoadShrunkBottom() - getCellLayoutSpringLoadShrunkTop())
- / getCellLayoutHeight();
+ public float getWorkspaceSpringLoadScale(Context context) {
+ float scale =
+ (getCellLayoutSpringLoadShrunkBottom(context) - getCellLayoutSpringLoadShrunkTop())
+ / 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);
@@ -1036,6 +1261,7 @@
* Returns the padding for hotseat view
*/
public Rect getHotseatLayoutPadding(Context context) {
+ Rect hotseatBarPadding = new Rect();
if (isVerticalBarLayout()) {
// The hotseat icons will be placed in the middle of the hotseat cells.
// Changing the hotseatCellHeightPx is not affecting hotseat icon positions
@@ -1049,10 +1275,10 @@
+ diffOverlapFactor), 0);
if (isSeascape()) {
- mHotseatBarPadding.set(mInsets.left + hotseatBarSidePaddingStartPx, paddingTop,
+ hotseatBarPadding.set(mInsets.left + hotseatBarSidePaddingStartPx, paddingTop,
hotseatBarSidePaddingEndPx, paddingBottom);
} else {
- mHotseatBarPadding.set(hotseatBarSidePaddingEndPx, paddingTop,
+ hotseatBarPadding.set(hotseatBarSidePaddingEndPx, paddingTop,
mInsets.right + hotseatBarSidePaddingStartPx, paddingBottom);
}
} else if (isTaskbarPresent) {
@@ -1061,34 +1287,27 @@
int hotseatBarTopPadding =
hotseatBarSizePx - hotseatBarBottomPadding - hotseatCellHeightPx;
- // Push icons to the side
- int additionalQsbSpace = isQsbInline ? qsbWidth + hotseatBorderSpace : 0;
- int requiredWidth = iconSizePx * numShownHotseatIcons
- + hotseatBorderSpace * (numShownHotseatIcons - 1)
- + additionalQsbSpace;
- int hotseatWidth = Math.min(requiredWidth, availableWidthPx - hotseatBarEndOffset);
- int sideSpacing = (availableWidthPx - hotseatWidth) / 2;
+ int hotseatWidth = getHotseatRequiredWidth();
+ int leftSpacing = (availableWidthPx - hotseatWidth) / 2;
+ int rightSpacing = leftSpacing;
+ // Hotseat aligns to the left with nav buttons
+ if (hotseatBarEndOffset > 0) {
+ leftSpacing = inlineNavButtonsEndSpacing;
+ rightSpacing = availableWidthPx - hotseatWidth - leftSpacing + hotseatBorderSpace;
+ }
- mHotseatBarPadding.set(sideSpacing, hotseatBarTopPadding, sideSpacing,
+ hotseatBarPadding.set(leftSpacing, hotseatBarTopPadding, rightSpacing,
hotseatBarBottomPadding);
boolean isRtl = Utilities.isRtl(context.getResources());
if (isRtl) {
- mHotseatBarPadding.right += additionalQsbSpace;
+ hotseatBarPadding.right += getAdditionalQsbSpace();
} else {
- mHotseatBarPadding.left += additionalQsbSpace;
- }
-
- if (hotseatBarEndOffset > sideSpacing) {
- int diff = isRtl
- ? sideSpacing - hotseatBarEndOffset
- : hotseatBarEndOffset - sideSpacing;
- mHotseatBarPadding.left -= diff;
- mHotseatBarPadding.right += diff;
+ hotseatBarPadding.left += getAdditionalQsbSpace();
}
} else if (isScalableGrid) {
- int sideSpacing = (availableWidthPx - qsbWidth) / 2;
- mHotseatBarPadding.set(sideSpacing,
+ int sideSpacing = (availableWidthPx - hotseatQsbWidth) / 2;
+ hotseatBarPadding.set(sideSpacing,
0,
sideSpacing,
getHotseatBarBottomPadding());
@@ -1100,7 +1319,7 @@
float workspaceCellWidth = (float) widthPx / inv.numColumns;
float hotseatCellWidth = (float) widthPx / numShownHotseatIcons;
int hotseatAdjustment = Math.round((workspaceCellWidth - hotseatCellWidth) / 2);
- mHotseatBarPadding.set(
+ hotseatBarPadding.set(
hotseatAdjustment + workspacePadding.left + cellLayoutPaddingPx.left
+ mInsets.left,
0,
@@ -1108,7 +1327,21 @@
+ mInsets.right,
getHotseatBarBottomPadding());
}
- return mHotseatBarPadding;
+ return hotseatBarPadding;
+ }
+
+ private int getAdditionalQsbSpace() {
+ return isQsbInline ? hotseatQsbWidth + hotseatBorderSpace : 0;
+ }
+
+ /**
+ * Calculate how much space the hotseat needs to be shown completely
+ */
+ private int getHotseatRequiredWidth() {
+ int additionalQsbSpace = getAdditionalQsbSpace();
+ return iconSizePx * numShownHotseatIcons
+ + hotseatBorderSpace * (numShownHotseatIcons - (areNavButtonsInline ? 0 : 1))
+ + additionalQsbSpace;
}
/**
@@ -1149,12 +1382,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 transientTaskbarSize + transientTaskbarMargin * 2;
+ }
- 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. */
@@ -1164,6 +1402,19 @@
}
/**
+ * Takes the View and return the scales of width and height depending on the DeviceProfile
+ * specifications
+ *
+ * @param itemInfo The tag of the widget view
+ * @return A PointF instance with the x set to be the scale of width, and y being the scale of
+ * height
+ */
+ @NonNull
+ public PointF getAppWidgetScale(@Nullable final ItemInfo itemInfo) {
+ return mViewScaleProvider.getScaleFromItemInfo(itemInfo);
+ }
+
+ /**
* @return the bounds for which the open folders should be contained within
*/
public Rect getAbsoluteOpenFolderBounds() {
@@ -1246,8 +1497,12 @@
return "\t" + name + ": " + value + "px (" + dpiFromPx(value, mMetrics.densityDpi) + "dp)";
}
- // LINT.IfChange
- public void dump(String prefix, PrintWriter writer) {
+ 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:");
writer.println(prefix + "\t1 dp = " + mMetrics.density + " px");
@@ -1281,7 +1536,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));
@@ -1293,9 +1548,12 @@
cellLayoutBorderSpacePx.x));
writer.println(prefix + pxToDpStr("cellLayoutBorderSpacePx Vertical",
cellLayoutBorderSpacePx.y));
- writer.println(prefix + pxToDpStr("cellLayoutPaddingPx.left", cellLayoutPaddingPx.left));
- writer.println(prefix + pxToDpStr("cellLayoutPaddingPx.top", cellLayoutPaddingPx.top));
- writer.println(prefix + pxToDpStr("cellLayoutPaddingPx.right", cellLayoutPaddingPx.right));
+ writer.println(
+ prefix + pxToDpStr("cellLayoutPaddingPx.left", cellLayoutPaddingPx.left));
+ writer.println(
+ prefix + pxToDpStr("cellLayoutPaddingPx.top", cellLayoutPaddingPx.top));
+ writer.println(
+ prefix + pxToDpStr("cellLayoutPaddingPx.right", cellLayoutPaddingPx.right));
writer.println(
prefix + pxToDpStr("cellLayoutPaddingPx.bottom", cellLayoutPaddingPx.bottom));
@@ -1309,17 +1567,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",
@@ -1345,14 +1609,19 @@
writer.println(prefix + pxToDpStr("hotseatQsbHeight", hotseatQsbHeight));
writer.println(prefix + pxToDpStr("springLoadedHotseatBarTopMarginPx",
springLoadedHotseatBarTopMarginPx));
- writer.println(prefix + pxToDpStr("mHotseatBarPadding.top", mHotseatBarPadding.top));
- writer.println(prefix + pxToDpStr("mHotseatBarPadding.bottom", mHotseatBarPadding.bottom));
- writer.println(prefix + pxToDpStr("mHotseatBarPadding.left", mHotseatBarPadding.left));
- writer.println(prefix + pxToDpStr("mHotseatBarPadding.right", mHotseatBarPadding.right));
+ Rect hotseatLayoutPadding = getHotseatLayoutPadding(context);
+ writer.println(prefix + pxToDpStr("getHotseatLayoutPadding(context).top",
+ hotseatLayoutPadding.top));
+ writer.println(prefix + pxToDpStr("getHotseatLayoutPadding(context).bottom",
+ hotseatLayoutPadding.bottom));
+ writer.println(prefix + pxToDpStr("getHotseatLayoutPadding(context).left",
+ hotseatLayoutPadding.left));
+ writer.println(prefix + pxToDpStr("getHotseatLayoutPadding(context).right",
+ hotseatLayoutPadding.right));
writer.println(prefix + "\tnumShownHotseatIcons: " + numShownHotseatIcons);
writer.println(prefix + pxToDpStr("hotseatBorderSpace", hotseatBorderSpace));
writer.println(prefix + "\tisQsbInline: " + isQsbInline);
- writer.println(prefix + pxToDpStr("qsbWidth", qsbWidth));
+ writer.println(prefix + pxToDpStr("hotseatQsbWidth", hotseatQsbWidth));
writer.println(prefix + "\tisTaskbarPresent:" + isTaskbarPresent);
writer.println(prefix + "\tisTaskbarPresentInApps:" + isTaskbarPresentInApps);
@@ -1370,16 +1639,11 @@
writer.println(prefix + pxToDpStr("extraSpace", extraSpace));
writer.println(prefix + pxToDpStr("unscaled extraSpace", extraSpace / iconScale));
- if (inv.devicePaddings != null) {
- int unscaledExtraSpace = (int) (extraSpace / iconScale);
- writer.println(prefix + pxToDpStr("maxEmptySpace",
- inv.devicePaddings.getDevicePadding(unscaledExtraSpace).getMaxEmptySpacePx()));
- }
+ writer.println(prefix + pxToDpStr("maxEmptySpace", maxEmptySpace));
writer.println(prefix + pxToDpStr("workspaceTopPadding", workspaceTopPadding));
writer.println(prefix + pxToDpStr("workspaceBottomPadding", workspaceBottomPadding));
writer.println(prefix + pxToDpStr("overviewTaskMarginPx", overviewTaskMarginPx));
- writer.println(prefix + pxToDpStr("overviewTaskMarginGridPx", overviewTaskMarginGridPx));
writer.println(prefix + pxToDpStr("overviewTaskIconSizePx", overviewTaskIconSizePx));
writer.println(prefix + pxToDpStr("overviewTaskIconDrawableSizePx",
overviewTaskIconDrawableSizePx));
@@ -1391,6 +1655,8 @@
overviewActionsTopMarginPx));
writer.println(prefix + pxToDpStr("overviewActionsHeight",
overviewActionsHeight));
+ writer.println(prefix + pxToDpStr("overviewActionsClaimedSpaceBelow",
+ getOverviewActionsClaimedSpaceBelow()));
writer.println(prefix + pxToDpStr("overviewActionsButtonSpacing",
overviewActionsButtonSpacing));
writer.println(prefix + pxToDpStr("overviewPageSpacing", overviewPageSpacing));
@@ -1402,30 +1668,27 @@
writer.println(
prefix + pxToDpStr("dropTargetBarBottomMarginPx", dropTargetBarBottomMarginPx));
- writer.println(
- prefix + pxToDpStr("workspaceSpringLoadShrunkTop", mWorkspaceSpringLoadShrunkTop));
- writer.println(prefix + pxToDpStr("workspaceSpringLoadShrunkBottom",
- mWorkspaceSpringLoadShrunkBottom));
+ writer.println(prefix + pxToDpStr("getCellLayoutSpringLoadShrunkTop()",
+ getCellLayoutSpringLoadShrunkTop()));
+ writer.println(prefix + pxToDpStr("getCellLayoutSpringLoadShrunkBottom()",
+ getCellLayoutSpringLoadShrunkBottom(context)));
writer.println(prefix + pxToDpStr("workspaceSpringLoadedMinNextPageVisiblePx",
workspaceSpringLoadedMinNextPageVisiblePx));
- writer.println(
- prefix + pxToDpStr("getWorkspaceSpringLoadScale()", getWorkspaceSpringLoadScale()));
+ writer.println(prefix + pxToDpStr("getWorkspaceSpringLoadScale()",
+ getWorkspaceSpringLoadScale(context)));
writer.println(prefix + pxToDpStr("getCellLayoutHeight()", getCellLayoutHeight()));
writer.println(prefix + pxToDpStr("getCellLayoutWidth()", getCellLayoutWidth()));
}
- // LINT.ThenChange(
- // packages/apps/Launcher3/quickstep/tests/src/com/android/quickstep/DeviceProfilePhoneTest.kt,
- // packages/apps/Launcher3/quickstep/tests/src/com/android/quickstep/DeviceProfileVerticalBarTest.kt,
- // packages/apps/Launcher3/quickstep/tests/src/com/android/quickstep/DeviceProfilePhone3ButtonTest.kt,
- // packages/apps/Launcher3/quickstep/tests/src/com/android/quickstep/DeviceProfileVerticalBar3ButtonTest.kt,
- // packages/apps/Launcher3/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletLandscapeTest.kt,
- // packages/apps/Launcher3/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletPortraitTest.kt,
- // packages/apps/Launcher3/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletLandscape3ButtonTest.kt,
- // packages/apps/Launcher3/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletPortrait3ButtonTest.kt,
- // packages/apps/Launcher3/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelLandscapeTest.kt,
- // packages/apps/Launcher3/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelPortraitTest.kt,
- // packages/apps/Launcher3/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelLandscape3ButtonTest.kt,
- // packages/apps/Launcher3/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelPortrait3ButtonTest.kt)
+
+ /** Returns a reduced representation of this DeviceProfile. */
+ public String toSmallString() {
+ return "isTablet:" + isTablet + ", "
+ + "isMultiDisplay:" + isMultiDisplay + ", "
+ + "widthPx:" + widthPx + ", "
+ + "heightPx:" + heightPx + ", "
+ + "insets:" + mInsets + ", "
+ + "rotationHint:" + rotationHint;
+ }
private static Context getContext(Context c, Info info, int orientation, WindowBounds bounds) {
Configuration config = new Configuration(c.getResources().getConfiguration());
@@ -1449,33 +1712,20 @@
void onDeviceProfileChanged(DeviceProfile dp);
}
- /** Allows registering listeners for {@link DeviceProfile} changes. */
- public interface DeviceProfileListenable {
-
- /** The current device profile. */
- DeviceProfile getDeviceProfile();
-
- /** Registered {@link OnDeviceProfileChangeListener} instances. */
- List<OnDeviceProfileChangeListener> getOnDeviceProfileChangeListeners();
-
- /** Notifies listeners of a {@link DeviceProfile} change. */
- default void dispatchDeviceProfileChanged() {
- DeviceProfile deviceProfile = getDeviceProfile();
- List<OnDeviceProfileChangeListener> listeners = getOnDeviceProfileChangeListeners();
- for (int i = listeners.size() - 1; i >= 0; i--) {
- listeners.get(i).onDeviceProfileChanged(deviceProfile);
- }
- }
-
- /** Register listener for {@link DeviceProfile} changes. */
- default void addOnDeviceProfileChangeListener(OnDeviceProfileChangeListener listener) {
- getOnDeviceProfileChangeListeners().add(listener);
- }
-
- /** Unregister listener for {@link DeviceProfile} changes. */
- default void removeOnDeviceProfileChangeListener(OnDeviceProfileChangeListener listener) {
- getOnDeviceProfileChangeListeners().remove(listener);
- }
+ /**
+ * Handler that deals with ItemInfo of the views for the DeviceProfile
+ */
+ @FunctionalInterface
+ public interface ViewScaleProvider {
+ /**
+ * Get the scales from the view
+ *
+ * @param itemInfo The tag of the widget view
+ * @return PointF instance containing the scale information, or null if using the default
+ * app widget scale of this device profile.
+ */
+ @NonNull
+ PointF getScaleFromItemInfo(@Nullable ItemInfo itemInfo);
}
public static class Builder {
@@ -1484,11 +1734,14 @@
private Info mInfo;
private WindowBounds mWindowBounds;
- private boolean mUseTwoPanels;
+ private boolean mIsMultiDisplay;
private boolean mIsMultiWindowMode = false;
private Boolean mTransposeLayoutWithOrientation;
private Boolean mIsGestureMode;
+ private ViewScaleProvider mViewScaleProvider = null;
+
+ private SparseArray<DotRenderer> mDotRendererCache;
public Builder(Context context, InvariantDeviceProfile inv, Info info) {
mContext = context;
@@ -1501,11 +1754,15 @@
return this;
}
- public Builder setUseTwoPanels(boolean useTwoPanels) {
- mUseTwoPanels = useTwoPanels;
+ public Builder setIsMultiDisplay(boolean isMultiDisplay) {
+ mIsMultiDisplay = isMultiDisplay;
return this;
}
+ public Builder setDotRendererCache(SparseArray<DotRenderer> dotRendererCache) {
+ mDotRendererCache = dotRendererCache;
+ return this;
+ }
public Builder setWindowBounds(WindowBounds bounds) {
mWindowBounds = bounds;
@@ -1522,6 +1779,19 @@
return this;
}
+ /**
+ * Set the viewScaleProvider for the builder
+ *
+ * @param viewScaleProvider The viewScaleProvider to be set for the
+ * DeviceProfile
+ * @return This builder
+ */
+ @NonNull
+ public Builder setViewScaleProvider(@Nullable ViewScaleProvider viewScaleProvider) {
+ mViewScaleProvider = viewScaleProvider;
+ return this;
+ }
+
public DeviceProfile build() {
if (mWindowBounds == null) {
throw new IllegalArgumentException("Window bounds not set");
@@ -1530,10 +1800,17 @@
mTransposeLayoutWithOrientation = !mInfo.isTablet(mWindowBounds);
}
if (mIsGestureMode == null) {
- mIsGestureMode = DisplayController.getNavigationMode(mContext).hasGestures;
+ mIsGestureMode = mInfo.navigationMode.hasGestures;
}
- return new DeviceProfile(mContext, mInv, mInfo, mWindowBounds, mIsMultiWindowMode,
- mTransposeLayoutWithOrientation, mUseTwoPanels, mIsGestureMode);
+ if (mDotRendererCache == null) {
+ mDotRendererCache = new SparseArray<>();
+ }
+ if (mViewScaleProvider == null) {
+ mViewScaleProvider = DEFAULT_PROVIDER;
+ }
+ return new DeviceProfile(mContext, mInv, mInfo, mWindowBounds, mDotRendererCache,
+ mIsMultiWindowMode, mTransposeLayoutWithOrientation, mIsMultiDisplay,
+ mIsGestureMode, mViewScaleProvider);
}
}
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 3eb2ee0..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();
- 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);
}
@@ -232,7 +246,7 @@
DeviceProfile dp = mLauncher.getDeviceProfile();
// Center vertical bar over scaled workspace, accounting for hotseat offset.
- float scale = dp.getWorkspaceSpringLoadScale();
+ float scale = dp.getWorkspaceSpringLoadScale(mLauncher);
Workspace<?> ws = mLauncher.getWorkspace();
int barCenter;
if (dp.isTwoPanels) {
diff --git a/src/com/android/launcher3/ExtendedEditText.java b/src/com/android/launcher3/ExtendedEditText.java
index 4629ca7..f94a3c5 100644
--- a/src/com/android/launcher3/ExtendedEditText.java
+++ b/src/com/android/launcher3/ExtendedEditText.java
@@ -15,9 +15,10 @@
*/
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.graphics.Rect;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.DragEvent;
@@ -27,14 +28,17 @@
import com.android.launcher3.views.ActivityContext;
+import java.util.HashSet;
+import java.util.Set;
+
/**
* The edit text that reports back when the back key has been pressed.
* Note: AppCompatEditText doesn't fully support #displayCompletions and #onCommitCompletion
*/
public class ExtendedEditText extends EditText {
+ private final Set<OnFocusChangeListener> mOnFocusChangeListeners = new HashSet<>();
- private boolean mShowImeAfterFirstLayout;
private boolean mForceDisableSuggestions = false;
/**
@@ -85,28 +89,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)
@@ -138,4 +135,38 @@
setText("");
}
}
+
+ /**
+ * This method should be preferred to {@link #setOnFocusChangeListener(OnFocusChangeListener)},
+ * as it allows for multiple listeners from different sources.
+ */
+ public void addOnFocusChangeListener(OnFocusChangeListener listener) {
+ mOnFocusChangeListeners.add(listener);
+ }
+
+ /**
+ * Removes the given listener from the set of registered focus listeners, or does nothing if it
+ * wasn't registered in the first place.
+ */
+ public void removeOnFocusChangeListener(OnFocusChangeListener listener) {
+ mOnFocusChangeListeners.remove(listener);
+ }
+
+ @Override
+ protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
+ super.onFocusChanged(focused, direction, previouslyFocusedRect);
+ for (OnFocusChangeListener listener : mOnFocusChangeListeners) {
+ listener.onFocusChange(this, focused);
+ }
+ }
+
+ /**
+ * Save the input, suggestion, hint states when it's on focus, and set to unfocused states.
+ */
+ public void saveFocusedStateAndUpdateToUnfocusedState() {}
+
+ /**
+ * Restore to the previous saved focused state.
+ */
+ public void restoreToFocusedState() {}
}
diff --git a/src/com/android/launcher3/FastScrollRecyclerView.java b/src/com/android/launcher3/FastScrollRecyclerView.java
index 94903f2..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;
@@ -57,7 +56,9 @@
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- bindFastScrollbar();
+ if (mScrollbar == null || !mScrollbar.hasRecyclerView()) {
+ bindFastScrollbar();
+ }
}
public void bindFastScrollbar() {
@@ -90,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);
}
@@ -144,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;
}
/**
@@ -158,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/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index 05ed319..bf492a9 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -47,7 +47,6 @@
private Consumer<Boolean> mOnVisibilityAggregatedCallback;
private final View mQsb;
- private final int mQsbHeight;
public Hotseat(Context context) {
this(context, null);
@@ -62,8 +61,6 @@
mQsb = LayoutInflater.from(context).inflate(R.layout.search_container_hotseat, this, false);
addView(mQsb);
-
- mQsbHeight = getResources().getDimensionPixelSize(R.dimen.qsb_widget_height);
}
/**
@@ -171,29 +168,29 @@
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- int qsbWidth = mActivity.getDeviceProfile().qsbWidth;
-
- mQsb.measure(MeasureSpec.makeMeasureSpec(qsbWidth, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(mQsbHeight, MeasureSpec.EXACTLY));
+ DeviceProfile dp = mActivity.getDeviceProfile();
+ mQsb.measure(MeasureSpec.makeMeasureSpec(dp.hotseatQsbWidth, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(dp.hotseatQsbHeight, MeasureSpec.EXACTLY));
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
- int qsbWidth = mQsb.getMeasuredWidth();
+ int qsbMeasuredWidth = mQsb.getMeasuredWidth();
int left;
- if (mActivity.getDeviceProfile().isQsbInline) {
- int qsbSpace = mActivity.getDeviceProfile().hotseatBorderSpace;
+ DeviceProfile dp = mActivity.getDeviceProfile();
+ if (dp.isQsbInline) {
+ int qsbSpace = dp.hotseatBorderSpace;
left = Utilities.isRtl(getResources()) ? r - getPaddingRight() + qsbSpace
- : l + getPaddingLeft() - qsbWidth - qsbSpace;
+ : l + getPaddingLeft() - qsbMeasuredWidth - qsbSpace;
} else {
- left = (r - l - qsbWidth) / 2;
+ left = (r - l - qsbMeasuredWidth) / 2;
}
- int right = left + qsbWidth;
+ int right = left + qsbMeasuredWidth;
- int bottom = b - t - mActivity.getDeviceProfile().getQsbOffsetY();
- int top = bottom - mQsbHeight;
+ int bottom = b - t - dp.getQsbOffsetY();
+ int top = bottom - dp.hotseatQsbHeight;
mQsb.layout(left, top, right, bottom);
}
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 9b01734..ffe81ad 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,11 +45,14 @@
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.annotation.XmlRes;
import androidx.core.content.res.ResourcesCompat;
+import com.android.launcher3.icons.DotRenderer;
import com.android.launcher3.model.DeviceGridState;
import com.android.launcher3.provider.RestoreDbTask;
import com.android.launcher3.testing.shared.ResourceUtils;
@@ -55,6 +60,7 @@
import com.android.launcher3.util.DisplayController.Info;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.launcher3.util.Partner;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.WindowBounds;
import com.android.launcher3.util.window.WindowManagerProxy;
@@ -63,6 +69,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;
@@ -105,6 +113,11 @@
static final int INDEX_TWO_PANEL_PORTRAIT = 2;
static final int INDEX_TWO_PANEL_LANDSCAPE = 3;
+ /** These resources are used to override the device profile */
+ private static final String RES_GRID_NUM_ROWS = "grid_num_rows";
+ private static final String RES_GRID_NUM_COLUMNS = "grid_num_columns";
+ private static final String RES_GRID_ICON_SIZE_DP = "grid_icon_size_dp";
+
/**
* Number of icons per row and column in the workspace.
*/
@@ -126,9 +139,9 @@
public PointF[] minCellSize;
public PointF[] borderSpaces;
- public float folderBorderSpace;
- public float[] hotseatBorderSpaces;
- public int inlineNavButtonsEndSpacing;
+ public @DimenRes int inlineNavButtonsEndSpacing;
+
+ public @StyleRes int folderStyle;
public float[] horizontalMargin;
@@ -145,11 +158,6 @@
public int numShownHotseatIcons;
/**
- * Number of icons inside the hotseat area when using 3 buttons navigation.
- */
- public int numShrunkenHotseatIcons;
-
- /**
* Number of icons inside the hotseat area that is stored in the database. This is greater than
* or equal to numnShownHotseatIcons, allowing for a seamless transition between two hotseat
* sizes that share the same DB.
@@ -170,21 +178,19 @@
* Do not query directly. see {@link DeviceProfile#isScalableGrid}.
*/
protected boolean isScalable;
- public int devicePaddingId;
+ @XmlRes
+ public int devicePaddingId = INVALID_RESOURCE_HANDLE;
public String dbFile;
public int defaultLayoutId;
int demoModeLayoutId;
- boolean[] inlineQsb = new boolean[COUNT_SIZES];
+ public boolean[] inlineQsb = new boolean[COUNT_SIZES];
/**
* An immutable list of supported profiles.
*/
public List<DeviceProfile> supportedProfiles = Collections.EMPTY_LIST;
- @Nullable
- public DevicePaddings devicePaddings;
-
public Point defaultWallpaperSize;
public Rect defaultWidgetPadding;
@@ -198,7 +204,8 @@
String gridName = getCurrentGridName(context);
String newGridName = initGrid(context, gridName);
if (!newGridName.equals(gridName)) {
- Utilities.getPrefs(context).edit().putString(KEY_IDP_GRID_NAME, newGridName).apply();
+ LauncherPrefs.getPrefs(context).edit().putString(KEY_IDP_GRID_NAME, newGridName)
+ .apply();
}
new DeviceGridState(this).writeToPrefs(context);
@@ -260,8 +267,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);
}
@@ -308,8 +313,7 @@
}
public static String getCurrentGridName(Context context) {
- return Utilities.isGridOptionsEnabled(context)
- ? Utilities.getPrefs(context).getString(KEY_IDP_GRID_NAME, null) : null;
+ return LauncherPrefs.getPrefs(context).getString(KEY_IDP_GRID_NAME, null);
}
private String initGrid(Context context, String gridName) {
@@ -325,6 +329,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();
@@ -335,8 +344,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;
@@ -358,16 +370,13 @@
minCellSize = displayOption.minCellSize;
borderSpaces = displayOption.borderSpaces;
- folderBorderSpace = displayOption.folderBorderSpace;
horizontalMargin = displayOption.horizontalMargin;
numShownHotseatIcons = closestProfile.numHotseatIcons;
- numShrunkenHotseatIcons = closestProfile.numShrunkenHotseatIcons;
numDatabaseHotseatIcons = deviceType == TYPE_MULTI_DISPLAY
? closestProfile.numDatabaseHotseatIcons : closestProfile.numHotseatIcons;
hotseatColumnSpan = closestProfile.hotseatColumnSpan;
- hotseatBorderSpaces = displayOption.hotseatBorderSpaces;
hotseatBarBottomSpace = displayOption.hotseatBarBottomSpace;
hotseatQsbSpace = displayOption.hotseatQsbSpace;
@@ -379,16 +388,8 @@
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
@@ -396,10 +397,12 @@
final List<DeviceProfile> localSupportedProfiles = new ArrayList<>();
defaultWallpaperSize = new Point(displayInfo.currentSize);
+ SparseArray<DotRenderer> dotRendererCache = new SparseArray<>();
for (WindowBounds bounds : displayInfo.supportedBounds) {
localSupportedProfiles.add(new DeviceProfile.Builder(context, this, displayInfo)
- .setUseTwoPanels(deviceType == TYPE_MULTI_DISPLAY)
+ .setIsMultiDisplay(deviceType == TYPE_MULTI_DISPLAY)
.setWindowBounds(bounds)
+ .setDotRendererCache(dotRendererCache)
.build());
// Wallpaper size should be the maximum of the all possible sizes Launcher expects
@@ -419,6 +422,21 @@
}
supportedProfiles = Collections.unmodifiableList(localSupportedProfiles);
+ int numMinShownHotseatIconsForTablet = supportedProfiles
+ .stream()
+ .filter(deviceProfile -> deviceProfile.isTablet)
+ .mapToInt(deviceProfile -> deviceProfile.numShownHotseatIcons)
+ .min()
+ .orElse(0);
+
+ supportedProfiles
+ .stream()
+ .filter(deviceProfile -> deviceProfile.isTablet)
+ .forEach(deviceProfile -> {
+ deviceProfile.numShownHotseatIcons = numMinShownHotseatIconsForTablet;
+ deviceProfile.recalculateHotseatWidthAndBorderSpace();
+ });
+
ComponentName cn = new ComponentName(context.getPackageName(), getClass().getName());
defaultWidgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(context, cn, null);
}
@@ -434,7 +452,7 @@
public void setCurrentGrid(Context context, String gridName) {
Context appContext = context.getApplicationContext();
- Utilities.getPrefs(appContext).edit().putString(KEY_IDP_GRID_NAME, gridName).apply();
+ LauncherPrefs.getPrefs(appContext).edit().putString(KEY_IDP_GRID_NAME, gridName).apply();
MAIN_EXECUTOR.execute(() -> onConfigChanged(appContext));
}
@@ -570,8 +588,24 @@
*/
private void applyPartnerDeviceProfileOverrides(Context context, DisplayMetrics dm) {
Partner p = Partner.get(context.getPackageManager());
- if (p != null) {
- p.applyInvariantDeviceProfileOverrides(this, dm);
+ if (p == null) {
+ return;
+ }
+ try {
+ int numRows = p.getIntValue(RES_GRID_NUM_ROWS, -1);
+ int numColumns = p.getIntValue(RES_GRID_NUM_COLUMNS, -1);
+ float iconSizePx = p.getDimenValue(RES_GRID_ICON_SIZE_DP, -1);
+
+ if (numRows > 0 && numColumns > 0) {
+ this.numRows = numRows;
+ this.numColumns = numColumns;
+ }
+ if (iconSizePx > 0) {
+ this.iconSize[InvariantDeviceProfile.INDEX_DEFAULT] =
+ Utilities.dpiFromPx(iconSizePx, dm.densityDpi);
+ }
+ } catch (Resources.NotFoundException ex) {
+ Log.e(TAG, "Invalid Partner grid resource!", ex);
}
}
@@ -642,6 +676,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);
}
@@ -722,6 +768,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;
@@ -730,16 +782,18 @@
private final int numFolderRows;
private final int numFolderColumns;
+ private final @StyleRes int folderStyle;
private final int numAllAppsColumns;
private final int numDatabaseAllAppsColumns;
private final int numHotseatIcons;
- private final int numShrunkenHotseatIcons;
private final int numDatabaseHotseatIcons;
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;
@@ -760,10 +814,8 @@
R.styleable.GridDisplayOption_numSearchContainerColumns, numColumns);
dbFile = a.getString(R.styleable.GridDisplayOption_dbFile);
- defaultLayoutId = a.getResourceId(deviceType == TYPE_MULTI_DISPLAY && a.hasValue(
- R.styleable.GridDisplayOption_defaultSplitDisplayLayoutId)
- ? R.styleable.GridDisplayOption_defaultSplitDisplayLayoutId
- : R.styleable.GridDisplayOption_defaultLayoutId, 0);
+ defaultLayoutId = a.getResourceId(
+ R.styleable.GridDisplayOption_defaultLayoutId, 0);
demoModeLayoutId = a.getResourceId(
R.styleable.GridDisplayOption_demoModeLayoutId, defaultLayoutId);
@@ -774,8 +826,6 @@
numHotseatIcons = a.getInt(
R.styleable.GridDisplayOption_numHotseatIcons, numColumns);
- numShrunkenHotseatIcons = a.getInt(
- R.styleable.GridDisplayOption_numShrunkenHotseatIcons, numHotseatIcons / 2);
numDatabaseHotseatIcons = a.getInt(
R.styleable.GridDisplayOption_numExtendedHotseatIcons, 2 * numHotseatIcons);
@@ -793,15 +843,19 @@
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(
- R.styleable.GridDisplayOption_devicePaddingId, 0);
+ R.styleable.GridDisplayOption_devicePaddingId, INVALID_RESOURCE_HANDLE);
int deviceCategory = a.getInt(R.styleable.GridDisplayOption_deviceCategory,
DEVICE_CATEGORY_ALL);
@@ -813,6 +867,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));
@@ -821,26 +888,16 @@
@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];
- //TODO(http://b/228998082) remove this when 3 button spaces are fixed
- private final float[] hotseatBorderSpaces = new float[COUNT_SIZES];
private final float[] hotseatBarBottomSpace = new float[COUNT_SIZES];
private final float[] hotseatQsbSpace = new float[COUNT_SIZES];
@@ -862,19 +919,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;
@@ -934,8 +978,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,
@@ -1014,7 +1056,9 @@
allAppsIconSizes[INDEX_DEFAULT] = a.getFloat(
R.styleable.ProfileDisplayOption_allAppsIconSize, iconSizes[INDEX_DEFAULT]);
- allAppsIconSizes[INDEX_LANDSCAPE] = allAppsIconSizes[INDEX_DEFAULT];
+ allAppsIconSizes[INDEX_LANDSCAPE] = a.getFloat(
+ R.styleable.ProfileDisplayOption_allAppsIconSizeLandscape,
+ allAppsIconSizes[INDEX_DEFAULT]);
allAppsIconSizes[INDEX_TWO_PANEL_PORTRAIT] = a.getFloat(
R.styleable.ProfileDisplayOption_allAppsIconSizeTwoPanelPortrait,
allAppsIconSizes[INDEX_DEFAULT]);
@@ -1056,18 +1100,6 @@
R.styleable.ProfileDisplayOption_horizontalMarginTwoPanelPortrait,
horizontalMargin[INDEX_DEFAULT]);
- hotseatBorderSpaces[INDEX_DEFAULT] = a.getFloat(
- R.styleable.ProfileDisplayOption_hotseatBorderSpace, borderSpace);
- hotseatBorderSpaces[INDEX_LANDSCAPE] = a.getFloat(
- R.styleable.ProfileDisplayOption_hotseatBorderSpaceLandscape,
- hotseatBorderSpaces[INDEX_DEFAULT]);
- hotseatBorderSpaces[INDEX_TWO_PANEL_LANDSCAPE] = a.getFloat(
- R.styleable.ProfileDisplayOption_hotseatBorderSpaceTwoPanelLandscape,
- hotseatBorderSpaces[INDEX_DEFAULT]);
- hotseatBorderSpaces[INDEX_TWO_PANEL_PORTRAIT] = a.getFloat(
- R.styleable.ProfileDisplayOption_hotseatBorderSpaceTwoPanelPortrait,
- hotseatBorderSpaces[INDEX_DEFAULT]);
-
hotseatBarBottomSpace[INDEX_DEFAULT] = a.getFloat(
R.styleable.ProfileDisplayOption_hotseatBarBottomSpace,
ResourcesCompat.getFloat(context.getResources(),
@@ -1117,7 +1149,6 @@
allAppsIconSizes[i] = 0;
allAppsIconTextSizes[i] = 0;
allAppsBorderSpaces[i] = new PointF();
- inlineQsb[i] = false;
}
}
@@ -1130,7 +1161,6 @@
minCellSize[i].x *= w;
minCellSize[i].y *= w;
horizontalMargin[i] *= w;
- hotseatBorderSpaces[i] *= w;
hotseatBarBottomSpace[i] *= w;
hotseatQsbSpace[i] *= w;
allAppsCellSize[i].x *= w;
@@ -1141,8 +1171,6 @@
allAppsBorderSpaces[i].y *= w;
}
- folderBorderSpace *= w;
-
return this;
}
@@ -1155,7 +1183,6 @@
minCellSize[i].x += p.minCellSize[i].x;
minCellSize[i].y += p.minCellSize[i].y;
horizontalMargin[i] += p.horizontalMargin[i];
- hotseatBorderSpaces[i] += p.hotseatBorderSpaces[i];
hotseatBarBottomSpace[i] += p.hotseatBarBottomSpace[i];
hotseatQsbSpace[i] += p.hotseatQsbSpace[i];
allAppsCellSize[i].x += p.allAppsCellSize[i].x;
@@ -1164,11 +1191,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 9a9403c..de5419e 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -18,8 +18,6 @@
import static android.app.PendingIntent.FLAG_IMMUTABLE;
import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
-import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
-import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
import static android.content.pm.ActivityInfo.CONFIG_UI_MODE;
import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO;
import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
@@ -44,6 +42,8 @@
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_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;
@@ -52,6 +52,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;
@@ -60,7 +62,6 @@
import static com.android.launcher3.popup.SystemShortcut.WIDGETS;
import static com.android.launcher3.states.RotationHelper.REQUEST_LOCK;
import static com.android.launcher3.states.RotationHelper.REQUEST_NONE;
-import static com.android.launcher3.testing.shared.TestProtocol.BAD_STATE;
import static com.android.launcher3.util.ItemInfoMatcher.forFolderMatch;
import android.animation.Animator;
@@ -75,17 +76,16 @@
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetManager;
import android.content.ActivityNotFoundException;
-import android.content.BroadcastReceiver;
import android.content.ComponentCallbacks2;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Bitmap;
+import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Build;
@@ -99,6 +99,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;
@@ -118,6 +119,7 @@
import android.widget.Toast;
import androidx.annotation.CallSuper;
+import androidx.annotation.FloatRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
@@ -131,6 +133,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;
@@ -141,6 +144,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;
@@ -165,6 +169,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;
@@ -179,7 +184,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;
@@ -192,22 +196,22 @@
import com.android.launcher3.util.PendingRequestArgs;
import com.android.launcher3.util.RunnableList;
import com.android.launcher3.util.SafeCloseable;
+import com.android.launcher3.util.ScreenOnTracker;
+import com.android.launcher3.util.ScreenOnTracker.ScreenOnListener;
import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.util.Themes;
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;
@@ -221,7 +225,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;
@@ -241,7 +244,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<>();
@@ -311,7 +314,11 @@
private static final FloatProperty<Hotseat> HOTSEAT_WIDGET_SCALE =
HOTSEAT_SCALE_PROPERTY_FACTORY.get(SCALE_INDEX_WIDGET_TRANSITION);
- private Configuration mOldConfig;
+ private static final boolean DESKTOP_MODE_1_SUPPORTED =
+ "1".equals(Utilities.getSystemProperty("persist.wm.debug.desktop_mode", "0"));
+
+ private static final boolean DESKTOP_MODE_2_SUPPORTED =
+ "1".equals(Utilities.getSystemProperty("persist.wm.debug.desktop_mode_2", "0"));
@Thunk
Workspace<?> mWorkspace;
@@ -319,7 +326,7 @@
DragLayer mDragLayer;
private WidgetManagerHelper mAppWidgetManager;
- private LauncherAppWidgetHost mAppWidgetHost;
+ private LauncherWidgetHolder mAppWidgetHolder;
private final int[] mTmpAddItemCellCoordinates = new int[2];
@@ -398,7 +405,7 @@
private LauncherState mPrevLauncherState;
private StringCache mStringCache;
- private ViewCapture mViewCapture;
+ private BaseSearchConfig mBaseSearchConfig;
@Override
@TargetApi(Build.VERSION_CODES.S)
@@ -467,14 +474,13 @@
super.onCreate(savedInstanceState);
LauncherAppState app = LauncherAppState.getInstance(this);
- mOldConfig = new Configuration(getResources().getConfiguration());
mModel = app.getModel();
mRotationHelper = new RotationHelper(this);
InvariantDeviceProfile idp = app.getInvariantDeviceProfile();
initDeviceProfile(idp);
idp.addOnChangeListener(this);
- mSharedPrefs = Utilities.getPrefs(this);
+ mSharedPrefs = LauncherPrefs.getPrefs(this);
mIconCache = app.getIconCache();
mAccessibilityDelegate = createAccessibilityDelegate();
@@ -484,11 +490,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);
@@ -513,7 +521,6 @@
if (!mModel.addCallbacksAndLoad(this)) {
if (!internalStateHandled) {
- Log.d(BAD_STATE, "Launcher onCreate not binding sync, prevent drawing");
// If we are not binding synchronously, pause drawing until initial bind complete,
// so that the system could continue to show the device loading prompt
mOnInitialBindListener = Boolean.FALSE::booleanValue;
@@ -529,9 +536,8 @@
}
getRootView().dispatchInsets();
- // Listen for broadcasts
- registerReceiver(mScreenOffReceiver, new IntentFilter(Intent.ACTION_SCREEN_OFF));
-
+ // Listen for screen turning off
+ ScreenOnTracker.INSTANCE.get(this).addListener(mScreenOnListener);
getSystemUiController().updateUiState(SystemUiController.UI_STATE_BASE_WINDOW,
Themes.getAttrBoolean(this, R.attr.isWorkspaceDarkText));
@@ -616,17 +622,6 @@
dispatchDeviceProfileChanged();
}
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- int diff = newConfig.diff(mOldConfig);
- if ((diff & (CONFIG_ORIENTATION | CONFIG_SCREEN_SIZE)) != 0) {
- onIdpChanged(false);
- }
-
- mOldConfig.setTo(newConfig);
- super.onConfigurationChanged(newConfig);
- }
-
/**
* Initializes the drag controller.
*/
@@ -636,7 +631,15 @@
@Override
public void onIdpChanged(boolean modelPropertiesChanged) {
- initDeviceProfile(mDeviceProfile.inv);
+ onHandleConfigurationChanged();
+ }
+
+ @Override
+ protected void onHandleConfigurationChanged() {
+ if (!initDeviceProfile(mDeviceProfile.inv)) {
+ return;
+ }
+
dispatchDeviceProfileChanged();
reapplyUi();
mDragLayer.recreateControllers();
@@ -652,16 +655,16 @@
}
/**
- * Called when one handed mode activated and deactivated.
- * @param activated true if one handed mode activated, false otherwise.
+ * Returns {@code true} if a new DeviceProfile is initialized, and {@code false} otherwise.
*/
- public void onOneHandedStateChanged(boolean activated) {
- mDragLayer.onOneHandedModeStateChanged(activated);
- }
-
- protected void initDeviceProfile(InvariantDeviceProfile idp) {
+ protected boolean initDeviceProfile(InvariantDeviceProfile idp) {
// Load configuration-specific DeviceProfile
- mDeviceProfile = idp.getDeviceProfile(this);
+ DeviceProfile deviceProfile = idp.getDeviceProfile(this);
+ if (mDeviceProfile == deviceProfile) {
+ return false;
+ }
+
+ mDeviceProfile = deviceProfile;
if (isInMultiWindowMode()) {
mDeviceProfile = mDeviceProfile.getMultiWindowProfile(
this, getMultiWindowDisplaySize());
@@ -669,6 +672,7 @@
onDeviceProfileInitiated();
mModelWriter = mModel.getWriter(getDeviceProfile().isVerticalBarLayout(), true, this);
+ return true;
}
public RotationHelper getRotationHelper() {
@@ -691,17 +695,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;
@@ -959,7 +955,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() {
@@ -970,7 +966,7 @@
}
};
} else if (resultCode == RESULT_CANCELED) {
- mAppWidgetHost.deleteAppWidgetId(appWidgetId);
+ mAppWidgetHolder.deleteAppWidgetId(appWidgetId);
animationType = Workspace.CANCEL_TWO_STAGE_WIDGET_DROP_ANIMATION;
}
if (mDragLayer.getAnimatedView() != null) {
@@ -993,7 +989,7 @@
}
hideKeyboard();
logStopAndResume(false /* isResume */);
- mAppWidgetHost.setActivityStarted(false);
+ mAppWidgetHolder.setActivityStarted(false);
NotificationListener.removeNotificationsChangedListener(getPopupDataProvider());
}
@@ -1006,7 +1002,7 @@
mOverlayManager.onActivityStarted(this);
}
- mAppWidgetHost.setActivityStarted(true);
+ mAppWidgetHolder.setActivityStarted(true);
TraceHelper.INSTANCE.endSection(traceToken);
}
@@ -1026,7 +1022,7 @@
NotificationListener.addNotificationsChangedListener(mPopupDataProvider);
DiscoveryBounce.showForHomeIfNeeded(this);
- mAppWidgetHost.setActivityResumed(true);
+ mAppWidgetHolder.setActivityResumed(true);
}
private void logStopAndResume(boolean isResume) {
@@ -1126,6 +1122,7 @@
.log(getAllAppsEntryEvent().get());
}
}
+ updateDisallowBack();
}
/**
@@ -1140,7 +1137,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();
@@ -1187,7 +1184,6 @@
mOverlayManager.onActivityResumed(this);
}
- AbstractFloatingView.closeAllOpenViewsExcept(this, false, TYPE_REBIND_SAFE);
DragView.removeAllViews(this);
TraceHelper.INSTANCE.endSection(traceToken);
}
@@ -1205,19 +1201,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);
}
/**
@@ -1261,6 +1245,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);
@@ -1291,6 +1276,23 @@
// Setup the drag controller (drop targets have to be added in reverse order in priority)
mDropTargetBar.setup(mDragController);
mAllAppsController.setupViews(mScrimView, mAppsView);
+
+ if (SHOW_DOT_PAGINATION.get()) {
+ mWorkspace.getPageIndicator().setShouldAutoHide(true);
+ mWorkspace.getPageIndicator().setPaintColor(
+ Themes.getAttrBoolean(this, R.attr.isWorkspaceDarkText)
+ ? Color.BLACK
+ : Color.WHITE);
+ }
+ }
+
+ @Override
+ public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
+ if (SHOW_DOT_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);
}
/**
@@ -1316,7 +1318,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;
}
@@ -1419,7 +1421,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;
@@ -1473,12 +1475,7 @@
hostView.setOnFocusChangeListener(mFocusHandler);
}
- private final BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- onScreenOff();
- }
- };
+ private final ScreenOnListener mScreenOnListener = this::onScreenOnChanged;
private void updateNotificationDots(Predicate<PackageUserKey> updatedDots) {
mWorkspace.updateNotificationDots(updatedDots);
@@ -1489,14 +1486,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);
- root.getViewTreeObserver().addOnDrawListener(mViewCapture);
- }
}
@Override
@@ -1558,13 +1547,13 @@
return mScrimView;
}
- public LauncherAppWidgetHost getAppWidgetHost() {
- return mAppWidgetHost;
+ public LauncherWidgetHolder getAppWidgetHolder() {
+ return mAppWidgetHolder;
}
- protected LauncherAppWidgetHost createAppWidgetHost() {
- return new LauncherAppWidgetHost(this,
- appWidgetId -> getWorkspace().removeWidget(appWidgetId));
+ protected LauncherWidgetHolder createAppWidgetHolder() {
+ return LauncherWidgetHolder.HolderFactory.newFactory(this).newInstance(
+ this, appWidgetId -> getWorkspace().removeWidget(appWidgetId));
}
public LauncherModel getModel() {
@@ -1582,13 +1571,17 @@
@Override
public SharedPreferences getDevicePrefs() {
- return Utilities.getDevicePrefs(this);
+ return LauncherPrefs.getDevicePrefs(this);
}
public int getOrientation() {
return mOldConfig.orientation;
}
+ public BaseSearchConfig getSearchConfig() {
+ return mBaseSearchConfig;
+ }
+
@Override
protected void onNewIntent(Intent intent) {
if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
@@ -1606,7 +1599,6 @@
&& AbstractFloatingView.getTopOpenView(this) == null;
boolean isActionMain = Intent.ACTION_MAIN.equals(intent.getAction());
boolean internalStateHandled = ACTIVITY_TRACKER.handleNewIntent(this);
- hideKeyboard();
if (isActionMain) {
if (!internalStateHandled) {
@@ -1664,16 +1656,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);
@@ -1703,6 +1685,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) {
@@ -1723,7 +1709,7 @@
super.onDestroy();
ACTIVITY_TRACKER.onActivityDestroyed(this);
- unregisterReceiver(mScreenOffReceiver);
+ ScreenOnTracker.INSTANCE.get(this).removeListener(mScreenOnListener);
mWorkspace.removeFolderListeners();
PluginManagerWrapper.INSTANCE.get(this).removePluginListener(this);
@@ -1731,10 +1717,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();
@@ -1906,7 +1893,7 @@
appWidgetId = CustomWidgetManager.INSTANCE.get(this).getWidgetIdForCustomProvider(
info.componentName);
} else {
- appWidgetId = getAppWidgetHost().allocateAppWidgetId();
+ appWidgetId = getAppWidgetHolder().allocateAppWidgetId();
}
Bundle options = info.bindOptions;
@@ -2020,7 +2007,7 @@
final LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo) itemInfo;
mWorkspace.removeWorkspaceItem(v);
if (deleteFromDb) {
- getModelWriter().deleteWidgetInfo(widgetInfo, getAppWidgetHost(), reason);
+ getModelWriter().deleteWidgetInfo(widgetInfo, getAppWidgetHolder(), reason);
}
} else {
return false;
@@ -2082,10 +2069,18 @@
mStateManager.getState().onBackPressed(this);
}
- protected void onScreenOff() {
+ protected void onBackProgressed(@FloatRange(from = 0.0, to = 1.0) float backProgress) {
+ mStateManager.getState().onBackProgressed(this, backProgress);
+ }
+
+ protected void onBackCancelled() {
+ mStateManager.getState().onBackCancelled(this);
+ }
+
+ protected void onScreenOnChanged(boolean isOn) {
// Reset AllApps to its initial state only if we are not in the middle of
// processing a multi-step drop
- if (mPendingRequestArgs == null) {
+ if (!isOn && mPendingRequestArgs == null) {
if (!isInState(NORMAL)) {
onUiChangedWhileSleeping();
}
@@ -2278,7 +2273,7 @@
mWorkspace.clearDropTargets();
mWorkspace.removeAllWorkspaceScreens();
- mAppWidgetHost.clearViews();
+ mAppWidgetHolder.clearViews();
if (mHotseat != null) {
mHotseat.resetLayout(getDeviceProfile().isVerticalBarLayout());
@@ -2585,7 +2580,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
@@ -2647,18 +2642,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);
}
@@ -2762,6 +2757,8 @@
getViewCache().setCacheSize(R.layout.folder_page, 2);
TraceHelper.INSTANCE.endSection(traceToken);
+
+ mWorkspace.removeExtraEmptyScreen(true);
}
private boolean canAnimatePageChange() {
@@ -2800,7 +2797,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());
@@ -2811,16 +2808,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));
}
/**
@@ -2873,7 +2884,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
@@ -3014,17 +3034,14 @@
writer.println(prefix + "\tmPendingRequestArgs=" + mPendingRequestArgs
+ " mPendingActivityResult=" + mPendingActivityResult);
writer.println(prefix + "\tmRotationHelper: " + mRotationHelper);
- writer.println(prefix + "\tmAppWidgetHost.isListening: " + mAppWidgetHost.isListening());
-
- if (mViewCapture != null) {
- writer.println(prefix + "\tmViewCapture: " + mViewCapture.dumpToString());
- }
+ writer.println(prefix + "\tmAppWidgetHolder.isListening: "
+ + mAppWidgetHolder.isListening());
// Extra logging for general debugging
mDragLayer.dump(prefix, writer);
mStateManager.dump(prefix, writer);
mPopupDataProvider.dump(prefix, writer);
- mDeviceProfile.dump(prefix, writer);
+ mDeviceProfile.dump(this, prefix, writer);
try {
FileLog.flushAll(writer);
@@ -3148,7 +3165,22 @@
public void useFadeOutAnimationForLauncherStart(CancellationSignal signal) { }
- public void onDragLayerHierarchyChanged() { }
+ public void onDragLayerHierarchyChanged() {
+ updateDisallowBack();
+ }
+
+ private void updateDisallowBack() {
+ if (DESKTOP_MODE_1_SUPPORTED || DESKTOP_MODE_2_SUPPORTED) {
+ // Do not disable back in launcher when prototype behavior is enabled
+ return;
+ }
+ LauncherRootView rv = getRootView();
+ if (rv != null) {
+ boolean disableBack = getStateManager().getState() == NORMAL
+ && AbstractFloatingView.getTopOpenView(this) == null;
+ rv.setDisallowBackGesture(disableBack);
+ }
+ }
@Override
public void returnToHomescreen() {
@@ -3248,12 +3280,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 {
@@ -3299,4 +3329,12 @@
return false; // Return false to continue iterating through all the items.
});
}
+
+ /**
+ * Returns {@code true} if there are visible tasks with windowing mode set to
+ * {@link android.app.WindowConfiguration#WINDOWING_MODE_FREEFORM}
+ */
+ public boolean areFreeformTasksVisible() {
+ return false; // Base launcher does not track freeform tasks
+ }
}
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..2b98d98 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -18,8 +18,8 @@
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.LauncherPrefs.ICON_STATE;
+import static com.android.launcher3.LauncherPrefs.THEMED_ICONS;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import static com.android.launcher3.util.SettingsCache.NOTIFICATION_BADGING_URI;
@@ -56,7 +56,7 @@
public class LauncherAppState implements SafeCloseable {
public static final String ACTION_FORCE_ROLOAD = "force-reload-launcher";
- private static final String KEY_ICON_STATE = "pref_icon_shape_path";
+ public static final String KEY_ICON_STATE = "pref_icon_shape_path";
// We do not need any synchronization for this variable as its only written on UI thread.
public static final MainThreadInitializedObject<LauncherAppState> INSTANCE =
@@ -102,7 +102,7 @@
Intent.ACTION_MANAGED_PROFILE_UNLOCKED,
ACTION_DEVICE_POLICY_RESOURCE_UPDATED);
if (FeatureFlags.IS_STUDIO_BUILD) {
- modelChangeReceiver.register(mContext, Context.RECEIVER_EXPORTED, ACTION_FORCE_ROLOAD);
+ modelChangeReceiver.register(mContext, ACTION_FORCE_ROLOAD);
}
mOnTerminateCallback.add(() -> mContext.unregisterReceiver(modelChangeReceiver));
@@ -118,12 +118,9 @@
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));
- }
+ LauncherPrefs.get(context).addListener(observer, THEMED_ICONS);
+ mOnTerminateCallback.add(
+ () -> LauncherPrefs.get(mContext).removeListener(observer, THEMED_ICONS));
InstallSessionTracker installSessionTracker =
InstallSessionHelper.INSTANCE.get(context).registerInstallTracker(mModel);
@@ -210,12 +207,12 @@
public void onSystemIconStateChanged(String iconState) {
IconShape.init(mContext);
refreshAndReloadLauncher();
- getDevicePrefs(mContext).edit().putString(KEY_ICON_STATE, iconState).apply();
+ LauncherPrefs.get(mContext).put(ICON_STATE, iconState);
}
void verifyIconChanged() {
String iconState = mIconProvider.getSystemIconState();
- if (!iconState.equals(getDevicePrefs(mContext).getString(KEY_ICON_STATE, ""))) {
+ if (!iconState.equals(LauncherPrefs.get(mContext).get(ICON_STATE))) {
onSystemIconStateChanged(iconState);
}
}
diff --git a/src/com/android/launcher3/LauncherFiles.java b/src/com/android/launcher3/LauncherFiles.java
index e59eac8..d730cea 100644
--- a/src/com/android/launcher3/LauncherFiles.java
+++ b/src/com/android/launcher3/LauncherFiles.java
@@ -47,9 +47,12 @@
DEVICE_PREFERENCES_KEY + XML,
APP_ICONS_DB));
- public static final List<String> ALL_FILES = Collections.unmodifiableList(
- new ArrayList<String>() {{
- addAll(GRID_DB_FILES);
- addAll(OTHER_FILES);
- }});
+ private static List<String> createAllFiles() {
+ ArrayList<String> result = new ArrayList<>();
+ result.addAll(GRID_DB_FILES);
+ result.addAll(OTHER_FILES);
+ return Collections.unmodifiableList(result);
+ }
+
+ public static final List<String> ALL_FILES = createAllFiles();
}
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index f790789..2c6458b 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;
@@ -46,7 +47,7 @@
import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.model.CacheDataUpdatedTask;
import com.android.launcher3.model.ItemInstallQueue;
-import com.android.launcher3.model.LoaderResults;
+import com.android.launcher3.model.LauncherBinder;
import com.android.launcher3.model.LoaderTask;
import com.android.launcher3.model.ModelDelegate;
import com.android.launcher3.model.ModelWriter;
@@ -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);
@@ -387,22 +405,22 @@
MAIN_EXECUTOR.execute(cb::clearPendingBinds);
}
- LoaderResults loaderResults = new LoaderResults(
+ LauncherBinder launcherBinder = new LauncherBinder(
mApp, mBgDataModel, mBgAllAppsList, callbacksList);
if (bindDirectly) {
// Divide the set of loaded items into those that we are binding synchronously,
// and everything else that is to be bound normally (asynchronously).
- loaderResults.bindWorkspace(bindAllCallbacks);
+ launcherBinder.bindWorkspace(bindAllCallbacks);
// For now, continue posting the binding of AllApps as there are other
// issues that arise from that.
- loaderResults.bindAllApps();
- loaderResults.bindDeepShortcuts();
- loaderResults.bindWidgets();
+ launcherBinder.bindAllApps();
+ launcherBinder.bindDeepShortcuts();
+ launcherBinder.bindWidgets();
return true;
} else {
stopLoader();
mLoaderTask = new LoaderTask(
- mApp, mBgAllAppsList, mBgDataModel, mModelDelegate, loaderResults);
+ mApp, mBgAllAppsList, mBgDataModel, mModelDelegate, launcherBinder);
// Always post the loader task, instead of running directly
// (even on same thread) so that we exit any nested synchronized blocks
@@ -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/LauncherPrefs.kt b/src/com/android/launcher3/LauncherPrefs.kt
new file mode 100644
index 0000000..1fb2dce
--- /dev/null
+++ b/src/com/android/launcher3/LauncherPrefs.kt
@@ -0,0 +1,256 @@
+package com.android.launcher3
+
+import android.content.Context
+import android.content.SharedPreferences
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener
+import androidx.annotation.VisibleForTesting
+import com.android.launcher3.allapps.WorkProfileManager
+import com.android.launcher3.model.DeviceGridState
+import com.android.launcher3.pm.InstallSessionHelper
+import com.android.launcher3.provider.RestoreDbTask
+import com.android.launcher3.util.MainThreadInitializedObject
+import com.android.launcher3.util.Themes
+
+/**
+ * Use same context for shared preferences, so that we use a single cached instance
+ * TODO(b/262721340): Replace all direct SharedPreference refs with LauncherPrefs / Item methods.
+ */
+class LauncherPrefs(private val context: Context) {
+
+ /**
+ * Retrieves the value for an [Item] from [SharedPreferences]. It handles method typing via the
+ * default value type, and will throw an error if the type of the item provided is not a
+ * `String`, `Boolean`, `Float`, `Int`, `Long`, or `Set<String>`.
+ */
+ @Suppress("IMPLICIT_CAST_TO_ANY", "UNCHECKED_CAST")
+ fun <T : Any> get(item: Item<T>): T {
+ val sp = context.getSharedPreferences(item.sharedPrefFile, Context.MODE_PRIVATE)
+
+ return when (item.defaultValue::class.java) {
+ String::class.java -> sp.getString(item.sharedPrefKey, item.defaultValue as String)
+ Boolean::class.java,
+ java.lang.Boolean::class.java ->
+ sp.getBoolean(item.sharedPrefKey, item.defaultValue as Boolean)
+ Int::class.java,
+ java.lang.Integer::class.java -> sp.getInt(item.sharedPrefKey, item.defaultValue as Int)
+ Float::class.java,
+ java.lang.Float::class.java ->
+ sp.getFloat(item.sharedPrefKey, item.defaultValue as Float)
+ Long::class.java,
+ java.lang.Long::class.java -> sp.getLong(item.sharedPrefKey, item.defaultValue as Long)
+ Set::class.java -> sp.getStringSet(item.sharedPrefKey, item.defaultValue as Set<String>)
+ else ->
+ throw IllegalArgumentException(
+ "item type: ${item.defaultValue::class.java}" +
+ " is not compatible with sharedPref methods"
+ )
+ }
+ as T
+ }
+
+ /**
+ * Stores each of the values provided in `SharedPreferences` according to the configuration
+ * contained within the associated items provided. Internally, it uses apply, so the caller
+ * cannot assume that the values that have been put are immediately available for use.
+ *
+ * The forEach loop is necessary here since there is 1 `SharedPreference.Editor` returned from
+ * prepareToPutValue(itemsToValues) for every distinct `SharedPreferences` file present in the
+ * provided item configurations.
+ */
+ fun put(vararg itemsToValues: Pair<Item<*>, Any>): Unit =
+ prepareToPutValues(itemsToValues).forEach { it.apply() }
+
+ /**
+ * Stores the value provided in `SharedPreferences` according to the item configuration provided
+ * It is asynchronous, so the caller can't assume that the value put is immediately available.
+ */
+ fun <T : Any> put(item: Item<T>, value: T): Unit =
+ context
+ .getSharedPreferences(item.sharedPrefFile, Context.MODE_PRIVATE)
+ .edit()
+ .putValue(item, value)
+ .apply()
+
+ /**
+ * Synchronously stores all the values provided according to their associated Item
+ * configuration.
+ */
+ fun putSync(vararg itemsToValues: Pair<Item<*>, Any>): Unit =
+ prepareToPutValues(itemsToValues).forEach { it.commit() }
+
+ /**
+ * Update each shared preference file with the item - value pairs provided. This method is
+ * optimized to avoid retrieving the same shared preference file multiple times.
+ *
+ * @return `List<SharedPreferences.Editor>` 1 for each distinct shared preference file among the
+ * items given as part of the itemsToValues parameter
+ */
+ private fun prepareToPutValues(
+ itemsToValues: Array<out Pair<Item<*>, Any>>
+ ): List<SharedPreferences.Editor> =
+ itemsToValues
+ .groupBy { it.first.sharedPrefFile }
+ .map { fileToItemValueList ->
+ context
+ .getSharedPreferences(fileToItemValueList.key, Context.MODE_PRIVATE)
+ .edit()
+ .apply {
+ fileToItemValueList.value.forEach { itemToValue ->
+ putValue(itemToValue.first, itemToValue.second)
+ }
+ }
+ }
+
+ /**
+ * Handles adding values to `SharedPreferences` regardless of type. This method is especially
+ * helpful for updating `SharedPreferences` values for `List<<Item>Any>` that have multiple
+ * types of Item values.
+ */
+ @Suppress("UNCHECKED_CAST")
+ private fun SharedPreferences.Editor.putValue(
+ item: Item<*>,
+ value: Any
+ ): SharedPreferences.Editor =
+ when (value::class.java) {
+ String::class.java -> putString(item.sharedPrefKey, value as String)
+ Boolean::class.java,
+ java.lang.Boolean::class.java -> putBoolean(item.sharedPrefKey, value as Boolean)
+ Int::class.java,
+ java.lang.Integer::class.java -> putInt(item.sharedPrefKey, value as Int)
+ Float::class.java,
+ java.lang.Float::class.java -> putFloat(item.sharedPrefKey, value as Float)
+ Long::class.java,
+ java.lang.Long::class.java -> putLong(item.sharedPrefKey, value as Long)
+ Set::class.java -> putStringSet(item.sharedPrefKey, value as Set<String>)
+ else ->
+ throw IllegalArgumentException(
+ "item type: " +
+ "${item.defaultValue!!::class} is not compatible with sharedPref methods"
+ )
+ }
+
+ /**
+ * After calling this method, the listener will be notified of any future updates to the
+ * `SharedPreferences` files associated with the provided list of items. The listener will need
+ * to filter update notifications so they don't activate for non-relevant updates.
+ */
+ fun addListener(listener: OnSharedPreferenceChangeListener, vararg items: Item<*>) {
+ items
+ .map { it.sharedPrefFile }
+ .distinct()
+ .forEach {
+ context
+ .getSharedPreferences(it, Context.MODE_PRIVATE)
+ .registerOnSharedPreferenceChangeListener(listener)
+ }
+ }
+
+ /**
+ * Stops the listener from getting notified of any more updates to any of the
+ * `SharedPreferences` files associated with any of the provided list of [Item].
+ */
+ fun removeListener(listener: OnSharedPreferenceChangeListener, vararg items: Item<*>) {
+ // If a listener is not registered to a SharedPreference, unregistering it does nothing
+ items
+ .map { it.sharedPrefFile }
+ .distinct()
+ .forEach {
+ context
+ .getSharedPreferences(it, Context.MODE_PRIVATE)
+ .unregisterOnSharedPreferenceChangeListener(listener)
+ }
+ }
+
+ /**
+ * Checks if all the provided [Item] have values stored in their corresponding
+ * `SharedPreferences` files.
+ */
+ fun has(vararg items: Item<*>): Boolean {
+ items
+ .groupBy { it.sharedPrefFile }
+ .forEach { (file, itemsSublist) ->
+ val prefs: SharedPreferences =
+ context.getSharedPreferences(file, Context.MODE_PRIVATE)
+ if (!itemsSublist.none { !prefs.contains(it.sharedPrefKey) }) return false
+ }
+ return true
+ }
+
+ /**
+ * Asynchronously removes the [Item]'s value from its corresponding `SharedPreferences` file.
+ */
+ fun remove(vararg items: Item<*>) = prepareToRemove(items).forEach { it.apply() }
+
+ /** Synchronously removes the [Item]'s value from its corresponding `SharedPreferences` file. */
+ fun removeSync(vararg items: Item<*>) = prepareToRemove(items).forEach { it.commit() }
+
+ /**
+ * Creates `SharedPreferences.Editor` transactions for removing all the provided [Item] values
+ * from their respective `SharedPreferences` files. These returned `Editors` can then be
+ * committed or applied for synchronous or async behavior.
+ */
+ private fun prepareToRemove(items: Array<out Item<*>>): List<SharedPreferences.Editor> =
+ items
+ .groupBy { it.sharedPrefFile }
+ .map { (file, items) ->
+ context.getSharedPreferences(file, Context.MODE_PRIVATE).edit().also { editor ->
+ items.forEach { item -> editor.remove(item.sharedPrefKey) }
+ }
+ }
+
+ companion object {
+ @JvmField var INSTANCE = MainThreadInitializedObject { LauncherPrefs(it) }
+
+ @JvmStatic fun get(context: Context): LauncherPrefs = INSTANCE.get(context)
+
+ @JvmField val ICON_STATE = nonRestorableItem(LauncherAppState.KEY_ICON_STATE, "")
+ @JvmField val THEMED_ICONS = backedUpItem(Themes.KEY_THEMED_ICONS, false)
+ @JvmField val PROMISE_ICON_IDS = backedUpItem(InstallSessionHelper.PROMISE_ICON_IDS, "")
+ @JvmField val WORK_EDU_STEP = backedUpItem(WorkProfileManager.KEY_WORK_EDU_STEP, 0)
+ @JvmField val WORKSPACE_SIZE = backedUpItem(DeviceGridState.KEY_WORKSPACE_SIZE, "")
+ @JvmField val HOTSEAT_COUNT = backedUpItem(DeviceGridState.KEY_HOTSEAT_COUNT, -1)
+ @JvmField
+ val DEVICE_TYPE =
+ backedUpItem(DeviceGridState.KEY_DEVICE_TYPE, InvariantDeviceProfile.TYPE_PHONE)
+ @JvmField val DB_FILE = backedUpItem(DeviceGridState.KEY_DB_FILE, "")
+ @JvmField
+ val RESTORE_DEVICE =
+ backedUpItem(RestoreDbTask.RESTORED_DEVICE_TYPE, InvariantDeviceProfile.TYPE_PHONE)
+ @JvmField val APP_WIDGET_IDS = backedUpItem(RestoreDbTask.APPWIDGET_IDS, "")
+ @JvmField val OLD_APP_WIDGET_IDS = backedUpItem(RestoreDbTask.APPWIDGET_OLD_IDS, "")
+
+ @VisibleForTesting
+ @JvmStatic
+ fun <T> backedUpItem(sharedPrefKey: String, defaultValue: T): Item<T> =
+ Item(sharedPrefKey, LauncherFiles.SHARED_PREFERENCES_KEY, defaultValue)
+
+ @VisibleForTesting
+ @JvmStatic
+ fun <T> nonRestorableItem(sharedPrefKey: String, defaultValue: T): Item<T> =
+ Item(sharedPrefKey, LauncherFiles.DEVICE_PREFERENCES_KEY, defaultValue)
+
+ @Deprecated("Don't use shared preferences directly. Use other LauncherPref methods.")
+ @JvmStatic
+ fun getPrefs(context: Context): SharedPreferences {
+ // Use application context for shared preferences, so we use single cached instance
+ return context.applicationContext.getSharedPreferences(
+ LauncherFiles.SHARED_PREFERENCES_KEY,
+ Context.MODE_PRIVATE
+ )
+ }
+
+ @Deprecated("Don't use shared preferences directly. Use other LauncherPref methods.")
+ @JvmStatic
+ fun getDevicePrefs(context: Context): SharedPreferences {
+ // Use application context for shared preferences, so we use a single cached instance
+ return context.applicationContext.getSharedPreferences(
+ LauncherFiles.DEVICE_PREFERENCES_KEY,
+ Context.MODE_PRIVATE
+ )
+ }
+ }
+}
+
+data class Item<T>(val sharedPrefKey: String, val sharedPrefFile: String, val defaultValue: T) {
+ fun to(value: T): Pair<Item<T>, T> = Pair(this, value)
+}
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index a20ff8c..767c3d9 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -16,13 +16,13 @@
package com.android.launcher3;
+import static com.android.launcher3.DefaultLayoutParser.RES_PARTNER_DEFAULT_LAYOUT;
import static com.android.launcher3.provider.LauncherDbUtils.copyTable;
import static com.android.launcher3.provider.LauncherDbUtils.dropTable;
import static com.android.launcher3.provider.LauncherDbUtils.tableExists;
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;
@@ -35,7 +35,6 @@
import android.content.OperationApplicationException;
import android.content.SharedPreferences;
import android.content.pm.ProviderInfo;
-import android.content.res.Resources;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.database.SQLException;
@@ -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;
@@ -69,8 +70,9 @@
import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.NoLocaleSQLiteHelper;
import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.util.Partner;
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 +87,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";
@@ -103,6 +106,7 @@
public static final String KEY_LAYOUT_PROVIDER_AUTHORITY = "KEY_LAYOUT_PROVIDER_AUTHORITY";
private static final int TEST_WORKSPACE_LAYOUT_RES_XML = R.xml.default_test_workspace;
+ private static final int TEST2_WORKSPACE_LAYOUT_RES_XML = R.xml.default_test2_workspace;
static final String EMPTY_DATABASE_CREATED = "EMPTY_DATABASE_CREATED";
@@ -111,7 +115,7 @@
private long mLastRestoreTimestamp = 0L;
- private boolean mUseTestWorkspaceLayout;
+ private int mDefaultWorkspaceLayoutOverride = 0;
/**
* $ adb shell dumpsys activity provider com.android.launcher3
@@ -254,17 +258,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;
@@ -369,7 +376,7 @@
case LauncherSettings.Settings.METHOD_WAS_EMPTY_DB_CREATED : {
Bundle result = new Bundle();
result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE,
- Utilities.getPrefs(getContext()).getBoolean(
+ LauncherPrefs.getPrefs(getContext()).getBoolean(
mOpenHelper.getKey(EMPTY_DATABASE_CREATED), false));
return result;
}
@@ -396,11 +403,21 @@
return null;
}
case LauncherSettings.Settings.METHOD_SET_USE_TEST_WORKSPACE_LAYOUT_FLAG: {
- mUseTestWorkspaceLayout = true;
+ switch (arg) {
+ case LauncherSettings.Settings.ARG_DEFAULT_WORKSPACE_LAYOUT_TEST:
+ mDefaultWorkspaceLayoutOverride = TEST_WORKSPACE_LAYOUT_RES_XML;
+ break;
+ case LauncherSettings.Settings.ARG_DEFAULT_WORKSPACE_LAYOUT_TEST2:
+ mDefaultWorkspaceLayoutOverride = TEST2_WORKSPACE_LAYOUT_RES_XML;
+ break;
+ default:
+ mDefaultWorkspaceLayoutOverride = 0;
+ break;
+ }
return null;
}
case LauncherSettings.Settings.METHOD_CLEAR_USE_TEST_WORKSPACE_LAYOUT_FLAG: {
- mUseTestWorkspaceLayout = false;
+ mDefaultWorkspaceLayoutOverride = 0;
return null;
}
case LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES: {
@@ -515,7 +532,7 @@
}
private void clearFlagEmptyDbCreated() {
- Utilities.getPrefs(getContext()).edit()
+ LauncherPrefs.getPrefs(getContext()).edit()
.remove(mOpenHelper.getKey(EMPTY_DATABASE_CREATED)).commit();
}
@@ -527,32 +544,30 @@
* 4) The default configuration for the particular device
*/
synchronized private void loadDefaultFavoritesIfNecessary() {
- SharedPreferences sp = Utilities.getPrefs(getContext());
+ SharedPreferences sp = LauncherPrefs.getPrefs(getContext());
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());
- if (partner != null && partner.hasDefaultLayout()) {
- final Resources partnerRes = partner.getResources();
- int workspaceResId = partnerRes.getIdentifier(Partner.RES_DEFAULT_LAYOUT,
- "xml", partner.getPackageName());
+ if (partner != null) {
+ int workspaceResId = partner.getXmlResId(RES_PARTNER_DEFAULT_LAYOUT);
if (workspaceResId != 0) {
- loader = new DefaultLayoutParser(getContext(), widgetHost,
- mOpenHelper, partnerRes, workspaceResId);
+ loader = new DefaultLayoutParser(getContext(), widgetHolder,
+ mOpenHelper, partner.getResources(), workspaceResId);
}
}
}
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 +579,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 +591,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 +618,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,17 +637,17 @@
.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;
+ int defaultLayout = mDefaultWorkspaceLayoutOverride > 0
+ ? mDefaultWorkspaceLayoutOverride : idp.defaultLayoutId;
if (getContext().getSystemService(UserManager.class).isDemoUser()
&& idp.demoModeLayoutId != 0) {
defaultLayout = idp.demoModeLayoutId;
}
- return new DefaultLayoutParser(getContext(), widgetHost,
+ return new DefaultLayoutParser(getContext(), widgetHolder,
mOpenHelper, getContext().getResources(), defaultLayout);
}
@@ -731,7 +748,7 @@
*/
protected void onEmptyDbCreated() {
// Set the flag for empty DB
- Utilities.getPrefs(mContext).edit().putBoolean(getKey(EMPTY_DATABASE_CREATED), true)
+ LauncherPrefs.getPrefs(mContext).edit().putBoolean(getKey(EMPTY_DATABASE_CREATED), true)
.commit();
}
@@ -931,28 +948,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 +1088,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 LauncherWidgetHolder.newInstance(mContext);
}
@Override
diff --git a/src/com/android/launcher3/LauncherRootView.java b/src/com/android/launcher3/LauncherRootView.java
index a5c5c02..1592154 100644
--- a/src/com/android/launcher3/LauncherRootView.java
+++ b/src/com/android/launcher3/LauncherRootView.java
@@ -55,6 +55,8 @@
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+ mActivity.handleConfigurationChanged(mActivity.getResources().getConfiguration());
+
insets = WindowManagerProxy.INSTANCE.get(getContext())
.normalizeWindowInsets(getContext(), insets, mTempRect);
handleSystemWindowInsets(mTempRect);
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index 66195f3..6ea331d 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>
*/
@@ -206,12 +207,9 @@
public static final int CONTAINER_BOTTOM_WIDGETS_TRAY = -112;
public static final int CONTAINER_PIN_WIDGETS = -113;
public static final int CONTAINER_WALLPAPERS = -114;
- // Represents search results view.
- public static final int CONTAINER_SEARCH_RESULTS = -106;
public static final int CONTAINER_SHORTCUTS = -107;
public static final int CONTAINER_SETTINGS = -108;
public static final int CONTAINER_TASKSWITCHER = -109;
- public static final int CONTAINER_QSB = -110;
// Represents any of the extended containers implemented in non-AOSP variants.
public static final int EXTENDED_CONTAINERS = -200;
@@ -225,7 +223,6 @@
case CONTAINER_PREDICTION: return "prediction";
case CONTAINER_ALL_APPS: return "all_apps";
case CONTAINER_WIDGETS_TRAY: return "widgets_tray";
- case CONTAINER_SEARCH_RESULTS: return "search_result";
case CONTAINER_SHORTCUTS: return "shortcuts";
default: return String.valueOf(container);
}
@@ -376,6 +373,8 @@
public static final String METHOD_SET_USE_TEST_WORKSPACE_LAYOUT_FLAG =
"set_use_test_workspace_layout_flag";
+ public static final String ARG_DEFAULT_WORKSPACE_LAYOUT_TEST = "default_test_workspace";
+ public static final String ARG_DEFAULT_WORKSPACE_LAYOUT_TEST2 = "default_test2_workspace";
public static final String METHOD_CLEAR_USE_TEST_WORKSPACE_LAYOUT_FLAG =
"clear_use_test_workspace_layout_flag";
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 4532ed4..b9e4c17 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -34,6 +34,8 @@
import android.graphics.Color;
import android.view.animation.Interpolator;
+import androidx.annotation.FloatRange;
+
import com.android.launcher3.statemanager.BaseState;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.states.HintState;
@@ -41,6 +43,7 @@
import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.uioverrides.states.AllAppsState;
import com.android.launcher3.uioverrides.states.OverviewState;
+import com.android.launcher3.views.ActivityContext;
import java.util.Arrays;
@@ -71,17 +74,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 +110,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 +264,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 & ActivityContext>
+ float getDepth(DEVICE_PROFILE_CONTEXT context) {
return getDepth(context,
BaseDraggingActivity.fromContext(context).getDeviceProfile().isMultiWindowMode);
}
@@ -275,14 +275,16 @@
*
* @see #getDepth(Context).
*/
- public final float getDepth(Context context, boolean isMultiWindowMode) {
+ public final <DEVICE_PROFILE_CONTEXT extends Context & ActivityContext>
+ 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 & ActivityContext>
+ float getDepthUnchecked(DEVICE_PROFILE_CONTEXT context) {
return 0f;
}
@@ -342,6 +344,27 @@
}
}
+ /**
+ * Find {@link StateManager} and target {@link LauncherState} to handle back progress in
+ * predictive back gesture.
+ */
+ public void onBackProgressed(
+ Launcher launcher, @FloatRange(from = 0.0, to = 1.0) float backProgress) {
+ StateManager<LauncherState> lsm = launcher.getStateManager();
+ LauncherState toState = lsm.getLastState();
+ lsm.onBackProgressed(toState, backProgress);
+ }
+
+ /**
+ * Find {@link StateManager} and target {@link LauncherState} to handle backProgress in
+ * predictive back gesture.
+ */
+ public void onBackCancelled(Launcher launcher) {
+ StateManager<LauncherState> lsm = launcher.getStateManager();
+ LauncherState toState = lsm.getLastState();
+ lsm.onBackCancelled(toState);
+ }
+
public static abstract class PageAlphaProvider {
public final Interpolator interpolator;
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 73be5be..5fb33cb 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();
@@ -694,7 +699,7 @@
}
/** Returns true iff this PagedView's scroll amounts are initialized to each page index. */
- protected boolean pageScrollsInitialized() {
+ protected boolean isPageScrollsInitialized() {
return mPageScrolls != null && mPageScrolls.length == getChildCount();
}
@@ -703,12 +708,12 @@
*/
public void runOnPageScrollsInitialized(Runnable callback) {
mOnPageScrollsInitializedCallbacks.add(callback);
- if (pageScrollsInitialized()) {
+ if (isPageScrollsInitialized()) {
onPageScrollsInitialized();
}
}
- private void onPageScrollsInitialized() {
+ protected void onPageScrollsInitialized() {
for (Runnable callback : mOnPageScrollsInitializedCallbacks) {
callback.run();
}
@@ -722,7 +727,7 @@
final int childCount = getChildCount();
int[] pageScrolls = mPageScrolls;
boolean pageScrollChanged = false;
- if (!pageScrollsInitialized()) {
+ if (!isPageScrollsInitialized()) {
pageScrolls = new int[childCount];
pageScrollChanged = true;
}
@@ -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();
@@ -1187,7 +1199,7 @@
}
public int getScrollForPage(int index) {
- if (!pageScrollsInitialized() || index >= mPageScrolls.length || index < 0) {
+ if (!isPageScrollsInitialized() || index >= mPageScrolls.length || index < 0) {
return 0;
} else {
return mPageScrolls[index];
@@ -1197,7 +1209,7 @@
// While layout transitions are occurring, a child's position may stray from its baseline
// position. This method returns the magnitude of this stray at any given time.
public int getLayoutTransitionOffsetForPage(int index) {
- if (!pageScrollsInitialized() || index >= mPageScrolls.length || index < 0) {
+ if (!isPageScrollsInitialized() || index >= mPageScrolls.length || index < 0) {
return 0;
} else {
View child = getChildAt(index);
@@ -1392,15 +1404,17 @@
(isFling && !isVelocityLeft)) && mCurrentPage > 0) {
finalPage = returnToOriginalPage
? mCurrentPage : mCurrentPage - getPanelCount();
- snapToPageWithVelocity(finalPage, velocity);
+ runOnPageScrollsInitialized(
+ () -> snapToPageWithVelocity(finalPage, velocity));
} else if (((isSignificantMove && isDeltaLeft && !isFling) ||
(isFling && isVelocityLeft)) &&
mCurrentPage < getChildCount() - 1) {
finalPage = returnToOriginalPage
? mCurrentPage : mCurrentPage + getPanelCount();
- snapToPageWithVelocity(finalPage, velocity);
+ runOnPageScrollsInitialized(
+ () -> snapToPageWithVelocity(finalPage, velocity));
} else {
- snapToDestination();
+ runOnPageScrollsInitialized(this::snapToDestination);
}
} else {
if (!mScroller.isFinished()) {
@@ -1423,7 +1437,7 @@
int finalPos = mScroller.getFinalX();
mNextPage = getDestinationPage(finalPos);
- onNotSnappingToPageInFreeScroll();
+ runOnPageScrollsInitialized(this::onNotSnappingToPageInFreeScroll);
}
invalidate();
}
@@ -1437,7 +1451,7 @@
case MotionEvent.ACTION_CANCEL:
if (mIsBeingDragged) {
- snapToDestination();
+ runOnPageScrollsInitialized(this::snapToDestination);
}
mEdgeGlowLeft.onRelease();
mEdgeGlowRight.onRelease();
@@ -1602,7 +1616,7 @@
}
private int getDisplacementFromScreenCenter(int childIndex, int screenCenter) {
- int childSize = Math.round(getChildVisibleSize(childIndex));
+ int childSize = getChildVisibleSize(childIndex);
int halfChildSize = (childSize / 2);
int childCenter = getChildOffset(childIndex) + halfChildSize;
return childCenter - screenCenter;
diff --git a/src/com/android/launcher3/Partner.java b/src/com/android/launcher3/Partner.java
deleted file mode 100644
index 2e27f32..0000000
--- a/src/com/android/launcher3/Partner.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright (C) 2014 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.util.PackageManagerHelper.findSystemApk;
-
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.util.Pair;
-
-import java.io.File;
-
-/**
- * Utilities to discover and interact with partner customizations. There can
- * only be one set of customizations on a device, and it must be bundled with
- * the system.
- */
-public class Partner {
-
- static final String TAG = "Launcher.Partner";
-
- /** Marker action used to discover partner */
- private static final String
- ACTION_PARTNER_CUSTOMIZATION = "com.android.launcher3.action.PARTNER_CUSTOMIZATION";
-
- public static final String RES_FOLDER = "partner_folder";
- public static final String RES_WALLPAPERS = "partner_wallpapers";
- public static final String RES_DEFAULT_LAYOUT = "partner_default_layout";
-
- public static final String RES_DEFAULT_WALLPAPER_HIDDEN = "default_wallpapper_hidden";
- public static final String RES_SYSTEM_WALLPAPER_DIR = "system_wallpaper_directory";
-
- public static final String RES_REQUIRE_FIRST_RUN_FLOW = "requires_first_run_flow";
-
- /** These resources are used to override the device profile */
- public static final String RES_GRID_NUM_ROWS = "grid_num_rows";
- public static final String RES_GRID_NUM_COLUMNS = "grid_num_columns";
- public static final String RES_GRID_ICON_SIZE_DP = "grid_icon_size_dp";
-
- /**
- * Find and return partner details, or {@code null} if none exists.
- */
- public static synchronized Partner get(PackageManager pm) {
- Pair<String, Resources> apkInfo = findSystemApk(ACTION_PARTNER_CUSTOMIZATION, pm);
- return apkInfo != null ? new Partner(apkInfo.first, apkInfo.second) : null;
- }
-
- private final String mPackageName;
- private final Resources mResources;
-
- private Partner(String packageName, Resources res) {
- mPackageName = packageName;
- mResources = res;
- }
-
- public String getPackageName() {
- return mPackageName;
- }
-
- public Resources getResources() {
- return mResources;
- }
-
- public boolean hasDefaultLayout() {
- int defaultLayout = getResources().getIdentifier(Partner.RES_DEFAULT_LAYOUT,
- "xml", getPackageName());
- return defaultLayout != 0;
- }
-
- public boolean hasFolder() {
- int folder = getResources().getIdentifier(Partner.RES_FOLDER,
- "xml", getPackageName());
- return folder != 0;
- }
-
- public boolean hideDefaultWallpaper() {
- int resId = getResources().getIdentifier(RES_DEFAULT_WALLPAPER_HIDDEN, "bool",
- getPackageName());
- return resId != 0 && getResources().getBoolean(resId);
- }
-
- public File getWallpaperDirectory() {
- int resId = getResources().getIdentifier(RES_SYSTEM_WALLPAPER_DIR, "string",
- getPackageName());
- return (resId != 0) ? new File(getResources().getString(resId)) : null;
- }
-
- public boolean requiresFirstRunFlow() {
- int resId = getResources().getIdentifier(RES_REQUIRE_FIRST_RUN_FLOW, "bool",
- getPackageName());
- return resId != 0 && getResources().getBoolean(resId);
- }
-
- public void applyInvariantDeviceProfileOverrides(InvariantDeviceProfile inv, DisplayMetrics dm) {
- int numRows = -1;
- int numColumns = -1;
- float iconSize = -1;
-
- try {
- int resId = getResources().getIdentifier(RES_GRID_NUM_ROWS,
- "integer", getPackageName());
- if (resId > 0) {
- numRows = getResources().getInteger(resId);
- }
-
- resId = getResources().getIdentifier(RES_GRID_NUM_COLUMNS,
- "integer", getPackageName());
- if (resId > 0) {
- numColumns = getResources().getInteger(resId);
- }
-
- resId = getResources().getIdentifier(RES_GRID_ICON_SIZE_DP,
- "dimen", getPackageName());
- if (resId > 0) {
- int px = getResources().getDimensionPixelSize(resId);
- iconSize = Utilities.dpiFromPx((float) px, dm.densityDpi);
- }
- } catch (Resources.NotFoundException ex) {
- Log.e(TAG, "Invalid Partner grid resource!", ex);
- return;
- }
-
- if (numRows > 0 && numColumns > 0) {
- inv.numRows = numRows;
- inv.numColumns = numColumns;
- }
-
- if (iconSize > 0) {
- inv.iconSize[InvariantDeviceProfile.INDEX_DEFAULT] = iconSize;
- }
- }
-}
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/SessionCommitReceiver.java b/src/com/android/launcher3/SessionCommitReceiver.java
index bcd4c3f..50ad2be 100644
--- a/src/com/android/launcher3/SessionCommitReceiver.java
+++ b/src/com/android/launcher3/SessionCommitReceiver.java
@@ -98,6 +98,6 @@
}
public static boolean isEnabled(Context context) {
- return Utilities.getPrefs(context).getBoolean(ADD_ICON_PREFERENCE_KEY, true);
+ return LauncherPrefs.getPrefs(context).getBoolean(ADD_ICON_PREFERENCE_KEY, true);
}
}
diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
index 5583eae..5f39f7e 100644
--- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java
+++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
@@ -25,13 +25,16 @@
import android.app.WallpaperManager;
import android.content.Context;
import android.graphics.Point;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.view.MotionEvent;
import android.view.View;
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;
import com.android.launcher3.widget.NavigableAppWidgetHostView;
@@ -78,10 +81,10 @@
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)) {
+ if ((lp.getCellX() <= cellX) && (cellX < lp.getCellX() + lp.cellHSpan)
+ && (lp.getCellY() <= cellY) && (cellY < lp.getCellY() + lp.cellVSpan)) {
return child;
}
}
@@ -105,12 +108,13 @@
}
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);
+ final PointF appWidgetScale = profile.getAppWidgetScale((ItemInfo) child.getTag());
lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX, mCountY,
- profile.appWidgetScale.x, profile.appWidgetScale.y, mBorderSpace, mTempRect);
+ appWidgetScale.x, appWidgetScale.y, mBorderSpace, mTempRect);
} else {
lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX, mCountY,
mBorderSpace, null);
@@ -128,13 +132,14 @@
}
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) {
((NavigableAppWidgetHostView) child).getWidgetInset(dp, mTempRect);
+ final PointF appWidgetScale = dp.getAppWidgetScale((ItemInfo) child.getTag());
lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX, mCountY,
- dp.appWidgetScale.x, dp.appWidgetScale.y, mBorderSpace, mTempRect);
+ appWidgetScale.x, appWidgetScale.y, mBorderSpace, mTempRect);
} else {
lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX, mCountY,
mBorderSpace, null);
@@ -147,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
@@ -171,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);
}
}
@@ -181,14 +185,15 @@
* 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;
// Scale and center the widget to fit within its cells.
DeviceProfile profile = mActivity.getDeviceProfile();
- float scaleX = profile.appWidgetScale.x;
- float scaleY = profile.appWidgetScale.y;
+ final PointF appWidgetScale = profile.getAppWidgetScale((ItemInfo) child.getTag());
+ float scaleX = appWidgetScale.x;
+ float scaleY = appWidgetScale.y;
nahv.setScaleToFit(Math.min(scaleX, scaleY));
nahv.setTranslationForCentering(-(lp.width - (lp.width * scaleX)) / 2.0f,
@@ -250,18 +255,18 @@
@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) {
CellLayout cl = (CellLayout) getParent();
- cl.setFolderLeaveBehindCell(lp.cellX, lp.cellY);
+ cl.setFolderLeaveBehindCell(lp.getCellX(), lp.getCellY());
}
}
@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 dc8c739..02e18c5 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -16,6 +16,9 @@
package com.android.launcher3;
+import static android.view.InputDevice.SOURCE_TOUCHSCREEN;
+
+import static com.android.launcher3.config.FeatureFlags.ENABLE_TRACKPAD_GESTURE;
import static com.android.launcher3.icons.BitmapInfo.FLAG_THEMED;
import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_ICON_BADGED;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
@@ -26,20 +29,12 @@
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 +46,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;
@@ -68,28 +62,26 @@
import android.util.Log;
import android.util.TypedValue;
import android.view.MotionEvent;
+import android.view.VelocityTracker;
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;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
-import com.android.launcher3.model.data.SearchActionItemInfo;
import com.android.launcher3.pm.ShortcutConfigActivityInfo;
import com.android.launcher3.shortcuts.ShortcutKey;
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;
@@ -98,10 +90,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;
@@ -115,8 +105,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();
@@ -165,17 +153,11 @@
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();
+ private static final int TRACKPAD_GESTURE_SCALE = 60;
+
public static void enableRunningInTestHarnessForTests() {
IS_RUNNING_IN_TEST_HARNESS = true;
}
@@ -184,12 +166,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.
@@ -303,9 +279,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) {
@@ -326,80 +302,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);
}
/**
@@ -417,6 +321,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.
@@ -452,30 +383,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.
*/
@@ -519,14 +426,19 @@
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);
}
public static int pxFromSp(float size, DisplayMetrics metrics, float scale) {
- return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
- size, metrics) * scale);
+ float value = scale * TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, size, metrics);
+ return ResourceUtils.roundPxValueFromFloat(value);
}
public static String createDbSelectionQuery(String columnName, IntArray values) {
@@ -552,18 +464,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.
@@ -613,18 +513,6 @@
return spanned;
}
- public static SharedPreferences getPrefs(Context context) {
- // Use application context for shared preferences, so that we use a single cached instance
- return context.getApplicationContext().getSharedPreferences(
- LauncherFiles.SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE);
- }
-
- public static SharedPreferences getDevicePrefs(Context context) {
- // Use application context for shared preferences, so that we use a single cached instance
- return context.getApplicationContext().getSharedPreferences(
- LauncherFiles.DEVICE_PREFERENCES_KEY, Context.MODE_PRIVATE);
- }
-
public static boolean isWallpaperSupported(Context context) {
return context.getSystemService(WallpaperManager.class).isWallpaperSupported();
}
@@ -638,42 +526,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.
*/
@@ -683,12 +535,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.
*
@@ -750,8 +596,8 @@
outObj[0] = icon;
return icon;
} else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SEARCH_ACTION
- && info instanceof SearchActionItemInfo) {
- return ((SearchActionItemInfo) info).bitmap.newIcon(context);
+ && info instanceof ItemInfoWithIcon) {
+ return ((ItemInfoWithIcon) info).bitmap.newIcon(context);
} else {
return null;
}
@@ -786,14 +632,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;
}
@@ -804,28 +642,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.
@@ -873,16 +689,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);
@@ -923,4 +729,53 @@
}
return options;
}
+
+ public static boolean isTrackpadMotionEvent(MotionEvent event) {
+ // TODO: ideally should use event.getClassification(), but currently only the move
+ // events get assigned the correct classification.
+ return ENABLE_TRACKPAD_GESTURE.get()
+ && (event.getSource() & SOURCE_TOUCHSCREEN) != SOURCE_TOUCHSCREEN;
+ }
+
+ public static int getTrackpadMotionEventScale(Context context) {
+ return ViewConfiguration.get(context).getScaledTouchSlop() * TRACKPAD_GESTURE_SCALE;
+ }
+
+ public static float getXVelocity(VelocityTracker velocityTracker, MotionEvent event,
+ int pointerId) {
+ // Will be enabled after ag/20353570 is submitted
+// if (isTrackpadMotionEvent(event)) {
+// return velocityTracker.getAxisVelocity(AXIS_GESTURE_X_OFFSET, pointerId);
+// } else {
+ return velocityTracker.getXVelocity(pointerId);
+// }
+ }
+
+ public static float getXVelocity(VelocityTracker velocityTracker, MotionEvent event) {
+ return getXVelocity(velocityTracker, event, -1 /* ACTIVE_POINTER_ID */);
+ }
+
+ public static float getYVelocity(VelocityTracker velocityTracker, MotionEvent event,
+ int pointerId) {
+ // Will be enabled after ag/20353570 is submitted
+// if (isTrackpadMotionEvent(event)) {
+// return velocityTracker.getAxisVelocity(AXIS_GESTURE_Y_OFFSET, pointerId);
+// } else {
+ return velocityTracker.getYVelocity(pointerId);
+// }
+ }
+
+ public static float getYVelocity(VelocityTracker velocityTracker, MotionEvent event) {
+ return getYVelocity(velocityTracker, event, -1 /* ACTIVE_POINTER_ID */);
+ }
+
+ /** Logs the Scale and Translate properties of a matrix. Ignores skew and perspective. */
+ public static void logMatrix(String label, Matrix matrix) {
+ float[] matrixValues = new float[9];
+ matrix.getValues(matrixValues);
+ Log.d(label, String.format("%s: %s\nscale (x,y) = (%f, %f)\ntranslate (x,y) = (%f, %f)",
+ label, matrix, matrixValues[Matrix.MSCALE_X], matrixValues[Matrix.MSCALE_Y],
+ matrixValues[Matrix.MTRANS_X], matrixValues[Matrix.MTRANS_Y]
+ ));
+ }
}
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index e025790..460c658 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -26,11 +26,9 @@
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;
-import static com.android.launcher3.testing.shared.TestProtocol.BAD_STATE;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -45,6 +43,7 @@
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Point;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Handler;
@@ -53,20 +52,23 @@
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;
+import androidx.annotation.VisibleForTesting;
import com.android.launcher3.accessibility.AccessibleDragListenerAdapter;
import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dot.FolderDotInfo;
import com.android.launcher3.dragndrop.DragController;
@@ -106,9 +108,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 +119,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 +137,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,16 +155,16 @@
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;
protected ShortcutAndWidgetContainer mDragSourceInternal;
- @Thunk final IntSparseArrayMap<CellLayout> mWorkspaceScreens = new IntSparseArrayMap<>();
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ @Thunk public final IntSparseArrayMap<CellLayout> mWorkspaceScreens = new IntSparseArrayMap<>();
+
@Thunk final IntArray mScreenOrder = new IntArray();
@Thunk boolean mDeferRemoveExtraEmptyScreen = false;
@@ -223,8 +226,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 +257,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 +291,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 +327,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() {
@@ -370,7 +391,8 @@
float scale = 1;
if (isWidget) {
DeviceProfile profile = mLauncher.getDeviceProfile();
- scale = Utilities.shrinkRect(r, profile.appWidgetScale.x, profile.appWidgetScale.y);
+ final PointF appWidgetScale = profile.getAppWidgetScale(null);
+ scale = Utilities.shrinkRect(r, appWidgetScale.x, appWidgetScale.y);
}
size[0] = r.width();
size[1] = r.height();
@@ -553,22 +575,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, FIRST_SCREEN_ID);
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;
}
}
@@ -577,9 +599,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
@@ -899,6 +921,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.
@@ -1050,20 +1076,23 @@
mXDown = ev.getX();
mYDown = ev.getY();
- if (mQsb != 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];
+ if (mFirstPagePinnedItem != null) {
+ final float[] tempFXY = new float[2];
+ tempFXY[0] = mXDown;
+ tempFXY[1] = mYDown;
+ Utilities.mapCoordInSelfToDescendant(mFirstPagePinnedItem, this, tempFXY);
+ mIsEventOverFirstPagePinnedItem = mFirstPagePinnedItem.getLeft() <= tempFXY[0]
+ && mFirstPagePinnedItem.getRight() >= tempFXY[0]
+ && mFirstPagePinnedItem.getTop() <= tempFXY[1]
+ && mFirstPagePinnedItem.getBottom() >= tempFXY[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);
@@ -1124,9 +1153,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 {
@@ -1176,133 +1211,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);
- Log.d(BAD_STATE, "Workspace onOverlayScrollChanged DragLayer ALPHA_INDEX_OVERLAY=" + alpha);
- 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 +1625,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,8 +1756,9 @@
boolean willCreateUserFolder(ItemInfo info, View dropOverView, boolean considerTimeout) {
if (dropOverView != null) {
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) dropOverView.getLayoutParams();
- if (lp.useTmpCoords && (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.cellY)) {
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) dropOverView.getLayoutParams();
+ if (lp.useTmpCoords && (lp.getTmpCellX() != lp.getCellX()
+ || lp.getTmpCellY() != lp.getCellY())) {
return false;
}
}
@@ -1837,8 +1792,9 @@
}
boolean willAddToExistingUserFolder(ItemInfo dragInfo, View dropOverView) {
if (dropOverView != null) {
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) dropOverView.getLayoutParams();
- if (lp.useTmpCoords && (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.cellY)) {
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) dropOverView.getLayoutParams();
+ if (lp.useTmpCoords && (lp.getTmpCellX() != lp.getCellX()
+ || lp.getTmpCellY() != lp.getCellY())) {
return false;
}
}
@@ -2054,32 +2010,25 @@
}
// update the item's position after drop
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams();
- lp.cellX = lp.tmpCellX = mTargetCell[0];
- lp.cellY = lp.tmpCellY = mTargetCell[1];
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) cell.getLayoutParams();
+ lp.setTmpCellX(mTargetCell[0]);
+ lp.setCellX(mTargetCell[0]);
+ lp.setTmpCellY(mTargetCell[1]);
+ lp.setCellY(mTargetCell[1]);
lp.cellHSpan = item.spanX;
lp.cellVSpan = item.spanY;
lp.isLockedToGrid = true;
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);
+ lp.getCellX(), lp.getCellY(), item.spanX, item.spanY);
} else {
if (!returnToOriginalCellToPreventShuffling) {
onNoCellFound(dropTargetLayout, d.dragInfo, d.logInstanceId);
@@ -2089,16 +2038,24 @@
}
// If we can't find a drop location, we return the item to its original position
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams();
- mTargetCell[0] = lp.cellX;
- mTargetCell[1] = lp.cellY;
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) cell.getLayoutParams();
+ mTargetCell[0] = lp.getCellX();
+ mTargetCell[1] = lp.getCellY();
CellLayout layout = (CellLayout) cell.getParent().getParent();
layout.markCellsAsOccupiedForView(cell);
}
} 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);
+ }
}
}
@@ -2124,7 +2081,8 @@
final ItemInfo info = (ItemInfo) cell.getTag();
boolean isWidget = info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
|| info.itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
- if (isWidget) {
+ if (isWidget && dropTargetLayout != null) {
+ // animate widget to a valid place
int animationType = resizeOnDrop ? ANIMATE_INTO_POSITION_AND_RESIZE :
ANIMATE_INTO_POSITION_AND_DISAPPEAR;
animateWidgetDrop(info, parent, d.dragView, null, animationType, cell, false);
@@ -2150,6 +2108,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)
@@ -2386,7 +2359,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];
@@ -2402,7 +2375,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) {
@@ -2414,25 +2388,26 @@
}
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.performReorder((int) mDragViewVisualCenter[0],
+ (int) mDragViewVisualCenter[1], minSpanX, minSpanY, item.spanX, item.spanY,
+ child, mTargetCell, new int[2], CellLayout.MODE_SHOW_REORDER_HINT);
mDragTargetLayout.visualizeDropLocation(mTargetCell[0], mTargetCell[1],
item.spanX, item.spanY, d);
} else if ((mDragMode == DRAG_MODE_NONE || mDragMode == DRAG_MODE_REORDER)
- && !mReorderAlarm.alarmPending()
&& (mLastReorderX != reorderX || mLastReorderY != reorderY)
- && targetCellDistance < mDragTargetLayout.getReorderRadius(mTargetCell)) {
-
- int[] resultSpan = new int[2];
+ && targetCellDistance < mDragTargetLayout.getReorderRadius(mTargetCell, item.spanX,
+ item.spanY)) {
+ mReorderAlarm.cancelAlarm();
+ 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,
@@ -2493,10 +2468,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);
}
@@ -2637,8 +2612,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,
@@ -2650,7 +2623,6 @@
setDragMode(DRAG_MODE_REORDER);
}
- boolean resize = resultSpan[0] != spanX || resultSpan[1] != spanY;
mDragTargetLayout.visualizeDropLocation(mTargetCell[0], mTargetCell[1],
resultSpan[0], resultSpan[1], dragObject);
}
@@ -2884,7 +2856,8 @@
r.top -= widgetPadding.top;
r.bottom += widgetPadding.bottom;
}
- Utilities.shrinkRect(r, profile.appWidgetScale.x, profile.appWidgetScale.y);
+ PointF appWidgetScale = profile.getAppWidgetScale(null);
+ Utilities.shrinkRect(r, appWidgetScale.x, appWidgetScale.y);
}
mTempFXY[0] = r.left;
@@ -3006,7 +2979,7 @@
*/
@Thunk int[] findNearestArea(int pixelX, int pixelY,
int spanX, int spanY, CellLayout layout, int[] recycle) {
- return layout.findNearestArea(
+ return layout.findNearestAreaIgnoreOccupied(
pixelX, pixelY, spanX, spanY, recycle);
}
@@ -3341,7 +3314,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;
@@ -3418,7 +3391,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
@@ -3467,19 +3440,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);
@@ -3489,7 +3462,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..bf448c9 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,13 +112,13 @@
}
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, screenId);
} else {
- lp = (CellLayout.LayoutParams) genericLp;
- lp.cellX = x;
- lp.cellY = y;
+ lp = (CellLayoutLayoutParams) genericLp;
+ lp.setCellX(x);
+ lp.setCellY(y);
lp.cellHSpan = spanX;
lp.cellVSpan = spanY;
}
@@ -135,7 +136,8 @@
// TODO: This branch occurs when the workspace is adding views
// outside of the defined grid
// maybe we should be deleting these items from the LauncherModel?
- Log.e(TAG, "Failed to add to item at (" + lp.cellX + "," + lp.cellY + ") to CellLayout");
+ Log.e(TAG, "Failed to add to item at (" + lp.getCellX() + "," + lp.getCellY()
+ + ") to CellLayout");
}
child.setHapticFeedbackEnabled(false);
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..3c316b8 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);
@@ -252,7 +259,7 @@
if (((host.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL)
&& layout.isRegionVacant(info.cellX - 1, info.cellY, 1, info.spanY))
|| !layout.isRegionVacant(info.cellX + info.spanX, info.cellY, 1, info.spanY)) {
- lp.cellX --;
+ lp.setCellX(lp.getCellX() - 1);
info.cellX --;
}
lp.cellHSpan ++;
@@ -262,7 +269,7 @@
info.spanX --;
} else if (action == R.string.action_increase_height) {
if (!layout.isRegionVacant(info.cellX, info.cellY + info.spanY, info.spanX, 1)) {
- lp.cellY --;
+ lp.setCellY(lp.getCellY() - 1);
info.cellY --;
}
lp.cellVSpan ++;
diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
index 53a6fd7..2511cada 100644
--- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
@@ -15,8 +15,9 @@
*/
package com.android.launcher3.allapps;
+import static com.android.launcher3.allapps.BaseAllAppsContainerView.AdapterHolder.SEARCH;
+
import android.content.Context;
-import android.content.Intent;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.MotionEvent;
@@ -26,34 +27,30 @@
import androidx.core.graphics.ColorUtils;
import androidx.recyclerview.widget.RecyclerView;
-import com.android.launcher3.DeviceProfile.DeviceProfileListenable;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.BaseAllAppsAdapter.AdapterItem;
-import com.android.launcher3.allapps.search.SearchAdapterProvider;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.util.PackageManagerHelper;
-import com.android.launcher3.views.AppLauncher;
+import com.android.launcher3.views.ActivityContext;
import java.util.ArrayList;
-import java.util.Objects;
/**
* All apps container view with search support for use in a dragging activity.
*
* @param <T> Type of context inflating all apps.
*/
-public class ActivityAllAppsContainerView<T extends Context & AppLauncher
- & DeviceProfileListenable> extends BaseAllAppsContainerView<T> {
+public class ActivityAllAppsContainerView<T extends Context & ActivityContext>
+ extends BaseAllAppsContainerView<T> {
- protected SearchUiManager mSearchUiManager;
- /**
- * View that defines the search box. Result is rendered inside the recycler view defined in the
- * base class.
- */
- private View mSearchContainer;
+ 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;
+
/** {@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);
@@ -65,6 +62,14 @@
public ActivityAllAppsContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
+
+ mSearchTransitionController = new SearchTransitionController(this);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mSearchUiManager.initializeSearch(this);
}
public SearchUiManager getSearchUiManager() {
@@ -75,44 +80,65 @@
return mSearchContainer;
}
- /** Updates all apps container with the latest search query. */
- public void setLastSearchQuery(String query) {
- Intent marketSearchIntent = PackageManagerHelper.getMarketSearchIntent(
- mActivityContext, query);
- OnClickListener marketSearchClickListener = (v) -> mActivityContext.startActivitySafely(v,
- marketSearchIntent, null);
- for (int i = 0; i < mAH.size(); i++) {
- mAH.get(i).mAdapter.setLastSearchQuery(query, marketSearchClickListener);
- }
- 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);
}
}
- @Override
- protected final SearchAdapterProvider<?> createMainAdapterProvider() {
- return mActivityContext.createSearchAdapterProvider(this);
+ /**
+ * 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
@@ -130,14 +156,8 @@
super.reset(animate);
// Reset the search bar after transitioning home.
mSearchUiManager.resetSearch();
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mSearchContainer = findViewById(R.id.search_container_all_apps);
- mSearchUiManager = (SearchUiManager) mSearchContainer;
- mSearchUiManager.initializeSearch(this);
+ // Animate to A-Z with 0 time to reset the animation with proper state management.
+ animateToSearchState(false, 0);
}
@Override
@@ -156,31 +176,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
@@ -189,7 +213,9 @@
removeCustomRules(rvContainer);
removeCustomRules(getSearchRecyclerView());
- if (FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get()) {
+ if (!isSearchSupported()) {
+ layoutWithoutSearchContainer(rvContainer, showTabs);
+ } else if (FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get()) {
alignParentTop(rvContainer, showTabs);
alignParentTop(getSearchRecyclerView(), /* tabs= */ false);
layoutAboveSearchContainer(rvContainer);
@@ -238,14 +264,6 @@
(int) (mSearchContainer.getAlpha() * 255));
}
- @Override
- public int getHeaderBottom() {
- if (FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get()) {
- return super.getHeaderBottom();
- }
- return super.getHeaderBottom() + mSearchContainer.getBottom();
- }
-
private void layoutBelowSearchContainer(View v, boolean includeTabsMargin) {
if (!(v.getLayoutParams() instanceof RelativeLayout.LayoutParams)) {
return;
@@ -282,7 +300,7 @@
layoutParams.topMargin =
includeTabsMargin
? getContext().getResources().getDimensionPixelSize(
- R.dimen.all_apps_header_pill_height)
+ R.dimen.all_apps_header_pill_height)
: 0;
}
@@ -303,4 +321,27 @@
return new AllAppsGridAdapter<>(mActivityContext, getLayoutInflater(), appsList,
adapterProviders);
}
+
+ // TODO(b/216683257): Remove when Taskbar All Apps supports search.
+ protected boolean isSearchSupported() {
+ return true;
+ }
+
+ private void layoutWithoutSearchContainer(View v, boolean includeTabsMargin) {
+ if (!(v.getLayoutParams() instanceof RelativeLayout.LayoutParams)) {
+ return;
+ }
+
+ RelativeLayout.LayoutParams layoutParams = (LayoutParams) v.getLayoutParams();
+ layoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP);
+ layoutParams.topMargin = getContext().getResources().getDimensionPixelSize(includeTabsMargin
+ ? R.dimen.all_apps_header_pill_height
+ : R.dimen.all_apps_header_top_margin);
+ }
+
+ @Override
+ public boolean isInAllApps() {
+ // TODO: Make this abstract
+ return true;
+ }
}
diff --git a/src/com/android/launcher3/allapps/AllAppsBackgroundDrawable.java b/src/com/android/launcher3/allapps/AllAppsBackgroundDrawable.java
deleted file mode 100644
index 3830a93..0000000
--- a/src/com/android/launcher3/allapps/AllAppsBackgroundDrawable.java
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.allapps;
-
-import android.animation.ObjectAnimator;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.ColorFilter;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.view.ContextThemeWrapper;
-import android.view.Gravity;
-
-import com.android.launcher3.LauncherAnimUtils;
-import com.android.launcher3.R;
-import com.android.launcher3.util.Themes;
-
-/**
- * This is a custom composite drawable that has a fixed virtual size and dynamically lays out its
- * children images relatively within its bounds. This way, we can reduce the memory usage of a
- * single, large sparsely populated image.
- */
-public class AllAppsBackgroundDrawable extends Drawable {
-
- /**
- * A helper class to position and orient a drawable to be drawn.
- */
- protected static class TransformedImageDrawable {
- private Drawable mImage;
- private float mXPercent;
- private float mYPercent;
- private int mGravity;
- private int mAlpha;
-
- /**
- * @param gravity If one of the Gravity center values, the x and y offset will take the width
- * and height of the image into account to center the image to the offset.
- */
- public TransformedImageDrawable(Context context, int resourceId, float xPct, float yPct,
- int gravity) {
- mImage = context.getDrawable(resourceId);
- mXPercent = xPct;
- mYPercent = yPct;
- mGravity = gravity;
- }
-
- public void setAlpha(int alpha) {
- mImage.setAlpha(alpha);
- mAlpha = alpha;
- }
-
- public int getAlpha() {
- return mAlpha;
- }
-
- public void updateBounds(Rect bounds) {
- int width = mImage.getIntrinsicWidth();
- int height = mImage.getIntrinsicHeight();
- int left = bounds.left + (int) (mXPercent * bounds.width());
- int top = bounds.top + (int) (mYPercent * bounds.height());
- if ((mGravity & Gravity.CENTER_HORIZONTAL) == Gravity.CENTER_HORIZONTAL) {
- left -= (width / 2);
- }
- if ((mGravity & Gravity.CENTER_VERTICAL) == Gravity.CENTER_VERTICAL) {
- top -= (height / 2);
- }
- mImage.setBounds(left, top, left + width, top + height);
- }
-
- public void draw(Canvas canvas) {
- mImage.draw(canvas);
- }
-
- public Rect getBounds() {
- return mImage.getBounds();
- }
- }
-
- protected final TransformedImageDrawable mHand;
- protected final TransformedImageDrawable[] mIcons;
- private final int mWidth;
- private final int mHeight;
-
- private ObjectAnimator mBackgroundAnim;
-
- public AllAppsBackgroundDrawable(Context context) {
- Resources res = context.getResources();
- mWidth = res.getDimensionPixelSize(R.dimen.all_apps_background_canvas_width);
- mHeight = res.getDimensionPixelSize(R.dimen.all_apps_background_canvas_height);
-
- context = new ContextThemeWrapper(context,
- Themes.getAttrBoolean(context, R.attr.isMainColorDark)
- ? R.style.AllAppsEmptySearchBackground_Dark
- : R.style.AllAppsEmptySearchBackground);
- mHand = new TransformedImageDrawable(context, R.drawable.ic_all_apps_bg_hand,
- 0.575f, 0.f, Gravity.CENTER_HORIZONTAL);
- mIcons = new TransformedImageDrawable[4];
- mIcons[0] = new TransformedImageDrawable(context, R.drawable.ic_all_apps_bg_icon_1,
- 0.375f, 0, Gravity.CENTER_HORIZONTAL);
- mIcons[1] = new TransformedImageDrawable(context, R.drawable.ic_all_apps_bg_icon_2,
- 0.3125f, 0.2f, Gravity.CENTER_HORIZONTAL);
- mIcons[2] = new TransformedImageDrawable(context, R.drawable.ic_all_apps_bg_icon_3,
- 0.475f, 0.26f, Gravity.CENTER_HORIZONTAL);
- mIcons[3] = new TransformedImageDrawable(context, R.drawable.ic_all_apps_bg_icon_4,
- 0.7f, 0.125f, Gravity.CENTER_HORIZONTAL);
- }
-
- /**
- * Animates the background alpha.
- */
- public void animateBgAlpha(float finalAlpha, int duration) {
- int finalAlphaI = (int) (finalAlpha * 255f);
- if (getAlpha() != finalAlphaI) {
- mBackgroundAnim = cancelAnimator(mBackgroundAnim);
- mBackgroundAnim = ObjectAnimator.ofInt(this, LauncherAnimUtils.DRAWABLE_ALPHA,
- finalAlphaI);
- mBackgroundAnim.setDuration(duration);
- mBackgroundAnim.start();
- }
- }
-
- /**
- * Sets the background alpha immediately.
- */
- public void setBgAlpha(float finalAlpha) {
- int finalAlphaI = (int) (finalAlpha * 255f);
- if (getAlpha() != finalAlphaI) {
- mBackgroundAnim = cancelAnimator(mBackgroundAnim);
- setAlpha(finalAlphaI);
- }
- }
-
- @Override
- public int getIntrinsicWidth() {
- return mWidth;
- }
-
- @Override
- public int getIntrinsicHeight() {
- return mHeight;
- }
-
- @Override
- public void draw(Canvas canvas) {
- mHand.draw(canvas);
- for (int i = 0; i < mIcons.length; i++) {
- mIcons[i].draw(canvas);
- }
- }
-
- @Override
- protected void onBoundsChange(Rect bounds) {
- super.onBoundsChange(bounds);
- mHand.updateBounds(bounds);
- for (int i = 0; i < mIcons.length; i++) {
- mIcons[i].updateBounds(bounds);
- }
- invalidateSelf();
- }
-
- @Override
- public void setAlpha(int alpha) {
- mHand.setAlpha(alpha);
- for (int i = 0; i < mIcons.length; i++) {
- mIcons[i].setAlpha(alpha);
- }
- invalidateSelf();
- }
-
- @Override
- public int getAlpha() {
- return mHand.getAlpha();
- }
-
- @Override
- public void setColorFilter(ColorFilter colorFilter) {
- // Do nothing
- }
-
- @Override
- public int getOpacity() {
- return PixelFormat.TRANSLUCENT;
- }
-
- private ObjectAnimator cancelAnimator(ObjectAnimator animator) {
- if (animator != null) {
- animator.cancel();
- }
- return null;
- }
-}
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index 33d2f2b..112d47e 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -21,15 +21,19 @@
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
+import androidx.annotation.Px;
import androidx.core.view.accessibility.AccessibilityEventCompat;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
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.List;
+import java.util.concurrent.CopyOnWriteArrayList;
/**
* The grid view adapter of all the apps.
@@ -40,32 +44,62 @@
BaseAllAppsAdapter<T> {
public static final String TAG = "AppsGridAdapter";
- private final GridLayoutManager mGridLayoutMgr;
- private final GridSpanSizer mGridSizer;
+ private final AppsGridLayoutManager mGridLayoutMgr;
+ private final CopyOnWriteArrayList<OnLayoutCompletedListener> mOnLayoutCompletedListeners =
+ new CopyOnWriteArrayList<>();
+
+ /**
+ * 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
@@ -111,6 +145,19 @@
}
/**
+ * We need to extend all apps' RecyclerView's bottom by 5% of view height to ensure extra
+ * roll(s) of app icons is rendered at the bottom, so that they can fill the bottom gap
+ * created during predictive back's scale animation from all apps to home.
+ */
+ @Override
+ protected void calculateExtraLayoutSpace(RecyclerView.State state, int[] extraLayoutSpace) {
+ super.calculateExtraLayoutSpace(state, extraLayoutSpace);
+ @Px int extraSpacePx = (int) (getHeight()
+ * (1 - AllAppsTransitionController.SWIPE_ALL_APPS_TO_HOME_MIN_SCALE) / 2);
+ extraLayoutSpace[1] = Math.max(extraLayoutSpace[1], extraSpacePx);
+ }
+
+ /**
* Returns the number of rows before {@param adapterPosition}, including this position
* which should not be counted towards the collection info.
*/
@@ -125,6 +172,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
@@ -153,8 +217,12 @@
@Override
public int getSpanSize(int position) {
- int viewType = mApps.getAdapterItems().get(position).viewType;
int totalSpans = mGridLayoutMgr.getSpanCount();
+ List<AdapterItem> items = mApps.getAdapterItems();
+ if (position >= items.size()) {
+ return totalSpans;
+ }
+ int viewType = items.get(position).viewType;
if (isIconViewType(viewType)) {
return totalSpans / mAppsPerRow;
} else {
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index de34416..8cb31fa 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -15,31 +15,32 @@
*/
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_PERSONAL_SCROLLED_DOWN;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_PERSONAL_SCROLLED_UP;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_SCROLLED_UNKNOWN_DIRECTION;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_SEARCH_SCROLLED_DOWN;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_SEARCH_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.logging.StatsLogManager.LauncherEvent.LAUNCHER_WORK_FAB_BUTTON_COLLAPSE;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WORK_FAB_BUTTON_EXTEND;
import static com.android.launcher3.util.LogConfig.SEARCH_LOGGING;
-import static com.android.launcher3.util.UiThreadHelper.hideKeyboardAsync;
import android.content.Context;
-import android.content.res.Resources;
import android.graphics.Canvas;
-import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
-import android.util.SparseIntArray;
-import android.view.MotionEvent;
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;
import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.RecyclerViewFastScroller;
@@ -54,44 +55,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();
- }
- };
-
- // The empty-search result background
- protected AllAppsBackgroundDrawable mEmptySearchBackground;
- protected int mEmptySearchBackgroundTopOffset;
+ protected AlphabeticalAppsList<?> mApps;
public AllAppsRecyclerView(Context context) {
this(context, null);
@@ -108,9 +76,6 @@
public AllAppsRecyclerView(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr);
- Resources res = getResources();
- mEmptySearchBackgroundTopOffset = res.getDimensionPixelSize(
- R.dimen.all_apps_empty_search_bg_top_offset);
mNumAppsPerRow = LauncherAppState.getIDP(context).numColumns;
mFastScrollHelper = new AllAppsFastScrollHelper(this);
}
@@ -132,21 +97,12 @@
int approxRows = (int) Math.ceil(grid.availableHeightPx / grid.allAppsIconSizePx);
pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_EMPTY_SEARCH, 1);
pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_ALL_APPS_DIVIDER, 1);
- pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET, 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) {
- // Draw the background
- if (mEmptySearchBackground != null && mEmptySearchBackground.getAlpha() > 0) {
- mEmptySearchBackground.draw(c);
- }
if (DEBUG) {
Log.d(TAG, "onDraw at = " + System.currentTimeMillis());
}
@@ -158,33 +114,13 @@
}
@Override
- protected boolean verifyDrawable(Drawable who) {
- return who == mEmptySearchBackground || super.verifyDrawable(who);
- }
-
- @Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- updateEmptySearchBackgroundBounds();
updatePoolSize();
}
public void onSearchResultsChanged() {
// Always scroll the view to the top so the user can see the changed results
scrollToTop();
-
- if (mApps.hasNoFilteredResults() && !FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
- if (mEmptySearchBackground == null) {
- mEmptySearchBackground = new AllAppsBackgroundDrawable(getContext());
- mEmptySearchBackground.setAlpha(0);
- mEmptySearchBackground.setCallback(this);
- updateEmptySearchBackgroundBounds();
- }
- mEmptySearchBackground.animateBgAlpha(1f, 150);
- } else if (mEmptySearchBackground != null) {
- // For the time being, we just immediately hide the background to ensure that it does
- // not overlap with the results
- mEmptySearchBackground.setBgAlpha(0f);
- }
}
@Override
@@ -194,28 +130,24 @@
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 boolean onInterceptTouchEvent(MotionEvent e) {
- boolean result = super.onInterceptTouchEvent(e);
- if (!result && e.getAction() == MotionEvent.ACTION_DOWN
- && mEmptySearchBackground != null && mEmptySearchBackground.getAlpha() > 0) {
- mEmptySearchBackground.setHotspot(e.getX(), e.getY());
- }
- return result;
+ public void onScrolled(int dx, int dy) {
+ super.onScrolled(dx, dy);
+ mCumulativeVerticalScroll += dy;
}
/**
@@ -248,17 +180,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;
}
@@ -279,13 +200,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;
@@ -340,77 +261,55 @@
}
}
- @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);
+ return ActivityContext.lookupContext(getContext()).getAppsView().isSearchSupported()
+ ? getResources().getDimensionPixelOffset(R.dimen.all_apps_header_top_padding)
+ : 0;
}
public RecyclerViewFastScroller getScrollbar() {
return mScrollbar;
}
- /**
- * Updates the bounds of the empty search background.
- */
- private void updateEmptySearchBackgroundBounds() {
- if (mEmptySearchBackground == null) {
- return;
- }
-
- // Center the empty search background on this new view bounds
- int x = (getMeasuredWidth() - mEmptySearchBackground.getIntrinsicWidth()) / 2;
- int y = mEmptySearchBackgroundTopOffset;
- mEmptySearchBackground.setBounds(x, y,
- x + mEmptySearchBackground.getIntrinsicWidth(),
- y + mEmptySearchBackground.getIntrinsicHeight());
- }
-
@Override
public boolean hasOverlappingRendering() {
return false;
}
+
+ private void logCumulativeVerticalScroll() {
+ ActivityContext context = ActivityContext.lookupContext(getContext());
+ StatsLogManager mgr = context.getStatsLogManager();
+ ActivityAllAppsContainerView<?> appsView = context.getAppsView();
+ ExtendedEditText editText = appsView.getSearchUiManager().getEditText();
+ ContainerInfo containerInfo = ContainerInfo.newBuilder().setSearchResultContainer(
+ SearchResultContainer
+ .newBuilder()
+ .setQueryLength((editText == null) ? -1 : editText.length())).build();
+ if (mCumulativeVerticalScroll == 0) {
+ // mCumulativeVerticalScroll == 0 when user comes back to original position, we
+ // don't know the direction of scrolling.
+ mgr.logger().withContainerInfo(containerInfo).log(
+ LAUNCHER_ALLAPPS_SCROLLED_UNKNOWN_DIRECTION);
+ return;
+ } else if (appsView.isSearching()) {
+ // In search results page
+ mgr.logger().withContainerInfo(containerInfo).log((mCumulativeVerticalScroll > 0)
+ ? LAUNCHER_ALLAPPS_SEARCH_SCROLLED_DOWN
+ : LAUNCHER_ALLAPPS_SEARCH_SCROLLED_UP);
+ return;
+ } else if (appsView.mViewPager != null) {
+ int currentPage = appsView.mViewPager.getCurrentPage();
+ if (currentPage == BaseAllAppsContainerView.AdapterHolder.WORK) {
+ // In work A-Z list
+ mgr.logger().withContainerInfo(containerInfo).log((mCumulativeVerticalScroll > 0)
+ ? LAUNCHER_WORK_FAB_BUTTON_COLLAPSE
+ : LAUNCHER_WORK_FAB_BUTTON_EXTEND);
+ return;
+ }
+ }
+ // In personal A-Z list
+ mgr.logger().withContainerInfo(containerInfo).log((mCumulativeVerticalScroll > 0)
+ ? LAUNCHER_ALLAPPS_PERSONAL_SCROLLED_DOWN
+ : LAUNCHER_ALLAPPS_PERSONAL_SCROLLED_UP);
+ }
}
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index bdab03f..f7b0d96 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.allapps;
+import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
+import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.ALL_APPS_CONTENT;
import static com.android.launcher3.LauncherState.NORMAL;
@@ -23,6 +25,9 @@
import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS;
+import static com.android.launcher3.util.SystemUiController.FLAG_DARK_NAV;
+import static com.android.launcher3.util.SystemUiController.FLAG_LIGHT_NAV;
+import static com.android.launcher3.util.SystemUiController.UI_STATE_ALL_APPS;
import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
@@ -30,20 +35,29 @@
import android.util.FloatProperty;
import android.view.HapticFeedbackConstants;
import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
import android.view.animation.Interpolator;
+import androidx.annotation.FloatRange;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
+import com.android.launcher3.R;
+import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.AnimatorListeners;
+import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.PropertySetter;
-import com.android.launcher3.config.FeatureFlags;
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.util.Themes;
import com.android.launcher3.views.ScrimView;
/**
@@ -60,6 +74,10 @@
implements StateHandler<LauncherState>, OnDeviceProfileChangeListener {
// This constant should match the second derivative of the animator interpolator.
public static final float INTERP_COEFF = 1.7f;
+ public static final float SWIPE_ALL_APPS_TO_HOME_MIN_SCALE = 0.9f;
+ private static final int REVERT_SWIPE_ALL_APPS_TO_HOME_ANIMATION_DURATION_MS = 200;
+
+ private static final float NAV_BAR_COLOR_FORCE_UPDATE_THRESHOLD = 0.1f;
public static final FloatProperty<AllAppsTransitionController> ALL_APPS_PROGRESS =
new FloatProperty<AllAppsTransitionController>("allAppsProgress") {
@@ -75,6 +93,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 +103,7 @@
if (controller.mIsTablet) {
return controller.mAppsView.getActiveRecyclerView().getTranslationY();
} else {
- return controller.getAppsViewPullbackTranslationY().get(
- controller.mAppsView);
+ return controller.getAppsViewPullbackTranslationY().getValue();
}
}
@@ -92,13 +111,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 +139,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);
}
}
};
@@ -128,8 +156,14 @@
private ActivityAllAppsContainerView<Launcher> mAppsView;
private final Launcher mLauncher;
+ private final AnimatedFloat mAllAppScale = new AnimatedFloat(this::onScaleProgressChanged);
+ private final int mNavScrimFlag;
+
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,20 +175,23 @@
private ScrimView mScrimView;
- private final MultiAdditivePropertyFactory<View>
- mAppsViewTranslationYPropertyFactory = new MultiAdditivePropertyFactory<>(
- "appsViewTranslationY", View.TRANSLATION_Y);
private MultiValueAlpha mAppsViewAlpha;
+ private MultiPropertyFactory<View> mAppsViewTranslationY;
private boolean mIsTablet;
+ private boolean mHasScaleEffect;
+
public AllAppsTransitionController(Launcher l) {
mLauncher = l;
DeviceProfile dp = mLauncher.getDeviceProfile();
- setShiftRange(dp.allAppsShiftRange);
mProgress = 1f;
mIsVerticalLayout = dp.isVerticalBarLayout();
mIsTablet = dp.isTablet;
+ mNavScrimFlag = Themes.getAttrBoolean(l, R.attr.isMainColorDark)
+ ? FLAG_DARK_NAV : FLAG_LIGHT_NAV;
+
+ setShiftRange(dp.allAppsShiftRange);
mLauncher.addOnDeviceProfileChangeListener(this);
}
@@ -185,28 +222,33 @@
*/
public void setProgress(float progress) {
mProgress = progress;
- getAppsViewProgressTranslationY().set(mAppsView, mProgress * mShiftRange);
+ getAppsViewProgressTranslationY().setValue(mProgress * mShiftRange);
mLauncher.onAllAppsTransition(1 - progress);
+
+ boolean hasScrim = progress < NAV_BAR_COLOR_FORCE_UPDATE_THRESHOLD
+ && mLauncher.getAppsView().getNavBarScrimHeight() > 0;
+ mLauncher.getSystemUiController().updateUiState(
+ UI_STATE_ALL_APPS, hasScrim ? mNavScrimFlag : 0);
}
public float getProgress() {
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);
}
/**
@@ -220,6 +262,62 @@
onProgressAnimationEnd();
}
+ @Override
+ public void onBackProgressed(
+ LauncherState toState, @FloatRange(from = 0.0, to = 1.0) float backProgress) {
+ if (!mLauncher.isInState(ALL_APPS) || !NORMAL.equals(toState)) {
+ return;
+ }
+
+ float deceleratedProgress =
+ Interpolators.PREDICTIVE_BACK_DECELERATED_EASE.getInterpolation(backProgress);
+ float scaleProgress = SWIPE_ALL_APPS_TO_HOME_MIN_SCALE
+ + (1 - SWIPE_ALL_APPS_TO_HOME_MIN_SCALE) * (1 - deceleratedProgress);
+
+ mAllAppScale.updateValue(scaleProgress);
+ }
+
+ @Override
+ public void onBackCancelled(LauncherState toState) {
+ if (!mLauncher.isInState(ALL_APPS) || !NORMAL.equals(toState)) {
+ return;
+ }
+
+ // TODO: once ag/20649618 is picked into tm-qpr, we don't need to animate back on cancel
+ // swipe because framework will do that for us in {@link #onBackProgressed}.
+ animateAllAppsToNoScale();
+ }
+
+ private void onScaleProgressChanged() {
+ final float scaleProgress = mAllAppScale.value;
+ SCALE_PROPERTY.set(mLauncher.getAppsView(), scaleProgress);
+ mLauncher.getScrimView().setScrimHeaderScale(scaleProgress);
+
+ AllAppsRecyclerView rv = mLauncher.getAppsView().getActiveRecyclerView();
+ if (rv != null && rv.getScrollbar() != null) {
+ rv.getScrollbar().setVisibility(scaleProgress < 1f ? View.INVISIBLE : View.VISIBLE);
+ }
+
+ // Disable view clipping from all apps' RecyclerView up to all apps view during scale
+ // animation, and vice versa. The goal is to display extra roll(s) app icons (rendered in
+ // {@link AppsGridLayoutManager#calculateExtraLayoutSpace}) during scale animation.
+ boolean hasScaleEffect = scaleProgress < 1f;
+ if (hasScaleEffect != mHasScaleEffect) {
+ mHasScaleEffect = hasScaleEffect;
+ if (mHasScaleEffect) {
+ setClipChildrenOnViewTree(rv, mLauncher.getAppsView(), false);
+ } else {
+ restoreClipChildrenOnViewTree(rv, mLauncher.getAppsView());
+ }
+ }
+ }
+
+ private void animateAllAppsToNoScale() {
+ mAllAppScale.animateToValue(1f)
+ .setDuration(REVERT_SWIPE_ALL_APPS_TO_HOME_ANIMATION_DURATION_MS)
+ .start();
+ }
+
/**
* Creates an animation which updates the vertical transition progress and updates all the
* dependent UI using various animation events
@@ -227,12 +325,27 @@
@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);
- mLauncher.getAppsView().getSearchUiManager().getEditText().hideKeyboard();
+ 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 (config.userControlled && success && mShouldControlKeyboard) {
+ mLauncher.getAppsView().getSearchUiManager().getEditText().hideKeyboard();
+ }
+
+ mAllAppScale.updateValue(1f);
});
}
@@ -271,7 +384,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 =
@@ -290,8 +403,86 @@
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();
+ }
+
+ /**
+ * Recursively call {@link ViewGroup#setClipChildren(boolean)} from {@link View} to ts parent
+ * (direct or indirect) inclusive. This method will also save the old clipChildren value on each
+ * view with {@link View#setTag(int, Object)}, which can be restored in
+ * {@link #restoreClipChildrenOnViewTree(View, ViewParent)}.
+ *
+ * Note that if parent is null or not a parent of the view, this method will be applied all the
+ * way to root view.
+ *
+ * @param v child view
+ * @param parent direct or indirect parent of child view
+ * @param clipChildren whether we should clip children
+ */
+ private static void setClipChildrenOnViewTree(
+ @Nullable View v,
+ @Nullable ViewParent parent,
+ boolean clipChildren) {
+ if (v == null) {
+ return;
+ }
+
+ if (v instanceof ViewGroup) {
+ ViewGroup viewGroup = (ViewGroup) v;
+ boolean oldClipChildren = viewGroup.getClipChildren();
+ if (oldClipChildren != clipChildren) {
+ v.setTag(R.id.saved_clip_children_tag_id, oldClipChildren);
+ viewGroup.setClipChildren(clipChildren);
+ }
+ }
+
+ if (v == parent) {
+ return;
+ }
+
+ if (v.getParent() instanceof View) {
+ setClipChildrenOnViewTree((View) v.getParent(), parent, clipChildren);
+ }
+ }
+
+ /**
+ * Recursively call {@link ViewGroup#setClipChildren(boolean)} to restore clip children value
+ * set in {@link #setClipChildrenOnViewTree(View, ViewParent, boolean)} on view to its parent
+ * (direct or indirect) inclusive.
+ *
+ * Note that if parent is null or not a parent of the view, this method will be applied all the
+ * way to root view.
+ *
+ * @param v child view
+ * @param parent direct or indirect parent of child view
+ */
+ private static void restoreClipChildrenOnViewTree(
+ @Nullable View v, @Nullable ViewParent parent) {
+ if (v == null) {
+ return;
+ }
+ if (v instanceof ViewGroup) {
+ ViewGroup viewGroup = (ViewGroup) v;
+ Object viewTag = viewGroup.getTag(R.id.saved_clip_children_tag_id);
+ if (viewTag instanceof Boolean) {
+ viewGroup.setClipChildren((boolean) viewTag);
+ viewGroup.setTag(R.id.saved_clip_children_tag_id, null);
+ }
+ }
+
+ if (v == parent) {
+ return;
+ }
+
+ if (v.getParent() instanceof View) {
+ restoreClipChildrenOnViewTree((View) v.getParent(), parent);
+ }
}
/**
@@ -306,10 +497,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/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index 8bbe216..29767bf 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -15,17 +15,12 @@
*/
package com.android.launcher3.allapps;
-import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_ALL_APPS_DIVIDER;
-import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_EMPTY_SEARCH;
-import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_SEARCH_MARKET;
-
import android.content.Context;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.DiffUtil;
import com.android.launcher3.allapps.BaseAllAppsAdapter.AdapterItem;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.util.LabelComparator;
@@ -173,13 +168,6 @@
}
/**
- * Returns whether there are no filtered results.
- */
- public boolean hasNoFilteredResults() {
- return hasSearchResults() && mAccessibilityResultsCount == 0;
- }
-
- /**
* Sets results list for search
*/
public boolean setSearchResults(ArrayList<AdapterItem> results) {
@@ -249,34 +237,26 @@
// ordered set of sections
if (hasSearchResults()) {
mAdapterItems.addAll(mSearchResults);
- if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
- // Append the search market item
- if (hasNoFilteredResults()) {
- mAdapterItems.add(new AdapterItem(VIEW_TYPE_EMPTY_SEARCH));
- } else {
- mAdapterItems.add(new AdapterItem(VIEW_TYPE_ALL_APPS_DIVIDER));
- }
- mAdapterItems.add(new AdapterItem(VIEW_TYPE_SEARCH_MARKET));
- }
} else {
int position = 0;
+ boolean addApps = true;
if (mWorkProviderManager != null) {
position += mWorkProviderManager.addWorkItems(mAdapterItems);
- if (!mWorkProviderManager.shouldShowWorkApps()) {
- return;
- }
+ addApps = mWorkProviderManager.shouldShowWorkApps();
}
- String lastSectionName = null;
- for (AppInfo info : mApps) {
- mAdapterItems.add(AdapterItem.asApp(info));
+ if (addApps) {
+ String lastSectionName = null;
+ for (AppInfo info : mApps) {
+ mAdapterItems.add(AdapterItem.asApp(info));
- String sectionName = info.sectionName;
- // Create a new section if the section names do not match
- if (!sectionName.equals(lastSectionName)) {
- lastSectionName = sectionName;
- mFastScrollerSections.add(new FastScrollSectionInfo(sectionName, position));
+ String sectionName = info.sectionName;
+ // Create a new section if the section names do not match
+ if (!sectionName.equals(lastSectionName)) {
+ lastSectionName = sectionName;
+ mFastScrollerSections.add(new FastScrollSectionInfo(sectionName, position));
+ }
+ position++;
}
- position++;
}
}
mAccessibilityResultsCount = (int) mAdapterItems.stream()
diff --git a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
index 459fa88..42f8b0c 100644
--- a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
+++ b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
@@ -19,7 +19,6 @@
import android.content.Context;
import android.content.res.Resources;
-import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
@@ -54,19 +53,13 @@
public static final int VIEW_TYPE_ICON = 1 << 1;
// The message shown when there are no filtered results
public static final int VIEW_TYPE_EMPTY_SEARCH = 1 << 2;
- // The message to continue to a market search when there are no filtered results
- public static final int VIEW_TYPE_SEARCH_MARKET = 1 << 3;
-
- // We use various dividers for various purposes. They share enough attributes to reuse layouts,
- // but differ in enough attributes to require different view types
-
// A divider that separates the apps list and the search market button
- public static final int VIEW_TYPE_ALL_APPS_DIVIDER = 1 << 4;
+ public static final int VIEW_TYPE_ALL_APPS_DIVIDER = 1 << 3;
- public static final int VIEW_TYPE_WORK_EDU_CARD = 1 << 5;
- public static final int VIEW_TYPE_WORK_DISABLED_CARD = 1 << 6;
+ public static final int VIEW_TYPE_WORK_EDU_CARD = 1 << 4;
+ public static final int VIEW_TYPE_WORK_DISABLED_CARD = 1 << 5;
- public static final int NEXT_ID = 7;
+ public static final int NEXT_ID = 6;
// Common view type masks
public static final int VIEW_TYPE_MASK_DIVIDER = VIEW_TYPE_ALL_APPS_DIVIDER;
@@ -117,7 +110,7 @@
}
protected boolean isCountedForAccessibility() {
- return viewType == VIEW_TYPE_ICON || viewType == VIEW_TYPE_SEARCH_MARKET;
+ return viewType == VIEW_TYPE_ICON;
}
/**
@@ -134,20 +127,22 @@
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;
protected final AlphabeticalAppsList<T> mApps;
// The text to show when there are no search results and no market search handler.
- protected String mEmptySearchMessage;
protected int mAppsPerRow;
protected final LayoutInflater mLayoutInflater;
protected final OnClickListener mOnIconClickListener;
protected OnLongClickListener mOnIconLongClickListener = INSTANCE_ALL_APPS;
protected OnFocusChangeListener mIconFocusListener;
- // The click listener to send off to the market app, updated each time the search query changes.
- private OnClickListener mMarketSearchClickListener;
private final int mExtraHeight;
public BaseAllAppsAdapter(T activityContext, LayoutInflater inflater,
@@ -155,7 +150,6 @@
Resources res = activityContext.getResources();
mActivityContext = activityContext;
mApps = apps;
- mEmptySearchMessage = res.getString(R.string.all_apps_loading_message);
mLayoutInflater = inflater;
mOnIconClickListener = mActivityContext.getItemOnClickListener();
@@ -186,16 +180,6 @@
}
/**
- * Sets the last search query that was made, used to show when there are no results and to also
- * seed the intent for searching the market.
- */
- public void setLastSearchQuery(String query, OnClickListener marketSearchClickListener) {
- Resources res = mActivityContext.getResources();
- mEmptySearchMessage = res.getString(R.string.all_apps_no_search_results, query);
- mMarketSearchClickListener = marketSearchClickListener;
- }
-
- /**
* Returns the layout manager.
*/
public abstract RecyclerView.LayoutManager getLayoutManager();
@@ -222,11 +206,6 @@
case VIEW_TYPE_EMPTY_SEARCH:
return new ViewHolder(mLayoutInflater.inflate(R.layout.all_apps_empty_search,
parent, false));
- case VIEW_TYPE_SEARCH_MARKET:
- View searchMarketView = mLayoutInflater.inflate(R.layout.all_apps_search_market,
- parent, false);
- searchMarketView.setOnClickListener(mMarketSearchClickListener);
- return new ViewHolder(searchMarketView);
case VIEW_TYPE_ALL_APPS_DIVIDER:
return new ViewHolder(mLayoutInflater.inflate(
R.layout.all_apps_divider, parent, false));
@@ -248,26 +227,21 @@
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
switch (holder.getItemViewType()) {
- case VIEW_TYPE_ICON:
+ case VIEW_TYPE_ICON: {
AdapterItem adapterItem = mApps.getAdapterItems().get(position);
BubbleTextView icon = (BubbleTextView) holder.itemView;
icon.reset();
icon.applyFromApplicationInfo(adapterItem.itemInfo);
break;
- case VIEW_TYPE_EMPTY_SEARCH:
- TextView emptyViewText = (TextView) holder.itemView;
- emptyViewText.setText(mEmptySearchMessage);
- emptyViewText.setGravity(mApps.hasNoFilteredResults() ? Gravity.CENTER :
- Gravity.START | Gravity.CENTER_VERTICAL);
- break;
- case VIEW_TYPE_SEARCH_MARKET:
- TextView searchView = (TextView) holder.itemView;
- if (mMarketSearchClickListener != null) {
- searchView.setVisibility(View.VISIBLE);
- } else {
- searchView.setVisibility(View.GONE);
+ }
+ case VIEW_TYPE_EMPTY_SEARCH: {
+ AppInfo info = mApps.getAdapterItems().get(position).itemInfo;
+ if (info != null) {
+ ((TextView) holder.itemView).setText(mActivityContext.getString(
+ R.string.all_apps_no_search_results, info.title));
}
break;
+ }
case VIEW_TYPE_ALL_APPS_DIVIDER:
case VIEW_TYPE_WORK_DISABLED_CARD:
// nothing to do
diff --git a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
index f3c5dd6..9bb8250 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;
@@ -26,8 +26,11 @@
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.Path.Direction;
import android.graphics.Point;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.os.Bundle;
import android.os.Parcelable;
import android.os.Process;
@@ -35,6 +38,7 @@
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
+import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
@@ -48,7 +52,6 @@
import androidx.recyclerview.widget.RecyclerView;
import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.DeviceProfile.DeviceProfileListenable;
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.DragSource;
import com.android.launcher3.DropTarget.DragObject;
@@ -56,7 +59,9 @@
import com.android.launcher3.InsettableFrameLayout;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.allapps.search.DefaultSearchAdapterProvider;
import com.android.launcher3.allapps.search.SearchAdapterProvider;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.keyboard.FocusedItemDecorator;
import com.android.launcher3.model.StringCache;
import com.android.launcher3.model.data.ItemInfo;
@@ -79,8 +84,8 @@
*
* @param <T> Type of context inflating all apps.
*/
-public abstract class BaseAllAppsContainerView<T extends Context & ActivityContext
- & DeviceProfileListenable> extends SpringRelativeLayout implements DragSource, Insettable,
+public abstract class BaseAllAppsContainerView<T extends Context & ActivityContext>
+ extends SpringRelativeLayout implements DragSource, Insettable,
OnDeviceProfileChangeListener, OnActivePageChangedListener,
ScrimView.ScrimDrawingController {
@@ -89,6 +94,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();
@@ -97,71 +105,142 @@
protected final List<AdapterHolder> mAH;
protected final Predicate<ItemInfo> mPersonalMatcher = ItemInfoMatcher.ofUser(
Process.myUserHandle());
- private final SearchAdapterProvider<?> mMainAdapterProvider;
private final AllAppsStore mAllAppsStore = new AllAppsStore();
private final RecyclerView.OnScrollListener mScrollListener =
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;
+ private SearchAdapterProvider<?> mMainAdapterProvider;
protected FloatingHeaderView mHeader;
- private View mBottomSheetBackground;
+ protected View mBottomSheetBackground;
private View mBottomSheetHandleArea;
+ /**
+ * View that defines the search box. Result is rendered inside {@link #mSearchRecyclerView}.
+ */
+ protected View mSearchContainer;
+ protected SearchUiManager mSearchUiManager;
+
protected boolean mUsingTabs;
private boolean mHasWorkApps;
protected RecyclerViewFastScroller mTouchHandler;
protected final Point mFastScrollerOffset = new Point();
- private final int mScrimColor;
+ protected final int mScrimColor;
private final int mHeaderProtectionColor;
protected final float mHeaderThreshold;
- private int mHeaderBottomAdjustment;
+ private final Path mTmpPath = new Path();
+ private final RectF mTmpRectF = new RectF();
+ private float[] mBottomSheetCornerRadii;
private ScrimView mScrimView;
private int mHeaderColor;
+ private int mBottomSheetBackgroundColor;
private int mTabsProtectionAlpha;
protected BaseAllAppsContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mActivityContext = ActivityContext.lookupContext(context);
- mMainAdapterProvider = createMainAdapterProvider();
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(
- mActivityContext.getSystemService(UserManager.class),
- this, Utilities.getPrefs(mActivityContext));
+ mWorkManager = new WorkProfileManager(mActivityContext.getSystemService(UserManager.class),
+ this, mActivityContext.getStatsLogManager());
mAH = Arrays.asList(null, null, null);
- mAH.set(AdapterHolder.MAIN, new AdapterHolder(AdapterHolder.MAIN));
- mAH.set(AdapterHolder.WORK, new AdapterHolder(AdapterHolder.WORK));
- mAH.set(AdapterHolder.SEARCH, new AdapterHolder(AdapterHolder.SEARCH));
-
mNavBarScrimPaint = new Paint();
mNavBarScrimPaint.setColor(Themes.getAttrColor(context, R.attr.allAppsNavBarScrimColor));
mAllAppsStore.addUpdateListener(this::onAppsUpdated);
mActivityContext.addOnDeviceProfileChangeListener(this);
+
+ // This is a focus listener that proxies focus from a view into the list view. This is to
+ // work around the search box from getting first focus and showing the cursor.
+ setOnFocusChangeListener((v, hasFocus) -> {
+ if (hasFocus && getActiveRecyclerView() != null) {
+ getActiveRecyclerView().requestFocus();
+ }
+ });
+ initContent();
+ }
+
+ /**
+ * Initializes the view hierarchy and internal variables. Any initialization which actually uses
+ * these members should be done in {@link #onFinishInflate()}.
+ * In terms of subclass initialization, the following would be parallel order for activity:
+ * initContent -> onPreCreate
+ * constructor/init -> onCreate
+ * onFinishInflate -> onPostCreate
+ */
+ protected void initContent() {
+ mMainAdapterProvider = createMainAdapterProvider();
+
+ mAH.set(AdapterHolder.MAIN, new AdapterHolder(AdapterHolder.MAIN));
+ mAH.set(AdapterHolder.WORK, new AdapterHolder(AdapterHolder.WORK));
+ mAH.set(AdapterHolder.SEARCH, new AdapterHolder(AdapterHolder.SEARCH));
+
+ getLayoutInflater().inflate(R.layout.all_apps_content, this);
+ mHeader = findViewById(R.id.all_apps_header);
+ mBottomSheetBackground = findViewById(R.id.bottom_sheet_background);
+ mBottomSheetHandleArea = findViewById(R.id.bottom_sheet_handle_area);
+ mSearchRecyclerView = findViewById(R.id.search_results_list_view);
+
+ // Add the search box next to the header
+ mSearchContainer = inflateSearchBox();
+ addView(mSearchContainer, indexOfChild(mHeader) + 1);
+ mSearchUiManager = (SearchUiManager) mSearchContainer;
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ mAH.get(AdapterHolder.SEARCH).setup(mSearchRecyclerView,
+ /* Filter out A-Z apps */ itemInfo -> false);
+ rebindAdapters(true /* force */);
+ float cornerRadius = Themes.getDialogCornerRadius(getContext());
+ mBottomSheetCornerRadii = new float[]{
+ cornerRadius,
+ cornerRadius, // Top left radius in px
+ cornerRadius,
+ cornerRadius, // Top right radius in px
+ 0,
+ 0, // Bottom right
+ 0,
+ 0 // Bottom left
+ };
+ final TypedValue value = new TypedValue();
+ getContext().getTheme().resolveAttribute(android.R.attr.colorBackground, value, true);
+ mBottomSheetBackgroundColor = value.data;
+ updateBackground(mActivityContext.getDeviceProfile());
+ }
+
+ /**
+ * Inflates the search box
+ */
+ protected View inflateSearchBox() {
+ return getLayoutInflater().inflate(R.layout.search_container_all_apps, this, false);
}
/** Creates the adapter provider for the main section. */
- protected abstract SearchAdapterProvider<?> createMainAdapterProvider();
+ protected SearchAdapterProvider<?> createMainAdapterProvider() {
+ return new DefaultSearchAdapterProvider(mActivityContext);
+ }
/** The adapter provider for the main section. */
public final SearchAdapterProvider<?> getMainAdapterProvider() {
@@ -190,7 +269,6 @@
reset(true);
}
}
-
}
@Override
@@ -234,6 +312,9 @@
protected void updateBackground(DeviceProfile deviceProfile) {
mBottomSheetBackground.setVisibility(deviceProfile.isTablet ? View.VISIBLE : View.GONE);
+ // Note: For tablets, the opaque background and header protection are added in drawOnScrim.
+ // For the taskbar entrypoint, the scrim is drawn differently, so a static background is
+ // added in TaskbarAllAppsContainerView and header protection is not yet supported.
}
private void onAppsUpdated() {
@@ -244,6 +325,10 @@
mWorkManager.reset();
}
}
+
+ mActivityContext.getStatsLogManager().logger()
+ .withCardinality(mAllAppsStore.getApps().length)
+ .log(LAUNCHER_ALLAPPS_COUNT);
}
/**
@@ -272,6 +357,13 @@
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
+ // The AllAppsContainerView houses the QSB and is hence visible from the Workspace
+ // Overview states. We shouldn't intercept for the scrubber in these cases.
+ if (!isInAllApps()) {
+ mTouchHandler = null;
+ return false;
+ }
+
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
AllAppsRecyclerView rv = getActiveRecyclerView();
if (rv != null && rv.getScrollbar() != null
@@ -289,6 +381,10 @@
@Override
public boolean onTouchEvent(MotionEvent ev) {
+ if (!isInAllApps()) {
+ return false;
+ }
+
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
AllAppsRecyclerView rv = getActiveRecyclerView();
if (rv != null && rv.getScrollbar() != null
@@ -349,7 +445,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);
}
@@ -393,30 +489,6 @@
}
@Override
- protected void onFinishInflate() {
- super.onFinishInflate();
-
- // This is a focus listener that proxies focus from a view into the list view. This is to
- // work around the search box from getting first focus and showing the cursor.
- setOnFocusChangeListener((v, hasFocus) -> {
- if (hasFocus && getActiveRecyclerView() != null) {
- getActiveRecyclerView().requestFocus();
- }
- });
-
- mHeader = findViewById(R.id.all_apps_header);
- mSearchRecyclerView = findViewById(R.id.search_results_list_view);
- mAH.get(AdapterHolder.SEARCH).setup(mSearchRecyclerView,
- /* Filter out A-Z apps */ itemInfo -> false);
- rebindAdapters(true /* force */);
-
- mBottomSheetBackground = findViewById(R.id.bottom_sheet_background);
- updateBackground(mActivityContext.getDeviceProfile());
-
- mBottomSheetHandleArea = findViewById(R.id.bottom_sheet_handle_area);
- }
-
- @Override
public void onDropCompleted(View target, DragObject d, boolean success) {}
@Override
@@ -434,8 +506,12 @@
if (grid.isVerticalBarLayout()) {
setPadding(grid.workspacePadding.left, 0, grid.workspacePadding.right, 0);
} else {
- setPadding(grid.allAppsLeftRightMargin, grid.allAppsTopPadding,
- grid.allAppsLeftRightMargin, 0);
+ int topPadding = grid.allAppsTopPadding;
+ if (FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get() && !grid.isTablet) {
+ topPadding += getResources().getDimensionPixelSize(
+ R.dimen.all_apps_additional_top_padding_floating_search);
+ }
+ setPadding(grid.allAppsLeftRightMargin, topPadding, grid.allAppsLeftRightMargin, 0);
}
InsettableFrameLayout.dispatchInsets(this, insets);
@@ -444,13 +520,20 @@
/**
* Returns a padding in case a scrim is shown on the bottom of the view and a padding is needed.
*/
- protected int getNavBarScrimHeight(WindowInsets insets) {
+ protected int computeNavBarScrimHeight(WindowInsets insets) {
return 0;
}
+ /**
+ * Returns the current height of nav bar scrim
+ */
+ public int getNavBarScrimHeight() {
+ return mNavBarScrimHeight;
+ }
+
@Override
public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) {
- mNavBarScrimHeight = getNavBarScrimHeight(insets);
+ mNavBarScrimHeight = computeNavBarScrimHeight(insets);
applyAdapterSideAndBottomPaddings(mActivityContext.getDeviceProfile());
return super.dispatchApplyWindowInsets(insets);
}
@@ -491,11 +574,16 @@
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);
mAH.get(AdapterHolder.WORK).setup(mViewPager.getChildAt(1), mWorkManager.getMatcher());
mAH.get(AdapterHolder.WORK).mRecyclerView.setId(R.id.apps_list_view_work);
+ if (FeatureFlags.ENABLE_EXPANDING_PAUSE_WORK_BUTTON.get()) {
+ mAH.get(AdapterHolder.WORK).mRecyclerView.addOnScrollListener(
+ mWorkManager.newScrollListener());
+ }
mViewPager.getPageIndicator().setActiveMarker(AdapterHolder.MAIN);
findViewById(R.id.tab_personal)
.setOnClickListener((View view) -> {
@@ -503,8 +591,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 +599,7 @@
mActivityContext.getStatsLogManager().logger()
.log(LAUNCHER_ALLAPPS_TAP_ON_WORK_TAB);
}
- hideKeyboardAsync(ActivityContext.lookupContext(getContext()),
- getApplicationWindowToken());
+ mActivityContext.hideKeyboard();
});
setDeviceManagementResources();
onActivePageChanged(mViewPager.getNextPage());
@@ -525,15 +611,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());
@@ -586,10 +675,10 @@
mViewPager = (AllAppsPagedView) newView;
mViewPager.initParentViews(this);
mViewPager.getPageIndicator().setOnActivePageChangedListener(this);
- if (mWorkManager.attachWorkModeSwitch()) {
- mWorkManager.getWorkModeSwitch().post(
- () -> mAH.get(AdapterHolder.WORK).applyPadding());
- }
+
+ mWorkManager.reset();
+ post(() -> mAH.get(AdapterHolder.WORK).applyPadding());
+
} else {
mWorkManager.detachWorkModeSwitch();
mViewPager = null;
@@ -723,23 +812,64 @@
}
@Override
- public void drawOnScrim(Canvas canvas) {
- if (!mHeader.isHeaderProtectionSupported()) {
+ public void drawOnScrimWithScale(Canvas canvas, float scale) {
+ boolean isTablet = mActivityContext.getDeviceProfile().isTablet;
+
+ // Draw full background panel for tablets.
+ if (isTablet) {
+ mHeaderPaint.setColor(mBottomSheetBackgroundColor);
+ View panel = (View) mBottomSheetBackground;
+ float translationY = ((View) panel.getParent()).getTranslationY();
+ mTmpRectF.set(panel.getLeft(), panel.getTop() + translationY,
+ panel.getRight(), panel.getBottom());
+ mTmpPath.reset();
+ mTmpPath.addRoundRect(mTmpRectF, mBottomSheetCornerRadii, Direction.CW);
+ canvas.drawPath(mTmpPath, mHeaderPaint);
+ }
+
+ 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) {
return;
}
- mHeaderPaint.setColor(mHeaderColor);
- mHeaderPaint.setAlpha((int) (getAlpha() * Color.alpha(mHeaderColor)));
- if (mHeaderPaint.getColor() != mScrimColor && mHeaderPaint.getColor() != 0) {
- int bottom = getHeaderBottom();
- if (!mUsingTabs) {
- bottom += getFloatingHeaderView().getPaddingBottom() - mHeaderBottomAdjustment;
+ final float offset = (getVisibleContainerView().getHeight() * (1 - scale) / 2);
+ final float bottom =
+ scale * (getHeaderBottom() + getVisibleContainerView().getPaddingTop()) + offset;
+ FloatingHeaderView headerView = getFloatingHeaderView();
+ if (isTablet) {
+ // Start adding header protection if search bar or tabs will attach to the top.
+ if (!FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get() || mUsingTabs) {
+ View panel = (View) mBottomSheetBackground;
+ float translationY = ((View) panel.getParent()).getTranslationY();
+ mTmpRectF.set(panel.getLeft(), panel.getTop() + translationY, panel.getRight(),
+ bottom);
+ mTmpPath.reset();
+ mTmpPath.addRoundRect(mTmpRectF, mBottomSheetCornerRadii, Direction.CW);
+ canvas.drawPath(mTmpPath, mHeaderPaint);
}
+ } else {
canvas.drawRect(0, 0, canvas.getWidth(), bottom, mHeaderPaint);
- int tabsHeight = getFloatingHeaderView().getPeripheralProtectionHeight();
- if (mTabsProtectionAlpha > 0 && tabsHeight != 0) {
+ }
+ int tabsHeight = headerView.getPeripheralProtectionHeight();
+ if (mTabsProtectionAlpha > 0 && tabsHeight != 0) {
+ 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);
}
+ int left = 0;
+ int right = canvas.getWidth();
+ if (isTablet) {
+ left = mBottomSheetBackground.getLeft();
+ right = mBottomSheetBackground.getRight();
+ }
+ canvas.drawRect(left, bottom, right, bottom + tabsHeight, mHeaderPaint);
}
}
@@ -747,7 +877,7 @@
* redraws header protection
*/
public void invalidateHeader() {
- if (mScrimView != null && mHeader.isHeaderProtectionSupported()) {
+ if (mScrimView != null) {
mScrimView.invalidate();
}
}
@@ -774,7 +904,14 @@
BaseAdapterProvider[] adapterProviders);
public int getHeaderBottom() {
- return (int) getTranslationY();
+ int bottom = (int) getTranslationY() + mHeader.getClipTop();
+ if (FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get()) {
+ if (mActivityContext.getDeviceProfile().isTablet) {
+ return bottom + mBottomSheetBackground.getTop();
+ }
+ return bottom;
+ }
+ return bottom + mHeader.getTop();
}
/**
@@ -784,6 +921,16 @@
return mActivityContext.getDeviceProfile().isTablet ? mBottomSheetBackground : this;
}
+ protected void onInitializeRecyclerView(RecyclerView rv) {
+ rv.addOnScrollListener(mScrollListener);
+ }
+
+ /**
+ * Returns {@code true} the All Apps UI is currently being displayed on the target surface and
+ * is interactive.
+ */
+ public abstract boolean isInAllApps();
+
/** Holds a {@link BaseAllAppsAdapter} and related fields. */
public class AdapterHolder {
public static final int MAIN = 0;
@@ -820,7 +967,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..9f47e8d
--- /dev/null
+++ b/src/com/android/launcher3/allapps/BaseSearchConfig.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.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;
+ }
+}
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderView.java b/src/com/android/launcher3/allapps/FloatingHeaderView.java
index c5bdb69..c18f9e1 100644
--- a/src/com/android/launcher3/allapps/FloatingHeaderView.java
+++ b/src/com/android/launcher3/allapps/FloatingHeaderView.java
@@ -17,7 +17,6 @@
import android.animation.ValueAnimator;
import android.content.Context;
-import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Rect;
import android.util.ArrayMap;
@@ -42,6 +41,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 +67,7 @@
mAnimator.cancel();
}
- int current = -mCurrentRV.getCurrentScrollY();
+ int current = -mCurrentRV.computeVerticalScrollOffset();
boolean headerCollapsed = mHeaderCollapsed;
moved(current);
applyVerticalMove();
@@ -82,16 +82,14 @@
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 boolean mHeaderProtectionSupported;
+ private final int mTabsAdditionalPaddingTop;
+ private final int mTabsAdditionalPaddingBottom;
protected ViewGroup mTabLayout;
private AllAppsRecyclerView mMainRV;
private AllAppsRecyclerView mWorkRV;
private SearchRecyclerView mSearchRV;
private AllAppsRecyclerView mCurrentRV;
- public boolean mHeaderCollapsed;
protected int mSnappedScrolledY;
private int mTranslationY;
@@ -100,7 +98,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.
@@ -110,26 +113,16 @@
// enabled or disabled, and represent the current set of all rows.
private FloatingHeaderRow[] mAllRows = FloatingHeaderRow.NO_ROWS;
-
public FloatingHeaderView(@NonNull Context context) {
this(context, null);
}
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);
- }
-
- @Override
- protected void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- mHeaderProtectionSupported = getContext().getResources().getBoolean(
- R.bool.config_header_protection_supported);
}
@Override
@@ -148,6 +141,7 @@
}
mFixedRows = rows.toArray(new FloatingHeaderRow[rows.size()]);
mAllRows = mFixedRows;
+ updateFloatingRowsHeight();
}
@Override
@@ -179,6 +173,7 @@
count++;
}
}
+ updateFloatingRowsHeight();
}
@Override
@@ -195,7 +190,7 @@
int oldMaxHeight = mMaxTranslation;
updateExpectedHeight();
- if (mMaxTranslation != oldMaxHeight || mCollapsed) {
+ if (mMaxTranslation != oldMaxHeight || mFloatingRowsCollapsed) {
BaseAllAppsContainerView<?> parent = (BaseAllAppsContainerView<?>) getParent();
if (parent != null) {
parent.setupHeader();
@@ -258,20 +253,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 +307,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 +320,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,13 +343,19 @@
/**
* 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();
}
+ public int getClipTop() {
+ return mHeaderClip.top;
+ }
+
public void reset(boolean animate) {
if (mAnimator.isStarted()) {
mAnimator.cancel();
@@ -376,6 +378,30 @@
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 getTabsAdditionalPaddingBottom() {
+ return mTabsAdditionalPaddingBottom;
+ }
+
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mTranslationY = (Integer) animation.getAnimatedValue();
@@ -412,10 +438,6 @@
p.y = getTop() - mCurrentRV.getTop() - ((ViewGroup) mCurrentRV.getParent()).getTop();
}
- public boolean isHeaderProtectionSupported() {
- return mHeaderProtectionSupported;
- }
-
@Override
public boolean hasOverlappingRendering() {
return false;
@@ -440,16 +462,13 @@
/**
* Returns visible height of FloatingHeaderView contents requiring header protection
*/
- public int getPeripheralProtectionHeight() {
- if (!mHeaderProtectionSupported) {
- return 0;
- }
-
+ int getPeripheralProtectionHeight() {
// 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..aefedae 100644
--- a/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java
@@ -17,7 +17,6 @@
import android.content.Context;
import android.util.AttributeSet;
-import android.view.MotionEvent;
import android.view.WindowInsets;
import com.android.launcher3.Launcher;
@@ -42,31 +41,16 @@
}
@Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- // The AllAppsContainerView houses the QSB and is hence visible from the Workspace
- // Overview states. We shouldn't intercept for the scrubber in these cases.
- if (!mActivityContext.isInState(LauncherState.ALL_APPS)) {
- mTouchHandler = null;
- return false;
- }
-
- return super.onInterceptTouchEvent(ev);
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent ev) {
- if (!mActivityContext.isInState(LauncherState.ALL_APPS)) {
- return false;
- }
- return super.onTouchEvent(ev);
- }
-
- @Override
- protected int getNavBarScrimHeight(WindowInsets insets) {
+ protected int computeNavBarScrimHeight(WindowInsets insets) {
if (Utilities.ATLEAST_Q) {
return insets.getTappableElementInsets().bottom;
} else {
return insets.getStableInsetBottom();
}
}
+
+ @Override
+ public boolean isInAllApps() {
+ return mActivityContext.getStateManager().isInStableState(LauncherState.ALL_APPS);
+ }
}
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..5056782
--- /dev/null
+++ b/src/com/android/launcher3/allapps/SearchTransitionController.java
@@ -0,0 +1,340 @@
+/*
+ * 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.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 = mAllAppsContainerView.isInAllApps();
+ 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/SecondaryLauncherAllAppsContainerView.java b/src/com/android/launcher3/allapps/SecondaryLauncherAllAppsContainerView.java
index 0719c43..684e98e 100644
--- a/src/com/android/launcher3/allapps/SecondaryLauncherAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/SecondaryLauncherAllAppsContainerView.java
@@ -42,4 +42,9 @@
@Override
protected void updateBackground(DeviceProfile deviceProfile) {}
+
+ @Override
+ public boolean isInAllApps() {
+ return mActivityContext.isAppDrawerShown();
+ }
}
diff --git a/src/com/android/launcher3/allapps/WorkEduCard.java b/src/com/android/launcher3/allapps/WorkEduCard.java
index 968a556..b4cdc96 100644
--- a/src/com/android/launcher3/allapps/WorkEduCard.java
+++ b/src/com/android/launcher3/allapps/WorkEduCard.java
@@ -15,6 +15,7 @@
*/
package com.android.launcher3.allapps;
+import static com.android.launcher3.LauncherPrefs.WORK_EDU_STEP;
import static com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip.getTabWidth;
import android.content.Context;
@@ -26,8 +27,8 @@
import android.widget.FrameLayout;
import android.widget.TextView;
+import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
import com.android.launcher3.model.StringCache;
import com.android.launcher3.views.ActivityContext;
@@ -85,8 +86,7 @@
@Override
public void onClick(View view) {
startAnimation(mDismissAnim);
- Utilities.getPrefs(getContext()).edit().putInt(WorkProfileManager.KEY_WORK_EDU_STEP,
- 1).apply();
+ LauncherPrefs.get(getContext()).put(WORK_EDU_STEP, 1);
}
@Override
diff --git a/src/com/android/launcher3/allapps/WorkModeSwitch.java b/src/com/android/launcher3/allapps/WorkModeSwitch.java
index aee7c4c..3f2f21d 100644
--- a/src/com/android/launcher3/allapps/WorkModeSwitch.java
+++ b/src/com/android/launcher3/allapps/WorkModeSwitch.java
@@ -15,17 +15,23 @@
*/
package com.android.launcher3.allapps;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TURN_OFF_WORK_APPS_TAP;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WORK_FAB_BUTTON_COLLAPSE;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WORK_FAB_BUTTON_EXTEND;
import static com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip.getTabWidth;
+import android.animation.LayoutTransition;
import android.content.Context;
-import android.graphics.Insets;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
-import android.view.ViewGroup.MarginLayoutParams;
import android.view.WindowInsets;
-import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.core.graphics.Insets;
+import androidx.core.view.WindowInsetsCompat;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Insettable;
@@ -33,76 +39,84 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.KeyboardInsetAnimationCallback;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.model.StringCache;
import com.android.launcher3.views.ActivityContext;
-import com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip;
-
/**
* Work profile toggle switch shown at the bottom of AllApps work tab
*/
-public class WorkModeSwitch extends Button implements Insettable, View.OnClickListener,
- KeyboardInsetAnimationCallback.KeyboardInsetListener,
- PersonalWorkSlidingTabStrip.OnActivePageChangedListener {
+public class WorkModeSwitch extends LinearLayout implements Insettable,
+ KeyboardInsetAnimationCallback.KeyboardInsetListener {
private static final int FLAG_FADE_ONGOING = 1 << 1;
private static final int FLAG_TRANSLATION_ONGOING = 1 << 2;
private static final int FLAG_PROFILE_TOGGLE_ONGOING = 1 << 3;
+ private static final int SCROLL_THRESHOLD_DP = 10;
private final Rect mInsets = new Rect();
+ private final Rect mImeInsets = new Rect();
private int mFlags;
- private boolean mWorkEnabled;
- private boolean mOnWorkTab;
+ private final ActivityContext mActivityContext;
- public WorkModeSwitch(Context context) {
+ // Threshold when user scrolls up/down to determine when should button extend/collapse
+ private final int mScrollThreshold;
+ private ImageView mIcon;
+ private TextView mTextView;
+ private final StatsLogManager mStatsLogManager;
+
+
+ public WorkModeSwitch(@NonNull Context context) {
this(context, null, 0);
}
- public WorkModeSwitch(Context context, AttributeSet attrs) {
+ public WorkModeSwitch(@NonNull Context context, @NonNull AttributeSet attrs) {
this(context, attrs, 0);
}
- public WorkModeSwitch(Context context, AttributeSet attrs, int defStyleAttr) {
+ public WorkModeSwitch(@NonNull Context context, @NonNull AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
+ mScrollThreshold = Utilities.dpToPx(SCROLL_THRESHOLD_DP);
+ mActivityContext = ActivityContext.lookupContext(getContext());
+ mStatsLogManager = mActivityContext.getStatsLogManager();
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
+
+ mIcon = findViewById(R.id.work_icon);
+ mTextView = findViewById(R.id.pause_text);
setSelected(true);
- setOnClickListener(this);
if (Utilities.ATLEAST_R) {
KeyboardInsetAnimationCallback keyboardInsetAnimationCallback =
new KeyboardInsetAnimationCallback(this);
setWindowInsetsAnimationCallback(keyboardInsetAnimationCallback);
}
- ActivityContext activityContext = ActivityContext.lookupContext(getContext());
- DeviceProfile grid = activityContext.getDeviceProfile();
- setInsets(grid.getInsets());
- StringCache cache = activityContext.getStringCache();
+ setInsets(mActivityContext.getDeviceProfile().getInsets());
+ StringCache cache = mActivityContext.getStringCache();
if (cache != null) {
- setText(cache.workProfilePauseButton);
+ mTextView.setText(cache.workProfilePauseButton);
}
+
+ mIcon.setColorFilter(mTextView.getCurrentTextColor());
+ getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
}
@Override
public void setInsets(Rect insets) {
mInsets.set(insets);
+ updateTranslationY();
MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams();
if (lp != null) {
int bottomMargin = getResources().getDimensionPixelSize(R.dimen.work_fab_margin_bottom);
+ DeviceProfile dp = ActivityContext.lookupContext(getContext()).getDeviceProfile();
if (FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get()) {
- bottomMargin <<= 1; // Double margin to add space above search bar.
- bottomMargin += getResources().getDimensionPixelSize(R.dimen.qsb_widget_height);
+ bottomMargin += dp.hotseatQsbHeight;
}
- DeviceProfile dp = ActivityContext.lookupContext(getContext()).getDeviceProfile();
- if (!dp.isGestureMode) {
- if (dp.isTaskbarPresent) {
- bottomMargin += dp.taskbarSize;
- } else {
- bottomMargin += insets.bottom;
- }
+ if (!dp.isGestureMode && dp.isTaskbarPresent) {
+ bottomMargin += dp.taskbarSize;
}
lp.bottomMargin = bottomMargin;
@@ -112,72 +126,61 @@
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
- DeviceProfile dp = ActivityContext.lookupContext(getContext()).getDeviceProfile();
View parent = (View) getParent();
+ int allAppsLeftRightPadding = mActivityContext.getDeviceProfile().allAppsLeftRightPadding;
int size = parent.getWidth() - parent.getPaddingLeft() - parent.getPaddingRight()
- - 2 * dp.allAppsLeftRightPadding;
+ - 2 * allAppsLeftRightPadding;
int tabWidth = getTabWidth(getContext(), size);
- int shift = (size - tabWidth) / 2 + dp.allAppsLeftRightPadding;
+ int shift = (size - tabWidth) / 2 + allAppsLeftRightPadding;
setTranslationX(Utilities.isRtl(getResources()) ? shift : -shift);
}
@Override
- public void onActivePageChanged(int page) {
- mOnWorkTab = page == ActivityAllAppsContainerView.AdapterHolder.WORK;
- updateVisibility();
- }
-
- @Override
- public void onClick(View view) {
- if (Utilities.ATLEAST_P && isEnabled()) {
- setFlag(FLAG_PROFILE_TOGGLE_ONGOING);
- ActivityContext activityContext = ActivityContext.lookupContext(getContext());
- activityContext.getStatsLogManager().logger().log(LAUNCHER_TURN_OFF_WORK_APPS_TAP);
- activityContext.getAppsView().getWorkManager().setWorkProfileEnabled(false);
- }
- }
-
- @Override
public boolean isEnabled() {
return super.isEnabled() && getVisibility() == VISIBLE && mFlags == 0;
}
- /**
- * Sets the enabled or disabled state of the button
- */
- public void updateCurrentState(boolean isEnabled) {
- removeFlag(FLAG_PROFILE_TOGGLE_ONGOING);
- if (mWorkEnabled != isEnabled) {
- mWorkEnabled = isEnabled;
- updateVisibility();
- }
- }
-
- private void updateVisibility() {
+ public void animateVisibility(boolean visible) {
clearAnimation();
- if (mWorkEnabled && mOnWorkTab) {
+ if (visible) {
setFlag(FLAG_FADE_ONGOING);
setVisibility(VISIBLE);
+ extend();
animate().alpha(1).withEndAction(() -> removeFlag(FLAG_FADE_ONGOING)).start();
} else if (getVisibility() != GONE) {
setFlag(FLAG_FADE_ONGOING);
animate().alpha(0).withEndAction(() -> {
removeFlag(FLAG_FADE_ONGOING);
- this.setVisibility(GONE);
+ setVisibility(GONE);
}).start();
}
}
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
- if (Utilities.ATLEAST_R && isEnabled()) {
- setTranslationY(0);
- if (insets.isVisible(WindowInsets.Type.ime())) {
- Insets keyboardInsets = insets.getInsets(WindowInsets.Type.ime());
- setTranslationY(mInsets.bottom - keyboardInsets.bottom);
- }
+ WindowInsetsCompat windowInsetsCompat =
+ WindowInsetsCompat.toWindowInsetsCompat(insets, this);
+ if (windowInsetsCompat.isVisible(WindowInsetsCompat.Type.ime())) {
+ setInsets(mImeInsets, windowInsetsCompat.getInsets(WindowInsetsCompat.Type.ime()));
+ } else {
+ mImeInsets.setEmpty();
}
- return insets;
+ updateTranslationY();
+ return super.onApplyWindowInsets(insets);
+ }
+
+ private void updateTranslationY() {
+ setTranslationY(-mImeInsets.bottom);
+ }
+
+ @Override
+ public void setTranslationY(float translationY) {
+ // Always translate at least enough for nav bar insets.
+ super.setTranslationY(Math.min(translationY, -mInsets.bottom));
+ }
+
+ private void setInsets(Rect rect, Insets insets) {
+ rect.set(insets.left, insets.top, insets.right, insets.bottom);
}
@Override
@@ -197,4 +200,16 @@
private void removeFlag(int flag) {
mFlags &= ~flag;
}
+
+ public void extend() {
+ mTextView.setVisibility(VISIBLE);
+ }
+
+ public void shrink(){
+ mTextView.setVisibility(GONE);
+ }
+
+ public int getScrollThreshold() {
+ return mScrollThreshold;
+ }
}
diff --git a/src/com/android/launcher3/allapps/WorkProfileManager.java b/src/com/android/launcher3/allapps/WorkProfileManager.java
index cfac985..fa03905 100644
--- a/src/com/android/launcher3/allapps/WorkProfileManager.java
+++ b/src/com/android/launcher3/allapps/WorkProfileManager.java
@@ -15,26 +15,36 @@
*/
package com.android.launcher3.allapps;
+import static com.android.launcher3.LauncherPrefs.WORK_EDU_STEP;
import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_WORK_DISABLED_CARD;
import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_WORK_EDU_CARD;
+import static com.android.launcher3.allapps.BaseAllAppsContainerView.AdapterHolder.MAIN;
+import static com.android.launcher3.allapps.BaseAllAppsContainerView.AdapterHolder.SEARCH;
+import static com.android.launcher3.allapps.BaseAllAppsContainerView.AdapterHolder.WORK;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TURN_OFF_WORK_APPS_TAP;
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_HAS_SHORTCUT_PERMISSION;
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_CHANGE_PERMISSION;
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
-import android.content.SharedPreferences;
import android.os.Build;
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
+import android.view.View;
import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
+import androidx.recyclerview.widget.RecyclerView;
+import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.BaseAllAppsAdapter.AdapterItem;
+import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip;
@@ -71,19 +81,20 @@
private final UserManager mUserManager;
private final BaseAllAppsContainerView<?> mAllApps;
private final Predicate<ItemInfo> mMatcher;
+ private final StatsLogManager mStatsLogManager;
private WorkModeSwitch mWorkModeSwitch;
@WorkProfileState
private int mCurrentState;
- private SharedPreferences mPreferences;
public WorkProfileManager(
- UserManager userManager, BaseAllAppsContainerView<?> allApps, SharedPreferences prefs) {
+ UserManager userManager, BaseAllAppsContainerView<?> allApps,
+ StatsLogManager statsLogManager) {
mUserManager = userManager;
mAllApps = allApps;
- mPreferences = prefs;
mMatcher = mAllApps.mPersonalMatcher.negate();
+ mStatsLogManager = statsLogManager;
}
/**
@@ -104,8 +115,16 @@
@Override
public void onActivePageChanged(int page) {
+ updateWorkFAB(page);
+ }
+
+ private void updateWorkFAB(int page) {
if (mWorkModeSwitch != null) {
- mWorkModeSwitch.onActivePageChanged(page);
+ if (page == MAIN || page == SEARCH) {
+ mWorkModeSwitch.animateVisibility(false);
+ } else if (page == WORK && mCurrentState == STATE_ENABLED) {
+ mWorkModeSwitch.animateVisibility(true);
+ }
}
}
@@ -123,7 +142,12 @@
getAH().mAppsList.updateAdapterItems();
}
if (mWorkModeSwitch != null) {
- mWorkModeSwitch.updateCurrentState(currentState == STATE_ENABLED);
+ updateWorkFAB(mAllApps.getCurrentPage());
+ }
+ if (mCurrentState == STATE_ENABLED) {
+ attachWorkModeSwitch();
+ } else if (mCurrentState == STATE_DISABLED) {
+ detachWorkModeSwitch();
}
}
@@ -140,13 +164,16 @@
mWorkModeSwitch = (WorkModeSwitch) mAllApps.getLayoutInflater().inflate(
R.layout.work_mode_fab, mAllApps, false);
}
- if (mWorkModeSwitch.getParent() != mAllApps) {
+ if (mWorkModeSwitch.getParent() == null) {
mAllApps.addView(mWorkModeSwitch);
}
+ if (mAllApps.getCurrentPage() != WORK) {
+ mWorkModeSwitch.animateVisibility(false);
+ }
if (getAH() != null) {
getAH().applyPadding();
}
- mWorkModeSwitch.updateCurrentState(mCurrentState == STATE_ENABLED);
+ mWorkModeSwitch.setOnClickListener(this::onWorkFabClicked);
return true;
}
/**
@@ -169,7 +196,7 @@
}
private BaseAllAppsContainerView<?>.AdapterHolder getAH() {
- return mAllApps.mAH.get(BaseAllAppsContainerView.AdapterHolder.WORK);
+ return mAllApps.mAH.get(WORK);
}
public int getCurrentState() {
@@ -197,6 +224,40 @@
}
private boolean isEduSeen() {
- return mPreferences.getInt(KEY_WORK_EDU_STEP, 0) != 0;
+ return LauncherPrefs.get(mAllApps.getContext()).get(WORK_EDU_STEP) != 0;
+ }
+
+ private void onWorkFabClicked(View view) {
+ if (Utilities.ATLEAST_P && mCurrentState == STATE_ENABLED && mWorkModeSwitch.isEnabled()) {
+ mStatsLogManager.logger().log(LAUNCHER_TURN_OFF_WORK_APPS_TAP);
+ setWorkProfileEnabled(false);
+ }
+ }
+
+ public RecyclerView.OnScrollListener newScrollListener() {
+ return new RecyclerView.OnScrollListener() {
+ int totalDelta = 0;
+ @Override
+ public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState){
+ if (newState == RecyclerView.SCROLL_STATE_IDLE) {
+ totalDelta = 0;
+ }
+ }
+ @Override
+ public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
+ WorkModeSwitch fab = getWorkModeSwitch();
+ if (fab == null){
+ return;
+ }
+ totalDelta = Utilities.boundToRange(totalDelta,
+ -fab.getScrollThreshold(), fab.getScrollThreshold()) + dy;
+ boolean isScrollAtTop = recyclerView.computeVerticalScrollOffset() == 0;
+ if ((isScrollAtTop || totalDelta < -fab.getScrollThreshold())) {
+ fab.extend();
+ } else if (totalDelta > fab.getScrollThreshold()) {
+ fab.shrink();
+ }
+ }
+ };
}
}
diff --git a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
index 886460e..4427a49 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;
@@ -70,7 +67,7 @@
mInput.addTextChangedListener(this);
mInput.setOnEditorActionListener(this);
mInput.setOnBackKeyListener(this);
- mInput.setOnFocusChangeListener(this);
+ mInput.addOnFocusChangeListener(this);
mSearchAlgorithm = searchAlgorithm;
}
@@ -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();
}
@@ -157,6 +153,7 @@
mCallback.clearSearchResult();
mInput.reset();
mQuery = null;
+ mInput.removeOnFocusChangeListener(this);
}
/**
diff --git a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
index 6539c05..78c305b 100644
--- a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
+++ b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
@@ -131,7 +131,7 @@
public void initializeSearch(ActivityAllAppsContainerView<?> appsView) {
mAppsView = appsView;
mSearchBarController.initialize(
- new DefaultAppSearchAlgorithm(getContext()),
+ new DefaultAppSearchAlgorithm(getContext(), true),
this, mLauncher, this);
}
@@ -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 4eceb71..ab47097 100644
--- a/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
+++ b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
@@ -15,12 +15,14 @@
*/
package com.android.launcher3.allapps.search;
+import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_EMPTY_SEARCH;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import android.content.Context;
import android.os.Handler;
import androidx.annotation.AnyThread;
+import androidx.annotation.NonNull;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.allapps.BaseAllAppsAdapter.AdapterItem;
@@ -44,10 +46,16 @@
private final LauncherAppState mAppState;
private final Handler mResultHandler;
+ private final boolean mAddNoResultsMessage;
public DefaultAppSearchAlgorithm(Context context) {
+ this(context, false);
+ }
+
+ public DefaultAppSearchAlgorithm(Context context, boolean addNoResultsMessage) {
mAppState = LauncherAppState.getInstance(context);
mResultHandler = new Handler(MAIN_EXECUTOR.getLooper());
+ mAddNoResultsMessage = addNoResultsMessage;
}
@Override
@@ -61,13 +69,26 @@
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));
+ }
mResultHandler.post(() -> callback.onSearchResult(query, result));
}
});
}
+ private static AdapterItem getEmptyMessageAdapterItem(String query) {
+ AdapterItem item = new AdapterItem(VIEW_TYPE_EMPTY_SEARCH);
+ // Add a place holder info to propagate the query
+ AppInfo placeHolder = new AppInfo();
+ placeHolder.title = query;
+ item.itemInfo = placeHolder;
+ return item;
+ }
+
/**
* Filters {@link AppInfo}s matching specified query
*/
diff --git a/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java b/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java
index a95bd51..20edf8a 100644
--- a/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java
+++ b/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java
@@ -26,17 +26,17 @@
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.allapps.AllAppsGridAdapter;
import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.views.AppLauncher;
+import com.android.launcher3.views.ActivityContext;
/**
* Provides views for local search results.
*/
-public class DefaultSearchAdapterProvider extends SearchAdapterProvider<AppLauncher> {
+public class DefaultSearchAdapterProvider extends SearchAdapterProvider<ActivityContext> {
private final RecyclerView.ItemDecoration mDecoration;
private View mHighlightedView;
- public DefaultSearchAdapterProvider(AppLauncher launcher) {
+ public DefaultSearchAdapterProvider(ActivityContext launcher) {
super(launcher);
mDecoration = new RecyclerView.ItemDecoration() {
@Override
@@ -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/quickstep/src/com/android/quickstep/AnimatedFloat.java b/src/com/android/launcher3/anim/AnimatedFloat.java
similarity index 87%
rename from quickstep/src/com/android/quickstep/AnimatedFloat.java
rename to src/com/android/launcher3/anim/AnimatedFloat.java
index a166553..b73621d 100644
--- a/quickstep/src/com/android/quickstep/AnimatedFloat.java
+++ b/src/com/android/launcher3/anim/AnimatedFloat.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.
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.quickstep;
+package com.android.launcher3.anim;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -99,30 +99,23 @@
}
/**
- * Starts the animation.
+ * Cancels the animation.
*/
- public void startAnimation() {
- if (mValueAnimator != null) {
- mValueAnimator.start();
- }
- }
-
public void cancelAnimation() {
if (mValueAnimator != null) {
mValueAnimator.cancel();
}
}
+ /**
+ * Ends the animation.
+ */
public void finishAnimation() {
if (mValueAnimator != null && mValueAnimator.isRunning()) {
mValueAnimator.end();
}
}
- public ObjectAnimator getCurrentAnimation() {
- return mValueAnimator;
- }
-
public boolean isAnimating() {
return mValueAnimator != null;
}
@@ -135,9 +128,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/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 0a77aa7..e886543 100644
--- a/src/com/android/launcher3/anim/Interpolators.java
+++ b/src/com/android/launcher3/anim/Interpolators.java
@@ -56,7 +56,13 @@
public static final Interpolator DECELERATED_EASE = new PathInterpolator(0, 0, .2f, 1f);
public static final Interpolator ACCELERATED_EASE = new PathInterpolator(0.4f, 0, 1f, 1f);
+ public static final Interpolator PREDICTIVE_BACK_DECELERATED_EASE =
+ new PathInterpolator(0, 0, 0, 1f);
+ /**
+ * The default emphasized interpolator. Used for hero / emphasized movement of content.
+ */
+ public static final Interpolator EMPHASIZED = createEmphasizedInterpolator();
public static final Interpolator EMPHASIZED_ACCELERATE = new PathInterpolator(
0.3f, 0f, 0.8f, 0.15f);
public static final Interpolator EMPHASIZED_DECELERATE = new PathInterpolator(
@@ -79,6 +85,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);
@@ -87,7 +94,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.
*/
@@ -218,4 +224,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/KeyboardInsetAnimationCallback.java b/src/com/android/launcher3/anim/KeyboardInsetAnimationCallback.java
index 9d96365..3863dc1 100644
--- a/src/com/android/launcher3/anim/KeyboardInsetAnimationCallback.java
+++ b/src/com/android/launcher3/anim/KeyboardInsetAnimationCallback.java
@@ -53,10 +53,21 @@
mView.setTranslationY(mInitialTranslation);
return windowInsets;
}
- float progress = list.get(0).getInterpolatedFraction();
+ WindowInsetsAnimation animation = list.get(0);
- mView.setTranslationY(
- Utilities.mapRange(progress, mInitialTranslation, mTerminalTranslation));
+ if (animation.getDurationMillis() > -1) {
+ float progress = animation.getInterpolatedFraction();
+ mView.setTranslationY(
+ Utilities.mapRange(progress, mInitialTranslation, mTerminalTranslation));
+ } else {
+ // Manually controlled animation: Set translation to keyboard height.
+ int translationY = -windowInsets.getInsets(WindowInsets.Type.ime()).bottom;
+ if (mView.getParent() instanceof View) {
+ // Offset any translation of the parent (e.g. All Apps parallax).
+ translationY -= ((View) mView.getParent()).getTranslationY();
+ }
+ mView.setTranslationY(translationY);
+ }
return windowInsets;
}
@@ -73,6 +84,7 @@
@Override
public void onEnd(WindowInsetsAnimation animation) {
+ mView.setTranslationY(mTerminalTranslation);
if (mView instanceof KeyboardInsetListener) {
((KeyboardInsetListener) mView).onTranslationEnd();
}
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..726ef05
--- /dev/null
+++ b/src/com/android/launcher3/celllayout/CellLayoutLayoutParams.java
@@ -0,0 +1,232 @@
+/*
+ * 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 {
+
+ private int mScreenId = -1;
+
+ @ViewDebug.ExportedProperty
+ private int mCellX;
+
+ @ViewDebug.ExportedProperty
+ private int mCellY;
+
+ private int mTmpCellX;
+
+ private int mTmpCellY;
+
+ /**
+ * 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.mCellX = source.getCellX();
+ this.mCellY = source.getCellY();
+ this.cellHSpan = source.cellHSpan;
+ this.cellVSpan = source.cellVSpan;
+ this.mScreenId = source.getScreenId();
+ this.mTmpCellX = source.getTmpCellX();
+ this.mTmpCellY = source.getTmpCellY();
+ this.useTmpCoords = source.useTmpCoords;
+ }
+
+ public CellLayoutLayoutParams(int cellX, int cellY, int cellHSpan, int cellVSpan,
+ int screenId) {
+ super(CellLayoutLayoutParams.MATCH_PARENT, CellLayoutLayoutParams.MATCH_PARENT);
+ this.mCellX = cellX;
+ this.mCellY = cellY;
+ this.cellHSpan = cellHSpan;
+ this.cellVSpan = cellVSpan;
+ this.mScreenId = screenId;
+ }
+
+ /**
+ * 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 ? getTmpCellX() : getCellX();
+ int myCellY = useTmpCoords ? getTmpCellY() : getCellY();
+
+ 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) {
+ setCellX(point.x);
+ setCellY(point.y);
+ }
+
+ /**
+ * @return the string representation of the position of the {@link CellLayoutLayoutParams}
+ */
+ public String toString() {
+ return "(" + this.getCellX() + ", " + this.getCellY() + ")";
+ }
+
+ public int getScreenId() {
+ return mScreenId;
+ }
+
+ public void setScreenId(int screenId) {
+ this.mScreenId = screenId;
+ }
+
+ /**
+ * Horizontal location of the item in the grid.
+ */
+ public int getCellX() {
+ return mCellX;
+ }
+
+ public void setCellX(int cellX) {
+ this.mCellX = cellX;
+ }
+
+ /**
+ * Vertical location of the item in the grid.
+ */
+ public int getCellY() {
+ return mCellY;
+ }
+
+ public void setCellY(int cellY) {
+ this.mCellY = cellY;
+ }
+
+ /**
+ * Temporary horizontal location of the item in the grid during reorder
+ */
+ public int getTmpCellX() {
+ return mTmpCellX;
+ }
+
+ public void setTmpCellX(int tmpCellX) {
+ this.mTmpCellX = tmpCellX;
+ }
+
+ /**
+ * Temporary vertical location of the item in the grid during reorder
+ */
+ public int getTmpCellY() {
+ return mTmpCellY;
+ }
+
+ public void setTmpCellY(int tmpCellY) {
+ this.mTmpCellY = tmpCellY;
+ }
+}
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index c04f03d..d72b692 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,31 +63,25 @@
* 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.");
-
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 +89,16 @@
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_HIDE_HEADER = new DeviceFlag("ENABLE_HIDE_HEADER",
true, "Hide header on keyboard before typing in all apps");
+ public static final BooleanFlag ENABLE_EXPANDING_PAUSE_WORK_BUTTON = new DeviceFlag(
+ "ENABLE_EXPANDING_PAUSE_WORK_BUTTON", false,
+ "Expand and collapse pause work button while scrolling");
+
+ 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 +116,18 @@
"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 INJECT_FALLBACK_APP_CORPUS_RESULTS = new DeviceFlag(
+ "INJECT_FALLBACK_APP_CORPUS_RESULTS", false, "Inject "
+ + "fallback app corpus result when AiAi fails to return it.");
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 +157,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 +182,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 +207,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,17 +242,45 @@
"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", true,
+ "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", true,
+ "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");
+ public static final BooleanFlag ENABLE_WIDGET_HOST_IN_BACKGROUND = getDebugFlag(
+ "ENABLE_WIDGET_HOST_IN_BACKGROUND", false,
+ "Enable background widget updates listening for widget holder");
+
public static final BooleanFlag ENABLE_ONE_SEARCH_MOTION = new DeviceFlag(
"ENABLE_ONE_SEARCH_MOTION", true, "Enables animations in OneSearch.");
+ 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_SEARCH_RESULT_LAUNCH_TRANSITION = new DeviceFlag(
+ "ENABLE_SEARCH_RESULT_LAUNCH_TRANSITION", false,
+ "Enable option to launch search results using the new standardized transitions");
+
+ public static final BooleanFlag TWO_PREDICTED_ROWS_ALL_APPS_SEARCH = new DeviceFlag(
+ "TWO_PREDICTED_ROWS_ALL_APPS_SEARCH", false,
+ "Use 2 rows of app predictions in All Apps search zero-state");
+
public static final BooleanFlag ENABLE_SHOW_KEYBOARD_OPTION_IN_ALL_APPS = new DeviceFlag(
"ENABLE_SHOW_KEYBOARD_OPTION_IN_ALL_APPS", true,
"Enable option to show keyboard when going to all-apps");
@@ -292,12 +308,105 @@
"SECONDARY_DRAG_N_DROP_TO_PIN", false,
"Enable dragging and dropping to pin apps within secondary display");
+ public static final BooleanFlag FOLDABLE_WORKSPACE_REORDER = getDebugFlag(
+ "FOLDABLE_WORKSPACE_REORDER", false,
+ "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 ENABLE_MULTI_DISPLAY_PARTIAL_DEPTH = getDebugFlag(
+ "ENABLE_MULTI_DISPLAY_PARTIAL_DEPTH", false,
+ "Allow bottom sheet depth to be smaller than 1 for multi-display devices.");
+
+ public static final BooleanFlag SCROLL_TOP_TO_RESET = new DeviceFlag(
+ "SCROLL_TOP_TO_RESET", true, "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 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_DOWNLOAD_APP_UX_V2 = new DeviceFlag(
+ "ENABLE_DOWNLOAD_APP_UX_V2", true, "Updates the download app UX"
+ + " to have better visuals");
+
+ public static final BooleanFlag ENABLE_TASKBAR_REVISED_THRESHOLDS = getDebugFlag(
+ "ENABLE_TASKBAR_REVISED_THRESHOLDS", true,
+ "Uses revised thresholds for transient taskbar.");
+
+ public static final BooleanFlag FORCE_PERSISTENT_TASKBAR = getDebugFlag(
+ "FORCE_PERSISTENT_TASKBAR", false, "Forces taskbar to be persistent, even in gesture"
+ + " nav mode and when transient taskbar is enabled.");
+
+ public static final BooleanFlag FOLDABLE_SINGLE_PAGE = getDebugFlag(
+ "FOLDABLE_SINGLE_PAGE", false,
+ "Use a single page for the workspace");
+
+ public static final BooleanFlag ENABLE_TRANSIENT_TASKBAR = getDebugFlag(
+ "ENABLE_TRANSIENT_TASKBAR", true, "Enables transient taskbar.");
+
+ public static final BooleanFlag ENABLE_TRACKPAD_GESTURE = getDebugFlag(
+ "ENABLE_TRACKPAD_GESTURE", false, "Enables trackpad gesture.");
+
+ 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_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", true,
+ "Enable the ability to tap a staged app during split select to launch it in full screen"
+ );
+
+ public static final BooleanFlag ENABLE_FORCED_MONO_ICON = getDebugFlag(
+ "ENABLE_FORCED_MONO_ICON", false,
+ "Enable the ability to generate monochromatic icons, if it is not provided by the app"
+ );
+
+ public static final BooleanFlag ENABLE_TASKBAR_EDU_TOOLTIP = getDebugFlag(
+ "ENABLE_TASKBAR_EDU_TOOLTIP", false,
+ "Enable the tooltip version of the Taskbar education flow.");
+
+ public static final BooleanFlag ENABLE_MULTI_INSTANCE = getDebugFlag(
+ "ENABLE_MULTI_INSTANCE", false,
+ "Enables creation and filtering of multiple task instances in overview");
+
+ public static final BooleanFlag ENABLE_TASKBAR_PINNING = getDebugFlag(
+ "ENABLE_TASKBAR_PINNING", false,
+ "Enables taskbar pinning to allow user to switch between transient and persistent "
+ + "taskbar flavors");
+
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);
+ });
}
}
@@ -353,6 +462,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) {
@@ -370,8 +480,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..4906c1d 100644
--- a/src/com/android/launcher3/dragndrop/AddItemActivity.java
+++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java
@@ -23,6 +23,7 @@
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ADD_EXTERNAL_ITEM_PLACED_AUTOMATICALLY;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ADD_EXTERNAL_ITEM_START;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+import static com.android.launcher3.widget.WidgetSections.NO_CATEGORY;
import android.annotation.TargetApi;
import android.app.ActivityOptions;
@@ -31,6 +32,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 +54,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;
@@ -60,14 +64,17 @@
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.model.ItemInstallQueue;
import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.model.WidgetsModel;
import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.PackageItemInfo;
import com.android.launcher3.pm.PinRequestHelper;
+import com.android.launcher3.util.PackageManagerHelper;
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;
@@ -75,6 +82,7 @@
import com.android.launcher3.widget.WidgetCellPreview;
import com.android.launcher3.widget.WidgetImageView;
import com.android.launcher3.widget.WidgetManagerHelper;
+import com.android.launcher3.widget.WidgetSections;
import java.util.function.Supplier;
@@ -102,7 +110,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 +145,28 @@
mAccessibilityManager =
getApplicationContext().getSystemService(AccessibilityManager.class);
- if (mRequest.getRequestType() == PinItemRequest.REQUEST_TYPE_SHORTCUT) {
- setupShortcut();
- } else {
- if (!setupWidget()) {
- // TODO: show error toast?
- finish();
- }
+ final PackageItemInfo targetApp;
+ switch (mRequest.getRequestType()) {
+ case PinItemRequest.REQUEST_TYPE_SHORTCUT:
+ targetApp = setupShortcut();
+ break;
+ case PinItemRequest.REQUEST_TYPE_APPWIDGET:
+ targetApp = setupWidget();
+ break;
+ default:
+ targetApp = null;
+ break;
+ }
+ if (targetApp == null) {
+ // TODO: show error toast?
+ finish();
+ return;
+ }
+ ApplicationInfo info = new PackageManagerHelper(this)
+ .getApplicationInfo(targetApp.packageName, targetApp.user, 0);
+ if (info == null) {
+ finish();
+ return;
}
WidgetCellPreview previewContainer = mWidgetCell.findViewById(
@@ -156,8 +180,13 @@
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);
+ WidgetSections.WidgetSection section = targetApp.widgetCategory == NO_CATEGORY ? null
+ : WidgetSections.getWidgetSections(this).get(targetApp.widgetCategory);
+ widgetAppName.setText(section == null ? info.loadLabel(getPackageManager())
+ : getString(section.mSectionTitle));
mSlideInView = findViewById(R.id.add_item_bottom_sheet);
mSlideInView.addOnCloseListener(this);
@@ -246,25 +275,27 @@
}
}
- private void setupShortcut() {
+ private PackageItemInfo setupShortcut() {
PinShortcutRequestActivityInfo shortcutInfo =
new PinShortcutRequestActivityInfo(mRequest, this);
mWidgetCell.getWidgetView().setTag(new PendingAddShortcutInfo(shortcutInfo));
applyWidgetItemAsync(
() -> new WidgetItem(shortcutInfo, mApp.getIconCache(), getPackageManager()));
+ return new PackageItemInfo(mRequest.getShortcutInfo().getPackage(),
+ mRequest.getShortcutInfo().getUserHandle());
}
- private boolean setupWidget() {
- LauncherAppWidgetProviderInfo widgetInfo = LauncherAppWidgetProviderInfo
+ private PackageItemInfo setupWidget() {
+ final 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 = LauncherWidgetHolder.newInstance(this);
PendingAddWidgetInfo pendingInfo =
new PendingAddWidgetInfo(widgetInfo, CONTAINER_PIN_WIDGETS);
@@ -274,7 +305,8 @@
mWidgetCell.getWidgetView().setTag(pendingInfo);
applyWidgetItemAsync(() -> new WidgetItem(widgetInfo, mIdp, mApp.getIconCache()));
- return true;
+ return WidgetsModel.newPendingItemInfo(this, widgetInfo.getComponent(),
+ widgetInfo.getUser());
}
private void applyWidgetItemAsync(final Supplier<WidgetItem> itemProvider) {
@@ -318,7 +350,7 @@
return;
}
- mPendingBindWidgetId = mAppWidgetHost.allocateAppWidgetId();
+ mPendingBindWidgetId = mAppWidgetHolder.allocateAppWidgetId();
AppWidgetProviderInfo widgetProviderInfo = mRequest.getAppWidgetProviderInfo(this);
boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed(
mPendingBindWidgetId, widgetProviderInfo, mWidgetOptions);
@@ -329,7 +361,7 @@
}
// request bind widget
- mAppWidgetHost.startBindFlow(this, mPendingBindWidgetId,
+ mAppWidgetHolder.startBindFlow(this, mPendingBindWidgetId,
mRequest.getAppWidgetProviderInfo(this), REQUEST_BIND_APPWIDGET);
}
@@ -343,6 +375,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 +399,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/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index 09fe740..f54d05d 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -568,7 +568,8 @@
.setSpring(new SpringForce(0)
.setDampingRatio(DAMPENING_RATIO)
.setStiffness(STIFFNESS));
- mDelta = view.getResources().getDisplayMetrics().density * PARALLAX_MAX_IN_DP;
+ mDelta = Math.min(
+ range, view.getResources().getDisplayMetrics().density * PARALLAX_MAX_IN_DP);
}
public void animateToPos(float value) {
diff --git a/src/com/android/launcher3/dragndrop/SpringLoadedDragController.java b/src/com/android/launcher3/dragndrop/SpringLoadedDragController.java
index 08e50dd..c58ca42 100644
--- a/src/com/android/launcher3/dragndrop/SpringLoadedDragController.java
+++ b/src/com/android/launcher3/dragndrop/SpringLoadedDragController.java
@@ -26,7 +26,7 @@
public class SpringLoadedDragController implements OnAlarmListener {
// how long the user must hover over a mini-screen before it unshrinks
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_HOVER_TIME_IN_TEST = 2000;
private static final long ENTER_SPRING_LOAD_CANCEL_HOVER_TIME = 950;
Alarm mAlarm;
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index e68ebdb..94eea35 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -282,7 +282,6 @@
mFolderName = findViewById(R.id.folder_name);
mFolderName.setTextSize(TypedValue.COMPLEX_UNIT_PX, dp.folderLabelTextSizePx);
mFolderName.setOnBackKeyListener(this);
- mFolderName.setOnFocusChangeListener(this);
mFolderName.setOnEditorActionListener(this);
mFolderName.setSelectAllOnFocus(true);
mFolderName.setInputType(mFolderName.getInputType()
@@ -292,7 +291,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 +367,7 @@
public void startEditingFolderName() {
post(() -> {
- if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
- showLabelSuggestions();
- }
+ showLabelSuggestions();
mFolderName.setHint("");
mIsEditingName = true;
});
@@ -459,6 +456,13 @@
// the folder itself.
requestFocus();
super.onAttachedToWindow();
+ mFolderName.addOnFocusChangeListener(this);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mFolderName.removeOnFocusChangeListener(this);
}
@Override
@@ -1080,8 +1084,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 +1173,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..60442f4 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,10 +278,10 @@
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);
+ mBackground.animateToAccept(cl, lp.getCellX(), lp.getCellY());
mOpenAlarm.setOnAlarmListener(mOnOpenListener);
if (SPRING_LOADING_ENABLED &&
((dragInfo instanceof WorkspaceItemFactory)
@@ -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..10a2637 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;
@@ -202,7 +203,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,13 +219,13 @@
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(
- item.cellX, item.cellY, item.spanX, item.spanY));
+ textView.setLayoutParams(new CellLayoutLayoutParams(
+ item.cellX, item.cellY, item.spanX, item.spanY, item.screenId));
} else {
- lp.cellX = item.cellX;
- lp.cellY = item.cellY;
+ lp.setCellX(item.cellX);
+ lp.setCellY(item.cellY);
lp.cellHSpan = lp.cellVSpan = 1;
}
return textView;
@@ -314,7 +315,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);
@@ -363,7 +364,7 @@
public int findNearestArea(int pixelX, int pixelY) {
int pageIndex = getNextPage();
CellLayout page = getPageAt(pageIndex);
- page.findNearestArea(pixelX, pixelY, 1, 1, sTmpArray);
+ page.findNearestAreaIgnoreOccupied(pixelX, pixelY, 1, 1, sTmpArray);
if (mFolder.isLayoutRtl()) {
sTmpArray[0] = page.getCountX() - sTmpArray[0] - 1;
}
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..9426c22 100644
--- a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
+++ b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
@@ -1,8 +1,7 @@
package com.android.launcher3.graphics;
-import static com.android.launcher3.Utilities.getPrefs;
+import static com.android.launcher3.LauncherPrefs.THEMED_ICONS;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
-import static com.android.launcher3.util.Themes.KEY_THEMED_ICONS;
import static com.android.launcher3.util.Themes.isThemedIconEnabled;
import android.annotation.TargetApi;
@@ -25,8 +24,8 @@
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.InvariantDeviceProfile.GridOption;
+import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.Executors;
/**
@@ -143,11 +142,8 @@
}
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();
- }
+ LauncherPrefs.get(getContext())
+ .put(THEMED_ICONS, values.getAsBoolean(BOOLEAN_VALUE));
return 1;
}
default:
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index a8546e8..4810b15 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -20,6 +20,7 @@
import static android.view.View.MeasureSpec.makeMeasureSpec;
import static android.view.View.VISIBLE;
+import static com.android.launcher3.DeviceProfile.DEFAULT_SCALE;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems;
import static com.android.launcher3.model.ModelUtils.getMissingHotseatRanks;
@@ -36,6 +37,7 @@
import android.content.Intent;
import android.content.res.TypedArray;
import android.graphics.Color;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.ColorDrawable;
@@ -55,20 +57,24 @@
import android.view.WindowManager;
import android.widget.TextClock;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.CellLayout;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.Hotseat;
import com.android.launcher3.InsettableFrameLayout;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.R;
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;
@@ -95,11 +101,12 @@
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;
+import com.android.launcher3.widget.util.WidgetSizes;
import java.util.ArrayList;
import java.util.Collections;
@@ -131,7 +138,7 @@
new ConcurrentLinkedQueue<>();
public PreviewContext(Context base, InvariantDeviceProfile idp) {
- super(base, UserCache.INSTANCE, InstallSessionHelper.INSTANCE,
+ super(base, UserCache.INSTANCE, InstallSessionHelper.INSTANCE, LauncherPrefs.INSTANCE,
LauncherAppState.INSTANCE, InvariantDeviceProfile.INSTANCE,
CustomWidgetManager.INSTANCE, PluginManagerWrapper.INSTANCE,
WindowManagerProxy.INSTANCE, DisplayController.INSTANCE);
@@ -169,10 +176,12 @@
}
}
+ private final List<OnDeviceProfileChangeListener> mDpChangeListeners = new ArrayList<>();
private final Handler mUiHandler;
private final Context mContext;
private final InvariantDeviceProfile mIdp;
private final DeviceProfile mDp;
+ private final DeviceProfile mDpOrig;
private final Rect mInsets;
private final WorkspaceItemInfo mWorkspaceItemInfo;
private final LayoutInflater mHomeElementInflater;
@@ -192,7 +201,16 @@
mUiHandler = new Handler(Looper.getMainLooper());
mContext = context;
mIdp = idp;
- mDp = idp.getDeviceProfile(context).copy(context);
+ mDp = idp.getDeviceProfile(context).toBuilder(context).setViewScaleProvider(
+ this::getAppWidgetScale).build();
+ if (context instanceof PreviewContext) {
+ Context tempContext = ((PreviewContext) context).getBaseContext();
+ mDpOrig = new InvariantDeviceProfile(tempContext, InvariantDeviceProfile
+ .getCurrentGridName(tempContext)).getDeviceProfile(tempContext)
+ .copy(tempContext);
+ } else {
+ mDpOrig = mDp;
+ }
WindowInsets currentWindowInsets = context.getSystemService(WindowManager.class)
.getCurrentWindowMetrics().getWindowInsets();
@@ -316,6 +334,11 @@
}
@Override
+ public List<OnDeviceProfileChangeListener> getOnDeviceProfileChangeListeners() {
+ return mDpChangeListeners;
+ }
+
+ @Override
public Hotseat getHotseat() {
return mHotseat;
}
@@ -390,6 +413,41 @@
addInScreenFromBind(view, info);
}
+ @NonNull
+ private PointF getAppWidgetScale(@Nullable ItemInfo itemInfo) {
+ if (!(itemInfo instanceof LauncherAppWidgetInfo)) {
+ return DEFAULT_SCALE;
+ }
+ LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) itemInfo;
+ final Size launcherWidgetSize = mLauncherWidgetSpanInfo.get(info.appWidgetId);
+ if (launcherWidgetSize == null) {
+ return DEFAULT_SCALE;
+ }
+ final Size origSize = WidgetSizes.getWidgetSizePx(mDpOrig,
+ launcherWidgetSize.getWidth(), launcherWidgetSize.getHeight());
+ final Size newSize = WidgetSizes.getWidgetSizePx(mDp, info.spanX, info.spanY);
+ final Rect previewInset = new Rect();
+ final Rect origInset = new Rect();
+ // When the setup() is called for the LayoutParams, insets are added to the width
+ // and height of the view. This is not accounted for in WidgetSizes and is handled
+ // here.
+ if (mDp.shouldInsetWidgets()) {
+ previewInset.set(mDp.inv.defaultWidgetPadding);
+ } else {
+ previewInset.setEmpty();
+ }
+ if (mDpOrig.shouldInsetWidgets()) {
+ origInset.set(mDpOrig.inv.defaultWidgetPadding);
+ } else {
+ origInset.setEmpty();
+ }
+
+ return new PointF((float) newSize.getWidth() / (origSize.getWidth()
+ + origInset.left + origInset.right),
+ (float) newSize.getHeight() / (origSize.getHeight()
+ + origInset.top + origInset.bottom));
+ }
+
private void inflateAndAddPredictedIcon(WorkspaceItemInfo info) {
CellLayout screen = mWorkspaceScreens.get(info.screenId);
View view = PredictedAppIconInflater.inflate(mHomeElementInflater, screen, info);
@@ -483,8 +541,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, FIRST_SCREEN_ID);
lp.canReorder = false;
firstScreen.addViewToCellLayout(qsb, 0, R.id.search_container_workspace, lp, true);
}
@@ -504,7 +562,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/graphics/PreloadIconDrawable.java b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
index 0eb86b1..f2fde0e 100644
--- a/src/com/android/launcher3/graphics/PreloadIconDrawable.java
+++ b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
@@ -17,26 +17,40 @@
package com.android.launcher3.graphics;
+import static com.android.launcher3.anim.Interpolators.EMPHASIZED;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_DOWNLOAD_APP_UX_V2;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathMeasure;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
import android.graphics.Rect;
+import android.os.SystemClock;
import android.util.Property;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
-import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.anim.AnimatedFloat;
+import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.icons.FastBitmapDrawable;
import com.android.launcher3.icons.GraphicsUtils;
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.util.Themes;
+import com.android.launcher3.util.window.RefreshRateTracker;
+
+import java.util.WeakHashMap;
+import java.util.function.Function;
/**
* Extension of {@link FastBitmapDrawable} which shows a progress bar around the icon.
@@ -62,17 +76,29 @@
private static final int DISABLED_ICON_ALPHA = (int) (0.6f * MAX_PAINT_ALPHA);
private static final long DURATION_SCALE = 500;
+ private static final long SCALE_AND_ALPHA_ANIM_DURATION = 500;
// The smaller the number, the faster the animation would be.
// Duration = COMPLETE_ANIM_FRACTION * DURATION_SCALE
- private static final float COMPLETE_ANIM_FRACTION = 0.3f;
+ private static final float COMPLETE_ANIM_FRACTION = 1f;
private static final float SMALL_SCALE = 0.7f;
- private static final float PROGRESS_STROKE_SCALE = 0.075f;
+ private static final float PROGRESS_STROKE_SCALE = ENABLE_DOWNLOAD_APP_UX_V2.get()
+ ? 0.0655f
+ : 0.075f;
+ private static final float PROGRESS_BOUNDS_SCALE = 0.075f;
private static final int PRELOAD_ACCENT_COLOR_INDEX = 0;
private static final int PRELOAD_BACKGROUND_COLOR_INDEX = 1;
+ private static final int ALPHA_DURATION_MILLIS = 3000;
+ private static final int OVERLAY_ALPHA_RANGE = 127;
+ private static final long WAVE_MOTION_DELAY_FACTOR_MILLIS = 100;
+ private static final WeakHashMap<Integer, PorterDuffColorFilter> COLOR_FILTER_MAP =
+ new WeakHashMap<>();
+ public static final Function<Integer, PorterDuffColorFilter> FILTER_FACTORY =
+ currArgb -> new PorterDuffColorFilter(currArgb, PorterDuff.Mode.SRC_ATOP);
+
private final Matrix mTmpMatrix = new Matrix();
private final PathMeasure mPathMeasure = new PathMeasure();
@@ -90,15 +116,17 @@
private final int mSystemBackgroundColor;
private final boolean mIsDarkMode;
- private int mTrackAlpha;
private float mTrackLength;
- private float mIconScale;
private boolean mRanFinishAnimation;
+ private final int mRefreshRateMillis;
// Progress of the internal state. [0, 1] indicates the fraction of completed progress,
// [1, (1 + COMPLETE_ANIM_FRACTION)] indicates the progress of zoom animation.
private float mInternalStateProgress;
+ // This multiplier is used to animate scale when going from 0 to non-zero and expanding
+ private final Runnable mInvalidateRunnable = this::invalidateSelf;
+ private final AnimatedFloat mIconScaleMultiplier = new AnimatedFloat(mInvalidateRunnable);
private ObjectAnimator mCurrentAnim;
@@ -109,14 +137,16 @@
info,
IconPalette.getPreloadProgressColor(context, info.bitmap.color),
getPreloadColors(context),
- Utilities.isDarkTheme(context));
+ Utilities.isDarkTheme(context),
+ getRefreshRateMillis(context));
}
public PreloadIconDrawable(
ItemInfoWithIcon info,
int indicatorColor,
int[] preloadColors,
- boolean isDarkMode) {
+ boolean isDarkMode,
+ int refreshRateMillis) {
super(info.bitmap);
mItem = info;
mShapePath = GraphicsUtils.getShapePath(DEFAULT_PATH_SIZE);
@@ -130,6 +160,10 @@
mSystemAccentColor = preloadColors[PRELOAD_ACCENT_COLOR_INDEX];
mSystemBackgroundColor = preloadColors[PRELOAD_BACKGROUND_COLOR_INDEX];
mIsDarkMode = isDarkMode;
+ mRefreshRateMillis = refreshRateMillis;
+
+ // If it's a pending app we will animate scale and alpha when it's no longer pending.
+ mIconScaleMultiplier.updateValue(info.getProgressLevel() == 0 ? 0 : 1);
setLevel(info.getProgressLevel());
setIsStartable(info.isAppStartable());
@@ -139,14 +173,17 @@
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
- float progressWidth = PROGRESS_STROKE_SCALE * bounds.width();
+
+ float progressWidth = bounds.width() * (ENABLE_DOWNLOAD_APP_UX_V2.get()
+ ? PROGRESS_BOUNDS_SCALE
+ : PROGRESS_STROKE_SCALE);
mTmpMatrix.setScale(
(bounds.width() - 2 * progressWidth) / DEFAULT_PATH_SIZE,
(bounds.height() - 2 * progressWidth) / DEFAULT_PATH_SIZE);
mTmpMatrix.postTranslate(bounds.left + progressWidth, bounds.top + progressWidth);
mShapePath.transform(mTmpMatrix, mScaledTrackPath);
- mProgressPaint.setStrokeWidth(progressWidth);
+ mProgressPaint.setStrokeWidth(PROGRESS_STROKE_SCALE * bounds.width());
mPathMeasure.setPath(mScaledTrackPath, true);
mTrackLength = mPathMeasure.getLength();
@@ -161,28 +198,44 @@
return;
}
- // Draw background.
- mProgressPaint.setStyle(Paint.Style.FILL_AND_STROKE);
- mProgressPaint.setColor(mSystemBackgroundColor);
- canvas.drawPath(mScaledTrackPath, mProgressPaint);
+ if (!ENABLE_DOWNLOAD_APP_UX_V2.get() && mInternalStateProgress > 0) {
+ // Draw background.
+ mProgressPaint.setStyle(Paint.Style.FILL_AND_STROKE);
+ mProgressPaint.setColor(mSystemBackgroundColor);
+ canvas.drawPath(mScaledTrackPath, mProgressPaint);
+ }
- // Draw track and progress.
- mProgressPaint.setStyle(Paint.Style.STROKE);
- mProgressPaint.setColor(mIsStartable ? mIndicatorColor : mSystemAccentColor);
- mProgressPaint.setAlpha(TRACK_ALPHA);
- canvas.drawPath(mScaledTrackPath, mProgressPaint);
- mProgressPaint.setAlpha(mTrackAlpha);
- canvas.drawPath(mScaledProgressPath, mProgressPaint);
+ if (!ENABLE_DOWNLOAD_APP_UX_V2.get() || mInternalStateProgress > 0) {
+ // Draw track and progress.
+ mProgressPaint.setStyle(Paint.Style.STROKE);
+ mProgressPaint.setColor(mSystemAccentColor);
+ mProgressPaint.setAlpha(TRACK_ALPHA);
+ canvas.drawPath(mScaledTrackPath, mProgressPaint);
+ mProgressPaint.setAlpha(MAX_PAINT_ALPHA);
+ canvas.drawPath(mScaledProgressPath, mProgressPaint);
+ }
int saveCount = canvas.save();
- canvas.scale(mIconScale, mIconScale, bounds.exactCenterX(), bounds.exactCenterY());
+ float scale = ENABLE_DOWNLOAD_APP_UX_V2.get()
+ ? 1 - mIconScaleMultiplier.value * (1 - SMALL_SCALE)
+ : SMALL_SCALE;
+ canvas.scale(scale, scale, bounds.exactCenterX(), bounds.exactCenterY());
+
+ ColorFilter filter = getOverlayFilter();
+ mPaint.setColorFilter(filter);
super.drawInternal(canvas, bounds);
canvas.restoreToCount(saveCount);
+
+ if (ENABLE_DOWNLOAD_APP_UX_V2.get() && filter != null) {
+ reschedule();
+ }
}
@Override
protected void updateFilter() {
- setAlpha(mIsDisabled ? DISABLED_ICON_ALPHA : MAX_PAINT_ALPHA);
+ if (!ENABLE_DOWNLOAD_APP_UX_V2.get()) {
+ setAlpha(mIsDisabled ? DISABLED_ICON_ALPHA : MAX_PAINT_ALPHA);
+ }
}
/**
@@ -191,7 +244,7 @@
@Override
protected boolean onLevelChange(int level) {
// Run the animation if we have already been bound.
- updateInternalState(level * 0.01f, getBounds().width() > 0, false);
+ updateInternalState(level * 0.01f, false, null);
return true;
}
@@ -199,12 +252,18 @@
* Runs the finish animation if it is has not been run after last call to
* {@link #onLevelChange}
*/
- public void maybePerformFinishedAnimation() {
+ public void maybePerformFinishedAnimation(
+ PreloadIconDrawable oldIcon, Runnable onFinishCallback) {
+
+ if (oldIcon.mInternalStateProgress >= 1) {
+ mInternalStateProgress = oldIcon.mInternalStateProgress;
+ }
+
// If the drawable was recently initialized, skip the progress animation.
if (mInternalStateProgress == 0) {
mInternalStateProgress = 1;
}
- updateInternalState(1 + COMPLETE_ANIM_FRACTION, true, true);
+ updateInternalState(1 + COMPLETE_ANIM_FRACTION, true, onFinishCallback);
}
public boolean hasNotCompleted() {
@@ -219,26 +278,29 @@
}
}
- private void updateInternalState(float finalProgress, boolean shouldAnimate, boolean isFinish) {
+ private void updateInternalState(
+ float finalProgress, boolean isFinish, Runnable onFinishCallback) {
if (mCurrentAnim != null) {
mCurrentAnim.cancel();
mCurrentAnim = null;
}
- if (Float.compare(finalProgress, mInternalStateProgress) == 0) {
- return;
- }
- if (finalProgress < mInternalStateProgress) {
- shouldAnimate = false;
- }
- if (!shouldAnimate || mRanFinishAnimation) {
+ boolean animateProgress =
+ finalProgress >= mInternalStateProgress && getBounds().width() > 0;
+ if (!animateProgress || mRanFinishAnimation) {
setInternalProgress(finalProgress);
+ if (isFinish && onFinishCallback != null) {
+ onFinishCallback.run();
+ }
} else {
mCurrentAnim = ObjectAnimator.ofFloat(this, INTERNAL_STATE, finalProgress);
mCurrentAnim.setDuration(
(long) ((finalProgress - mInternalStateProgress) * DURATION_SCALE));
- mCurrentAnim.setInterpolator(Interpolators.LINEAR);
+ mCurrentAnim.setInterpolator(LINEAR);
if (isFinish) {
+ if (onFinishCallback != null) {
+ mCurrentAnim.addListener(AnimatorListeners.forEndCallback(onFinishCallback));
+ }
mCurrentAnim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
@@ -253,47 +315,41 @@
/**
* Sets the internal progress and updates the UI accordingly
* for progress <= 0:
- * - icon in the small scale and disabled state
- * - progress track is visible
+ * - icon with pending motion
+ * - progress track is not visible
* - progress bar is not visible
- * for 0 < progress < 1
- * - icon in the small scale and disabled state
+ * for progress < 1:
+ * - icon without pending motion
* - progress track is visible
- * - progress bar is visible with dominant color. Progress bar is drawn as a fraction of
+ * - progress bar is visible. Progress bar is drawn as a fraction of
* {@link #mScaledTrackPath}.
* @see PathMeasure#getSegment(float, float, Path, boolean)
- * for 1 <= progress < (1 + COMPLETE_ANIM_FRACTION)
- * - we calculate fraction of progress in the above range
- * - progress track is drawn with alpha based on fraction
- * - progress bar is drawn at 100% with alpha based on fraction
- * - icon is scaled up based on fraction and is drawn in enabled state
- * for progress >= (1 + COMPLETE_ANIM_FRACTION)
- * - only icon is drawn in normal state
+ * for progress > 1:
+ * - scale the icon back to full size
*/
private void setInternalProgress(float progress) {
- mInternalStateProgress = progress;
- if (progress <= 0) {
- mIconScale = SMALL_SCALE;
- mScaledTrackPath.reset();
- mTrackAlpha = MAX_PAINT_ALPHA;
+ // Animate scale and alpha from pending to downloading state.
+ if (ENABLE_DOWNLOAD_APP_UX_V2.get() && progress > 0 && mInternalStateProgress == 0) {
+ // Progress is changing for the first time, animate the icon scale
+ Animator iconScaleAnimator = mIconScaleMultiplier.animateToValue(1);
+ iconScaleAnimator.setDuration(SCALE_AND_ALPHA_ANIM_DURATION);
+ iconScaleAnimator.setInterpolator(EMPHASIZED);
+ iconScaleAnimator.start();
}
- if (progress < 1 && progress > 0) {
- mPathMeasure.getSegment(0, progress * mTrackLength, mScaledProgressPath, true);
- mIconScale = SMALL_SCALE;
- mTrackAlpha = MAX_PAINT_ALPHA;
- } else if (progress >= 1) {
- setIsDisabled(mItem.isDisabled());
- mScaledTrackPath.set(mScaledProgressPath);
- float fraction = (progress - 1) / COMPLETE_ANIM_FRACTION;
-
- if (fraction >= 1) {
- // Animation has completed
- mIconScale = 1;
- mTrackAlpha = 0;
- } else {
- mTrackAlpha = Math.round((1 - fraction) * MAX_PAINT_ALPHA);
- mIconScale = SMALL_SCALE + (1 - SMALL_SCALE) * fraction;
+ mInternalStateProgress = progress;
+ if (progress <= 0) {
+ if (!ENABLE_DOWNLOAD_APP_UX_V2.get()) {
+ mScaledTrackPath.reset();
+ }
+ mIconScaleMultiplier.updateValue(0);
+ } else {
+ mPathMeasure.getSegment(
+ 0, Math.min(progress, 1) * mTrackLength, mScaledProgressPath, true);
+ if (progress > 1 && ENABLE_DOWNLOAD_APP_UX_V2.get()) {
+ // map the scale back to original value
+ mIconScaleMultiplier.updateValue(Utilities.mapBoundToRange(
+ progress - 1, 0, COMPLETE_ANIM_FRACTION, 1, 0, EMPHASIZED));
}
}
invalidateSelf();
@@ -310,6 +366,10 @@
return preloadColors;
}
+ private static int getRefreshRateMillis(Context context) {
+ return RefreshRateTracker.getSingleFrameMs(context);
+ }
+
/**
* Returns a FastBitmapDrawable with the icon.
*/
@@ -325,7 +385,53 @@
mItem,
mIndicatorColor,
new int[] {mSystemAccentColor, mSystemBackgroundColor},
- mIsDarkMode);
+ mIsDarkMode,
+ mRefreshRateMillis);
+ }
+
+ @Override
+ public boolean setVisible(boolean visible, boolean restart) {
+ if (!visible) {
+ unscheduleSelf(mInvalidateRunnable);
+ }
+ return super.setVisible(visible, restart);
+ }
+
+ private void reschedule() {
+ unscheduleSelf(mInvalidateRunnable);
+ if (!isVisible()) {
+ return;
+ }
+ final long upTime = SystemClock.uptimeMillis();
+ scheduleSelf(mInvalidateRunnable,
+ upTime - ((upTime % mRefreshRateMillis)) + mRefreshRateMillis);
+ }
+
+ /**
+ * Returns a color filter to be used as an overlay on the pending icon with cascading motion
+ * based on its position.
+ */
+ private ColorFilter getOverlayFilter() {
+ if (!ENABLE_DOWNLOAD_APP_UX_V2.get() || mInternalStateProgress > 0) {
+ // If the download has started, we do no need to animate
+ return null;
+ }
+ long waveMotionDelay = (mItem.cellX * WAVE_MOTION_DELAY_FACTOR_MILLIS)
+ + (mItem.cellY * WAVE_MOTION_DELAY_FACTOR_MILLIS);
+ long time = SystemClock.uptimeMillis();
+ int alpha = (int) Utilities.mapBoundToRange(
+ (int) ((time + waveMotionDelay) % ALPHA_DURATION_MILLIS),
+ 0,
+ ALPHA_DURATION_MILLIS,
+ 0,
+ OVERLAY_ALPHA_RANGE * 2,
+ LINEAR);
+ if (alpha > OVERLAY_ALPHA_RANGE) {
+ alpha = (OVERLAY_ALPHA_RANGE - (alpha % OVERLAY_ALPHA_RANGE));
+ }
+ int overlayColor = mIsDarkMode ? 0 : 255;
+ int currArgb = Color.argb(alpha, overlayColor, overlayColor, overlayColor);
+ return COLOR_FILTER_MAP.computeIfAbsent(currArgb, FILTER_FACTORY);
}
protected static class PreloadIconConstantState extends FastBitmapConstantState {
@@ -335,6 +441,7 @@
protected final int[] mPreloadColors;
protected final boolean mIsDarkMode;
protected final int mLevel;
+ protected final int mRefreshRateMillis;
public PreloadIconConstantState(
Bitmap bitmap,
@@ -342,13 +449,15 @@
ItemInfoWithIcon info,
int indicatorColor,
int[] preloadColors,
- boolean isDarkMode) {
+ boolean isDarkMode,
+ int refreshRateMillis) {
super(bitmap, iconColor);
mInfo = info;
mIndicatorColor = indicatorColor;
mPreloadColors = preloadColors;
mIsDarkMode = isDarkMode;
mLevel = info.getProgressLevel();
+ mRefreshRateMillis = refreshRateMillis;
}
@Override
@@ -357,7 +466,8 @@
mInfo,
mIndicatorColor,
mPreloadColors,
- mIsDarkMode);
+ mIsDarkMode,
+ mRefreshRateMillis);
}
}
}
diff --git a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
index 287b976..85c0a7a 100644
--- a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
+++ b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
@@ -50,7 +50,7 @@
import com.android.launcher3.Workspace;
import com.android.launcher3.graphics.LauncherPreviewRenderer.PreviewContext;
import com.android.launcher3.model.BgDataModel;
-import com.android.launcher3.model.GridSizeMigrationTaskV2;
+import com.android.launcher3.model.GridSizeMigrationUtil;
import com.android.launcher3.model.LoaderTask;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.RunnableList;
@@ -241,10 +241,10 @@
@WorkerThread
private boolean doGridMigrationIfNecessary() {
- if (!GridSizeMigrationTaskV2.needsToMigrate(mContext, mIdp)) {
+ if (!GridSizeMigrationUtil.needsToMigrate(mContext, mIdp)) {
return false;
}
- return GridSizeMigrationTaskV2.migrateGridIfNeeded(mContext, mIdp);
+ return GridSizeMigrationUtil.migrateGridIfNeeded(mContext, mIdp);
}
@UiThread
diff --git a/src/com/android/launcher3/graphics/ShadowDrawable.java b/src/com/android/launcher3/graphics/ShadowDrawable.java
deleted file mode 100644
index d8a7070..0000000
--- a/src/com/android/launcher3/graphics/ShadowDrawable.java
+++ /dev/null
@@ -1,213 +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.graphics;
-
-import android.annotation.TargetApi;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Bitmap;
-import android.graphics.BlurMaskFilter;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.ColorFilter;
-import android.graphics.Paint;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.util.AttributeSet;
-
-import com.android.launcher3.R;
-import com.android.launcher3.icons.BitmapRenderer;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-
-/**
- * A drawable which adds shadow around a child drawable.
- */
-@TargetApi(Build.VERSION_CODES.O)
-public class ShadowDrawable extends Drawable {
-
- private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
-
- private final ShadowDrawableState mState;
-
- @SuppressWarnings("unused")
- public ShadowDrawable() {
- this(new ShadowDrawableState());
- }
-
- private ShadowDrawable(ShadowDrawableState state) {
- mState = state;
- }
-
- @Override
- public void draw(Canvas canvas) {
- Rect bounds = getBounds();
- if (bounds.isEmpty()) {
- return;
- }
- if (mState.mLastDrawnBitmap == null) {
- regenerateBitmapCache();
- }
- canvas.drawBitmap(mState.mLastDrawnBitmap, null, bounds, mPaint);
- }
-
- @Override
- public void setAlpha(int alpha) {
- mPaint.setAlpha(alpha);
- invalidateSelf();
- }
-
- @Override
- public void setColorFilter(ColorFilter colorFilter) {
- mPaint.setColorFilter(colorFilter);
- invalidateSelf();
- }
-
- @Override
- public ConstantState getConstantState() {
- return mState;
- }
-
- @Override
- public int getOpacity() {
- return PixelFormat.TRANSLUCENT;
- }
-
- @Override
- public int getIntrinsicHeight() {
- return mState.mIntrinsicHeight;
- }
-
- @Override
- public int getIntrinsicWidth() {
- return mState.mIntrinsicWidth;
- }
-
- @Override
- public boolean canApplyTheme() {
- return mState.canApplyTheme();
- }
-
- @Override
- public void applyTheme(Resources.Theme t) {
- TypedArray ta = t.obtainStyledAttributes(new int[] {R.attr.isWorkspaceDarkText});
- boolean isDark = ta.getBoolean(0, false);
- ta.recycle();
- if (mState.mIsDark != isDark) {
- mState.mIsDark = isDark;
- mState.mLastDrawnBitmap = null;
- invalidateSelf();
- }
- }
-
- private void regenerateBitmapCache() {
- // Call mutate, so that the pixel allocation by the underlying vector drawable is cleared.
- Drawable d = mState.mChildState.newDrawable().mutate();
- d.setBounds(mState.mShadowSize, mState.mShadowSize,
- mState.mIntrinsicWidth - mState.mShadowSize,
- mState.mIntrinsicHeight - mState.mShadowSize);
- d.setTint(mState.mIsDark ? mState.mDarkTintColor : Color.WHITE);
-
- if (mState.mIsDark) {
- // Dark text do not have any shadow, but just the bitmap
- mState.mLastDrawnBitmap = BitmapRenderer.createHardwareBitmap(
- mState.mIntrinsicWidth, mState.mIntrinsicHeight, d::draw);
- } else {
- Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
- paint.setMaskFilter(new BlurMaskFilter(mState.mShadowSize, BlurMaskFilter.Blur.NORMAL));
-
- // Generate the shadow bitmap
- int[] offset = new int[2];
- Bitmap shadow = BitmapRenderer.createSoftwareBitmap(
- mState.mIntrinsicWidth, mState.mIntrinsicHeight, d::draw)
- .extractAlpha(paint, offset);
-
- paint.setMaskFilter(null);
- paint.setColor(mState.mShadowColor);
- mState.mLastDrawnBitmap = BitmapRenderer.createHardwareBitmap(
- mState.mIntrinsicWidth, mState.mIntrinsicHeight, c -> {
- c.drawBitmap(shadow, offset[0], offset[1], paint);
- d.draw(c);
- });
- }
- }
-
- @Override
- public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs,
- Resources.Theme theme) throws XmlPullParserException, IOException {
- super.inflate(r, parser, attrs, theme);
-
- final TypedArray a = theme == null
- ? r.obtainAttributes(attrs, R.styleable.ShadowDrawable)
- : theme.obtainStyledAttributes(attrs, R.styleable.ShadowDrawable, 0, 0);
- try {
- Drawable d = a.getDrawable(R.styleable.ShadowDrawable_android_src);
- if (d == null) {
- throw new XmlPullParserException("missing src attribute");
- }
- mState.mShadowColor = a.getColor(
- R.styleable.ShadowDrawable_android_shadowColor, Color.BLACK);
- mState.mShadowSize = a.getDimensionPixelSize(
- R.styleable.ShadowDrawable_android_elevation, 0);
- mState.mDarkTintColor = a.getColor(
- R.styleable.ShadowDrawable_darkTintColor, Color.BLACK);
-
- mState.mIntrinsicHeight = d.getIntrinsicHeight() + 2 * mState.mShadowSize;
- mState.mIntrinsicWidth = d.getIntrinsicWidth() + 2 * mState.mShadowSize;
- mState.mChangingConfigurations = d.getChangingConfigurations();
-
- mState.mChildState = d.getConstantState();
- } finally {
- a.recycle();
- }
- }
-
- private static class ShadowDrawableState extends ConstantState {
-
- int mChangingConfigurations;
- int mIntrinsicWidth;
- int mIntrinsicHeight;
-
- int mShadowColor;
- int mShadowSize;
- int mDarkTintColor;
-
- boolean mIsDark;
- Bitmap mLastDrawnBitmap;
- ConstantState mChildState;
-
- @Override
- public Drawable newDrawable() {
- return new ShadowDrawable(this);
- }
-
- @Override
- public int getChangingConfigurations() {
- return mChangingConfigurations;
- }
-
- @Override
- public boolean canApplyTheme() {
- return true;
- }
- }
-}
diff --git a/src/com/android/launcher3/graphics/SysUiScrim.java b/src/com/android/launcher3/graphics/SysUiScrim.java
index 185b8d3..11e7dc8 100644
--- a/src/com/android/launcher3/graphics/SysUiScrim.java
+++ b/src/com/android/launcher3/graphics/SysUiScrim.java
@@ -16,17 +16,10 @@
package com.android.launcher3.graphics;
-import static android.content.Intent.ACTION_SCREEN_OFF;
-import static android.content.Intent.ACTION_USER_PRESENT;
-
import static com.android.launcher3.config.FeatureFlags.KEYGUARD_ANIMATION;
import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
import android.animation.ObjectAnimator;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -43,10 +36,12 @@
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.R;
-import com.android.launcher3.testing.shared.ResourceUtils;
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.testing.shared.ResourceUtils;
import com.android.launcher3.util.DynamicResource;
+import com.android.launcher3.util.ScreenOnTracker;
+import com.android.launcher3.util.ScreenOnTracker.ScreenOnListener;
import com.android.launcher3.util.Themes;
import com.android.systemui.plugins.ResourceProvider;
@@ -85,18 +80,20 @@
/**
* Receiver used to get a signal that the user unlocked their device.
*/
- private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ private final ScreenOnListener mScreenOnListener = new ScreenOnListener() {
@Override
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- if (ACTION_SCREEN_OFF.equals(action)) {
+ public void onScreenOnChanged(boolean isOn) {
+ if (!isOn) {
mAnimateScrimOnNextDraw = true;
- } else if (ACTION_USER_PRESENT.equals(action)) {
- // ACTION_USER_PRESENT is sent after onStart/onResume. This covers the case where
- // the user unlocked and the Launcher is not in the foreground.
- mAnimateScrimOnNextDraw = false;
}
}
+
+ @Override
+ public void onUserPresent() {
+ // ACTION_USER_PRESENT is sent after onStart/onResume. This covers the case where
+ // the user unlocked and the Launcher is not in the foreground.
+ mAnimateScrimOnNextDraw = false;
+ }
};
private static final int MAX_HOTSEAT_SCRIM_ALPHA = 100;
@@ -208,16 +205,14 @@
@Override
public void onViewAttachedToWindow(View view) {
if (!KEYGUARD_ANIMATION.get() && mTopScrim != null) {
- IntentFilter filter = new IntentFilter(ACTION_SCREEN_OFF);
- filter.addAction(ACTION_USER_PRESENT); // When the device wakes up + keyguard is gone
- mRoot.getContext().registerReceiver(mReceiver, filter);
+ ScreenOnTracker.INSTANCE.get(mActivity).addListener(mScreenOnListener);
}
}
@Override
public void onViewDetachedFromWindow(View view) {
if (!KEYGUARD_ANIMATION.get() && mTopScrim != null) {
- mRoot.getContext().unregisterReceiver(mReceiver);
+ ScreenOnTracker.INSTANCE.get(mActivity).removeListener(mScreenOnListener);
}
}
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/LauncherIcons.java b/src/com/android/launcher3/icons/LauncherIcons.java
index 5508c49..57fa8a2 100644
--- a/src/com/android/launcher3/icons/LauncherIcons.java
+++ b/src/com/android/launcher3/icons/LauncherIcons.java
@@ -16,7 +16,10 @@
package com.android.launcher3.icons;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_FORCED_MONO_ICON;
+
import android.content.Context;
+import android.graphics.drawable.Drawable;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.graphics.IconShape;
@@ -68,6 +71,8 @@
private LauncherIcons next;
+ private MonochromeIconFactory mMonochromeIconFactory;
+
protected LauncherIcons(Context context, int fillResIconDpi, int iconBitmapSize, int poolId) {
super(context, fillResIconDpi, iconBitmapSize, IconShape.getShape().enableShapeDetection());
mMonoIconEnabled = Themes.isThemedIconEnabled(context);
@@ -91,6 +96,18 @@
}
@Override
+ protected Drawable getMonochromeDrawable(Drawable base) {
+ Drawable mono = super.getMonochromeDrawable(base);
+ if (mono != null || !ENABLE_FORCED_MONO_ICON.get()) {
+ return mono;
+ }
+ if (mMonochromeIconFactory == null) {
+ mMonochromeIconFactory = new MonochromeIconFactory(mIconBitmapSize);
+ }
+ return mMonochromeIconFactory.wrap(base);
+ }
+
+ @Override
public void close() {
recycle();
}
diff --git a/src/com/android/launcher3/icons/MonochromeIconFactory.java b/src/com/android/launcher3/icons/MonochromeIconFactory.java
new file mode 100644
index 0000000..511dcc7
--- /dev/null
+++ b/src/com/android/launcher3/icons/MonochromeIconFactory.java
@@ -0,0 +1,180 @@
+/*
+ * 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.icons;
+
+import static android.graphics.Paint.FILTER_BITMAP_FLAG;
+
+import android.annotation.TargetApi;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.BlendMode;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+
+import androidx.annotation.WorkerThread;
+
+import com.android.launcher3.icons.BaseIconFactory.ClippedMonoDrawable;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Utility class to generate monochrome icons version for a given drawable.
+ */
+@TargetApi(Build.VERSION_CODES.TIRAMISU)
+public class MonochromeIconFactory extends Drawable {
+
+ private final Bitmap mFlatBitmap;
+ private final Canvas mFlatCanvas;
+ private final Paint mCopyPaint;
+
+ private final Bitmap mAlphaBitmap;
+ private final Canvas mAlphaCanvas;
+ private final byte[] mPixels;
+
+ private final int mBitmapSize;
+ private final int mEdgePixelLength;
+
+ private final Paint mDrawPaint;
+ private final Rect mSrcRect;
+
+ MonochromeIconFactory(int iconBitmapSize) {
+ float extraFactor = AdaptiveIconDrawable.getExtraInsetFraction();
+ float viewPortScale = 1 / (1 + 2 * extraFactor);
+ mBitmapSize = Math.round(iconBitmapSize * 2 * viewPortScale);
+ mPixels = new byte[mBitmapSize * mBitmapSize];
+ mEdgePixelLength = mBitmapSize * (mBitmapSize - iconBitmapSize) / 2;
+
+ mFlatBitmap = Bitmap.createBitmap(mBitmapSize, mBitmapSize, Config.ARGB_8888);
+ mFlatCanvas = new Canvas(mFlatBitmap);
+
+ mAlphaBitmap = Bitmap.createBitmap(mBitmapSize, mBitmapSize, Config.ALPHA_8);
+ mAlphaCanvas = new Canvas(mAlphaBitmap);
+
+ mDrawPaint = new Paint(FILTER_BITMAP_FLAG);
+ mDrawPaint.setColor(Color.WHITE);
+ mSrcRect = new Rect(0, 0, mBitmapSize, mBitmapSize);
+
+ mCopyPaint = new Paint(FILTER_BITMAP_FLAG);
+ mCopyPaint.setBlendMode(BlendMode.SRC);
+
+ // Crate a color matrix which converts the icon to grayscale and then uses the average
+ // of RGB components as the alpha component.
+ ColorMatrix satMatrix = new ColorMatrix();
+ satMatrix.setSaturation(0);
+ float[] vals = satMatrix.getArray();
+ vals[15] = vals[16] = vals[17] = .3333f;
+ vals[18] = vals[19] = 0;
+ mCopyPaint.setColorFilter(new ColorMatrixColorFilter(vals));
+ }
+
+ private void drawDrawable(Drawable drawable) {
+ if (drawable != null) {
+ drawable.setBounds(0, 0, mBitmapSize, mBitmapSize);
+ drawable.draw(mFlatCanvas);
+ }
+ }
+
+ /**
+ * Creates a monochrome version of the provided drawable
+ */
+ @WorkerThread
+ public Drawable wrap(Drawable icon) {
+ if (icon instanceof AdaptiveIconDrawable) {
+ AdaptiveIconDrawable aid = (AdaptiveIconDrawable) icon;
+ mFlatCanvas.drawColor(Color.BLACK);
+ drawDrawable(aid.getBackground());
+ drawDrawable(aid.getForeground());
+ generateMono();
+ return new ClippedMonoDrawable(this);
+ } else {
+ mFlatCanvas.drawColor(Color.WHITE);
+ drawDrawable(icon);
+ generateMono();
+ return this;
+ }
+ }
+
+ @WorkerThread
+ private void generateMono() {
+ mAlphaCanvas.drawBitmap(mFlatBitmap, 0, 0, mCopyPaint);
+
+ // Scale the end points:
+ ByteBuffer buffer = ByteBuffer.wrap(mPixels);
+ buffer.rewind();
+ mAlphaBitmap.copyPixelsToBuffer(buffer);
+
+ int min = 0xFF;
+ int max = 0;
+ for (byte b : mPixels) {
+ min = Math.min(min, b & 0xFF);
+ max = Math.max(max, b & 0xFF);
+ }
+
+ if (min < max) {
+ // rescale pixels to increase contrast
+ float range = max - min;
+
+ // In order to check if the colors should be flipped, we just take the average color
+ // of top and bottom edge which should correspond to be background color. If the edge
+ // colors have more opacity, we flip the colors;
+ int sum = 0;
+ for (int i = 0; i < mEdgePixelLength; i++) {
+ sum += (mPixels[i] & 0xFF);
+ sum += (mPixels[mPixels.length - 1 - i] & 0xFF);
+ }
+ float edgeAverage = sum / (mEdgePixelLength * 2f);
+ float edgeMapped = (edgeAverage - min) / range;
+ boolean flipColor = edgeMapped > .5f;
+
+ for (int i = 0; i < mPixels.length; i++) {
+ int p = mPixels[i] & 0xFF;
+ int p2 = Math.round((p - min) * 0xFF / range);
+ mPixels[i] = flipColor ? (byte) (255 - p2) : (byte) (p2);
+ }
+ buffer.rewind();
+ mAlphaBitmap.copyPixelsFromBuffer(buffer);
+ }
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ canvas.drawBitmap(mAlphaBitmap, mSrcRect, getBounds(), mDrawPaint);
+ }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.TRANSLUCENT;
+ }
+
+ @Override
+ public void setAlpha(int i) {
+ mDrawPaint.setAlpha(i);
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter colorFilter) {
+ mDrawPaint.setColorFilter(colorFilter);
+ }
+}
diff --git a/src/com/android/launcher3/icons/ShortcutCachingLogic.java b/src/com/android/launcher3/icons/ShortcutCachingLogic.java
index 6a8f34a..bb7248f 100644
--- a/src/com/android/launcher3/icons/ShortcutCachingLogic.java
+++ b/src/com/android/launcher3/icons/ShortcutCachingLogic.java
@@ -29,9 +29,10 @@
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.BaseIconFactory.IconOptions;
import com.android.launcher3.icons.cache.CachingLogic;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.util.Themes;
@@ -44,41 +45,47 @@
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),
- Themes.getColorAccent(context));
+ return li.createBadgedIconBitmap(unbadgedDrawable,
+ new IconOptions().setExtractedColor(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..5f6df27 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,17 @@
+ "result page etc.")
LAUNCHER_ALLAPPS_SCROLLED(985),
+ @UiEvent(doc = "User scrolled up on the all apps personal A-Z list.")
+ LAUNCHER_ALLAPPS_PERSONAL_SCROLLED_UP(1287),
+
+ @UiEvent(doc = "User scrolled down on the all apps personal A-Z list.")
+ LAUNCHER_ALLAPPS_PERSONAL_SCROLLED_DOWN(1288),
+
+ @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 +607,35 @@
@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),
+
+ @UiEvent(doc = "User has collapsed the work FAB button by scrolling down in the all apps"
+ + " work A-Z list.")
+ LAUNCHER_WORK_FAB_BUTTON_COLLAPSE(1276),
+
+ @UiEvent(doc = "User has collapsed the work FAB button by scrolling up in the all apps"
+ + " work A-Z list.")
+ LAUNCHER_WORK_FAB_BUTTON_EXTEND(1277),
+
+ @UiEvent(doc = "User scrolled down on the search result page.")
+ LAUNCHER_ALLAPPS_SEARCH_SCROLLED_DOWN(1285),
+
+ @UiEvent(doc = "User scrolled up on the search result page.")
+ LAUNCHER_ALLAPPS_SEARCH_SCROLLED_UP(1286),
;
// ADD MORE
@@ -713,6 +757,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 +788,10 @@
HOT(2),
TIMEOUT(3),
FAIL(4),
- COLD_USERWAITING(5);
-
+ COLD_USERWAITING(5),
+ ATOMIC(6),
+ CONTROLLED(7),
+ CACHED(8);
private final int mId;
LatencyType(int id) {
@@ -748,7 +801,6 @@
public int getId() {
return mId;
}
-
}
/**
@@ -781,6 +833,13 @@
}
/**
+ * Sets sub event type.
+ */
+ default StatsLatencyLogger withSubEventType(int type) {
+ return this;
+ }
+
+ /**
* Sets packageId of log message.
*/
default StatsLatencyLogger withPackageId(int packageId) {
@@ -795,6 +854,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 +946,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 +977,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 4875d83..8f85bfb 100644
--- a/src/com/android/launcher3/model/AllAppsList.java
+++ b/src/com/android/launcher3/model/AllAppsList.java
@@ -64,7 +64,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/BaseLoaderResults.java b/src/com/android/launcher3/model/BaseLauncherBinder.java
similarity index 95%
rename from src/com/android/launcher3/model/BaseLoaderResults.java
rename to src/com/android/launcher3/model/BaseLauncherBinder.java
index 8c6428b..9f8db51 100644
--- a/src/com/android/launcher3/model/BaseLoaderResults.java
+++ b/src/com/android/launcher3/model/BaseLauncherBinder.java
@@ -47,12 +47,11 @@
import java.util.concurrent.Executor;
/**
- * Base Helper class to handle results of {@link com.android.launcher3.model.LoaderTask}.
+ * Binds the results of {@link com.android.launcher3.model.LoaderTask} to the Callbacks objects.
*/
-public abstract class BaseLoaderResults {
+public abstract class BaseLauncherBinder {
- protected static final String TAG = "LoaderResults";
- protected static final int INVALID_SCREEN_ID = -1;
+ protected static final String TAG = "LauncherBinder";
private static final int ITEMS_CHUNK = 6; // batch size for the workspace icons
protected final LooperExecutor mUiExecutor;
@@ -65,7 +64,7 @@
private int mMyBindingId;
- public BaseLoaderResults(LauncherAppState app, BgDataModel dataModel,
+ public BaseLauncherBinder(LauncherAppState app, BgDataModel dataModel,
AllAppsList allAppsList, Callbacks[] callbacksList, LooperExecutor uiExecutor) {
mUiExecutor = uiExecutor;
mApp = app;
@@ -101,8 +100,14 @@
}
}
+ /**
+ * BindDeepShortcuts is abstract because it is a no-op for the go launcher.
+ */
public abstract void bindDeepShortcuts();
+ /**
+ * Binds the all apps results from LoaderTask to the callbacks UX.
+ */
public void bindAllApps() {
// shallow copy
AppInfo[] apps = mBgAllAppsList.copyData();
@@ -110,6 +115,9 @@
executeCallbacksTask(c -> c.bindAllApplications(apps, flags), mUiExecutor);
}
+ /**
+ * bindWidgets is abstract because it is a no-op for the go launcher.
+ */
public abstract void bindWidgets();
/**
@@ -160,6 +168,9 @@
});
}
+ /**
+ * Only used in LoaderTask.
+ */
public LooperIdleLock newIdleLock(Object lock) {
LooperIdleLock idleLock = new LooperIdleLock(lock, mUiExecutor.getLooper());
// If we are not binding or if the main looper is already idle, there is no reason to wait
diff --git a/src/com/android/launcher3/model/BaseModelUpdateTask.java b/src/com/android/launcher3/model/BaseModelUpdateTask.java
index 2a6a691..74a2c5d 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,17 @@
.forEach(this::bindExtraContainerItems);
}
- public void bindExtraContainerItems(FixedContainerItems item) {
- FixedContainerItems copy = item.clone();
- scheduleCallbackTask(c -> c.bindExtraContainerItems(copy));
+ public void bindExtraContainerItems(@NonNull final FixedContainerItems item) {
+ scheduleCallbackTask(c -> c.bindExtraContainerItems(item));
}
- 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/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index de23c4b..b0f6e13 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -433,26 +433,9 @@
public final int containerId;
public final List<ItemInfo> items;
- public FixedContainerItems(int containerId) {
- this(containerId, new ArrayList<>());
- }
-
public FixedContainerItems(int containerId, List<ItemInfo> items) {
this.containerId = containerId;
- this.items = items;
- }
-
- @Override
- public FixedContainerItems clone() {
- return new FixedContainerItems(containerId, new ArrayList<>(items));
- }
-
- public void setItems(List<ItemInfo> newItems) {
- items.clear();
- newItems.forEach(item -> {
- item.container = containerId;
- items.add(item);
- });
+ this.items = Collections.unmodifiableList(items);
}
}
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/DeviceGridState.java b/src/com/android/launcher3/model/DeviceGridState.java
index 46f0b0b..edc8c1b 100644
--- a/src/com/android/launcher3/model/DeviceGridState.java
+++ b/src/com/android/launcher3/model/DeviceGridState.java
@@ -17,7 +17,10 @@
package com.android.launcher3.model;
import static com.android.launcher3.InvariantDeviceProfile.DeviceType;
-import static com.android.launcher3.InvariantDeviceProfile.TYPE_PHONE;
+import static com.android.launcher3.LauncherPrefs.DB_FILE;
+import static com.android.launcher3.LauncherPrefs.DEVICE_TYPE;
+import static com.android.launcher3.LauncherPrefs.HOTSEAT_COUNT;
+import static com.android.launcher3.LauncherPrefs.WORKSPACE_SIZE;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_GRID_SIZE_2;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_GRID_SIZE_3;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_GRID_SIZE_4;
@@ -25,11 +28,10 @@
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_GRID_SIZE_6;
import android.content.Context;
-import android.content.SharedPreferences;
import android.text.TextUtils;
import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.Utilities;
+import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.logging.StatsLogManager.LauncherEvent;
import java.util.Locale;
@@ -58,11 +60,11 @@
}
public DeviceGridState(Context context) {
- SharedPreferences prefs = Utilities.getPrefs(context);
- mGridSizeString = prefs.getString(KEY_WORKSPACE_SIZE, "");
- mNumHotseat = prefs.getInt(KEY_HOTSEAT_COUNT, -1);
- mDeviceType = prefs.getInt(KEY_DEVICE_TYPE, TYPE_PHONE);
- mDbFile = prefs.getString(KEY_DB_FILE, "");
+ LauncherPrefs lp = LauncherPrefs.get(context);
+ mGridSizeString = lp.get(WORKSPACE_SIZE);
+ mNumHotseat = lp.get(HOTSEAT_COUNT);
+ mDeviceType = lp.get(DEVICE_TYPE);
+ mDbFile = lp.get(DB_FILE);
}
/**
@@ -90,12 +92,11 @@
* Stores the device state to shared preferences
*/
public void writeToPrefs(Context context) {
- Utilities.getPrefs(context).edit()
- .putString(KEY_WORKSPACE_SIZE, mGridSizeString)
- .putInt(KEY_HOTSEAT_COUNT, mNumHotseat)
- .putInt(KEY_DEVICE_TYPE, mDeviceType)
- .putString(KEY_DB_FILE, mDbFile)
- .apply();
+ LauncherPrefs.get(context).put(
+ WORKSPACE_SIZE.to(mGridSizeString),
+ HOTSEAT_COUNT.to(mNumHotseat),
+ DEVICE_TYPE.to(mDeviceType),
+ DB_FILE.to(mDbFile));
}
/**
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java b/src/com/android/launcher3/model/GridSizeMigrationUtil.java
similarity index 71%
rename from src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
rename to src/com/android/launcher3/model/GridSizeMigrationUtil.java
index c25929a..eded5ea 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationUtil.java
@@ -31,7 +31,7 @@
import android.util.ArrayMap;
import android.util.Log;
-import androidx.annotation.VisibleForTesting;
+import androidx.annotation.NonNull;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
@@ -47,6 +47,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;
@@ -62,42 +63,13 @@
* This class takes care of shrinking the workspace (by maximum of one row and one column), as a
* result of restoring from a larger device or device density change.
*/
-public class GridSizeMigrationTaskV2 {
+public class GridSizeMigrationUtil {
- private static final String TAG = "GridSizeMigrationTaskV2";
- private static final boolean DEBUG = false;
+ private static final String TAG = "GridSizeMigrationUtil";
+ private static final boolean DEBUG = true;
- private final Context mContext;
- private final SQLiteDatabase mDb;
- private final DbReader mSrcReader;
- private final DbReader mDestReader;
-
- private final List<DbEntry> mHotseatItems;
- private final List<DbEntry> mWorkspaceItems;
-
- private final List<DbEntry> mHotseatDiff;
- private final List<DbEntry> mWorkspaceDiff;
-
- private final int mDestHotseatSize;
- private final int mTrgX, mTrgY;
-
- @VisibleForTesting
- protected GridSizeMigrationTaskV2(Context context, SQLiteDatabase db, DbReader srcReader,
- DbReader destReader, int destHotseatSize, Point targetSize) {
- mContext = context;
- mDb = db;
- mSrcReader = srcReader;
- mDestReader = destReader;
-
- mHotseatItems = destReader.loadHotseatEntries();
- mWorkspaceItems = destReader.loadAllWorkspaceEntries();
-
- mHotseatDiff = calcDiff(mSrcReader.loadHotseatEntries(), mHotseatItems);
- mWorkspaceDiff = calcDiff(mSrcReader.loadAllWorkspaceEntries(), mWorkspaceItems);
- mDestHotseatSize = destHotseatSize;
-
- mTrgX = targetSize.x;
- mTrgY = targetSize.y;
+ private GridSizeMigrationUtil() {
+ // Util class should not be instantiated
}
/**
@@ -185,9 +157,8 @@
context, validPackages);
Point targetSize = new Point(destDeviceState.getColumns(), destDeviceState.getRows());
- GridSizeMigrationTaskV2 task = new GridSizeMigrationTaskV2(context, t.getDb(),
- srcReader, destReader, destDeviceState.getNumHotseat(), targetSize);
- task.migrate(srcDeviceState, destDeviceState);
+ migrate(context, t.getDb(), srcReader, destReader, destDeviceState.getNumHotseat(),
+ targetSize, srcDeviceState, destDeviceState);
if (!migrateForPreview) {
dropTable(t.getDb(), LauncherSettings.Favorites.TMP_TABLE);
@@ -210,25 +181,66 @@
}
}
- @VisibleForTesting
- protected boolean migrate(DeviceGridState srcDeviceState, DeviceGridState destDeviceState) {
- if (mHotseatDiff.isEmpty() && mWorkspaceDiff.isEmpty()) {
+ public static boolean migrate(
+ @NonNull final Context context, @NonNull final SQLiteDatabase db,
+ @NonNull final DbReader srcReader, @NonNull final DbReader destReader,
+ final int destHotseatSize, @NonNull final Point targetSize,
+ @NonNull final DeviceGridState srcDeviceState,
+ @NonNull final DeviceGridState destDeviceState) {
+
+ final List<DbEntry> srcHotseatItems = srcReader.loadHotseatEntries();
+ final List<DbEntry> srcWorkspaceItems = srcReader.loadAllWorkspaceEntries();
+ final List<DbEntry> dstHotseatItems = destReader.loadHotseatEntries();
+ final List<DbEntry> dstWorkspaceItems = destReader.loadAllWorkspaceEntries();
+ final List<DbEntry> hotseatToBeAdded = new ArrayList<>(1);
+ final List<DbEntry> workspaceToBeAdded = new ArrayList<>(1);
+ final IntArray toBeRemoved = new IntArray();
+
+ calcDiff(srcHotseatItems, dstHotseatItems, hotseatToBeAdded, toBeRemoved);
+ calcDiff(srcWorkspaceItems, dstWorkspaceItems, workspaceToBeAdded, toBeRemoved);
+
+ final int trgX = targetSize.x;
+ final int trgY = targetSize.y;
+
+ if (DEBUG) {
+ Log.d(TAG, "Start migration:"
+ + "\n Source Device:"
+ + srcWorkspaceItems.stream().map(DbEntry::toString).collect(
+ Collectors.joining(",\n", "[", "]"))
+ + "\n Target Device:"
+ + dstWorkspaceItems.stream().map(DbEntry::toString).collect(
+ Collectors.joining(",\n", "[", "]"))
+ + "\n Removing Items:"
+ + dstWorkspaceItems.stream().filter(entry ->
+ toBeRemoved.contains(entry.id)).map(DbEntry::toString).collect(
+ Collectors.joining(",\n", "[", "]"))
+ + "\n Adding Workspace Items:"
+ + workspaceToBeAdded.stream().map(DbEntry::toString).collect(
+ Collectors.joining(",\n", "[", "]"))
+ + "\n Adding Hotseat Items:"
+ + hotseatToBeAdded.stream().map(DbEntry::toString).collect(
+ Collectors.joining(",\n", "[", "]"))
+ );
+ }
+ if (!toBeRemoved.isEmpty()) {
+ removeEntryFromDb(destReader.mDb, destReader.mTableName, toBeRemoved);
+ }
+ if (hotseatToBeAdded.isEmpty() && workspaceToBeAdded.isEmpty()) {
return false;
}
// Sort the items by the reading order.
- Collections.sort(mHotseatDiff);
- Collections.sort(mWorkspaceDiff);
+ Collections.sort(hotseatToBeAdded);
+ Collections.sort(workspaceToBeAdded);
// Migrate hotseat
- HotseatPlacementSolution hotseatSolution = new HotseatPlacementSolution(mDb, mSrcReader,
- mDestReader, mContext, mDestHotseatSize, mHotseatItems, mHotseatDiff);
- hotseatSolution.find();
+ solveHotseatPlacement(db, srcReader,
+ destReader, context, destHotseatSize, dstHotseatItems, hotseatToBeAdded);
// Migrate workspace.
// First we create a collection of the screens
List<Integer> screens = new ArrayList<>();
- for (int screenId = 0; screenId <= mDestReader.mLastScreenId; screenId++) {
+ for (int screenId = 0; screenId <= destReader.mLastScreenId; screenId++) {
screens.add(screenId);
}
@@ -243,55 +255,47 @@
if (DEBUG) {
Log.d(TAG, "Migrating " + screenId);
}
- GridPlacementSolution workspaceSolution = new GridPlacementSolution(mDb, mSrcReader,
- mDestReader, mContext, screenId, mTrgX, mTrgY, mWorkspaceDiff, false);
- workspaceSolution.find();
- if (mWorkspaceDiff.isEmpty()) {
+ solveGridPlacement(db, srcReader,
+ destReader, context, screenId, trgX, trgY, workspaceToBeAdded, false);
+ if (workspaceToBeAdded.isEmpty()) {
break;
}
}
// In case the new grid is smaller, there might be some leftover items that don't fit on
// any of the screens, in this case we add them to new screens until all of them are placed.
- int screenId = mDestReader.mLastScreenId + 1;
- while (!mWorkspaceDiff.isEmpty()) {
- GridPlacementSolution workspaceSolution = new GridPlacementSolution(mDb, mSrcReader,
- mDestReader, mContext, screenId, mTrgX, mTrgY, mWorkspaceDiff,
- preservePages);
- workspaceSolution.find();
+ int screenId = destReader.mLastScreenId + 1;
+ while (!workspaceToBeAdded.isEmpty()) {
+ solveGridPlacement(db, srcReader,
+ destReader, context, screenId, trgX, trgY, workspaceToBeAdded, preservePages);
screenId++;
}
return true;
}
- /** Return what's in the src but not in the dest */
- private static List<DbEntry> calcDiff(List<DbEntry> src, List<DbEntry> dest) {
- Map<String, Integer> destIdSet = new HashMap<>();
- for (DbEntry entry : dest) {
- String entryID = entry.getEntryMigrationId();
- if (destIdSet.containsKey(entryID)) {
- destIdSet.put(entryID, destIdSet.get(entryID) + 1);
- } else {
- destIdSet.put(entryID, 1);
+ /**
+ * Calculate the differences between {@code src} (denoted by A) and {@code dest}
+ * (denoted by B).
+ * All DbEntry in A - B will be added to {@code toBeAdded}
+ * All DbEntry.id in B - A will be added to {@code toBeRemoved}
+ */
+ private static void calcDiff(@NonNull final List<DbEntry> src,
+ @NonNull final List<DbEntry> dest, @NonNull final List<DbEntry> toBeAdded,
+ @NonNull final IntArray toBeRemoved) {
+ src.forEach(entry -> {
+ if (!dest.contains(entry)) {
+ toBeAdded.add(entry);
}
- }
- List<DbEntry> diff = new ArrayList<>();
- for (DbEntry entry : src) {
- String entryID = entry.getEntryMigrationId();
- if (destIdSet.containsKey(entryID)) {
- Integer count = destIdSet.get(entryID);
- if (count <= 0) {
- diff.add(entry);
- destIdSet.remove(entryID);
- } else {
- destIdSet.put(entryID, count - 1);
+ });
+ dest.forEach(entry -> {
+ if (!src.contains(entry)) {
+ toBeRemoved.add(entry.id);
+ if (entry.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
+ entry.mFolderItems.values().forEach(ids -> ids.forEach(toBeRemoved::add));
}
- } else {
- diff.add(entry);
}
- }
- return diff;
+ });
}
private static void insertEntryInDb(SQLiteDatabase db, Context context, DbEntry entry,
@@ -363,144 +367,88 @@
return validPackages;
}
- protected static class GridPlacementSolution {
-
- private final SQLiteDatabase mDb;
- private final DbReader mSrcReader;
- private final DbReader mDestReader;
- private final Context mContext;
- private final GridOccupancy mOccupied;
- private final int mScreenId;
- private final int mTrgX;
- private final int mTrgY;
- private final List<DbEntry> mSortedItemsToPlace;
- private final boolean mMatchingScreenIdOnly;
-
- private int mNextStartX;
- private int mNextStartY;
-
- GridPlacementSolution(SQLiteDatabase db, DbReader srcReader, DbReader destReader,
- Context context, int screenId, int trgX, int trgY, List<DbEntry> sortedItemsToPlace,
- boolean matchingScreenIdOnly) {
- mDb = db;
- mSrcReader = srcReader;
- mDestReader = destReader;
- mContext = context;
- mOccupied = new GridOccupancy(trgX, trgY);
- mScreenId = screenId;
- mTrgX = trgX;
- mTrgY = trgY;
- mNextStartX = 0;
- mNextStartY = mScreenId == 0 && FeatureFlags.QSB_ON_FIRST_SCREEN
- ? 1 /* smartspace */ : 0;
- List<DbEntry> existedEntries = mDestReader.mWorkspaceEntriesByScreenId.get(screenId);
- if (existedEntries != null) {
- for (DbEntry entry : existedEntries) {
- mOccupied.markCells(entry, true);
- }
- }
- mSortedItemsToPlace = sortedItemsToPlace;
- mMatchingScreenIdOnly = matchingScreenIdOnly;
- }
-
- public void find() {
- Iterator<DbEntry> iterator = mSortedItemsToPlace.iterator();
- while (iterator.hasNext()) {
- final DbEntry entry = iterator.next();
- if (mMatchingScreenIdOnly && entry.screenId < mScreenId) continue;
- if (mMatchingScreenIdOnly && entry.screenId > mScreenId) break;
- if (entry.minSpanX > mTrgX || entry.minSpanY > mTrgY) {
- iterator.remove();
- continue;
- }
- if (findPlacement(entry)) {
- insertEntryInDb(mDb, mContext, entry, mSrcReader.mTableName,
- mDestReader.mTableName);
- iterator.remove();
- }
+ private static void solveGridPlacement(@NonNull final SQLiteDatabase db,
+ @NonNull final DbReader srcReader, @NonNull final DbReader destReader,
+ @NonNull final Context context, final int screenId, final int trgX, final int trgY,
+ @NonNull final List<DbEntry> sortedItemsToPlace, final boolean matchingScreenIdOnly) {
+ final GridOccupancy occupied = new GridOccupancy(trgX, trgY);
+ final Point trg = new Point(trgX, trgY);
+ final Point next = new Point(0, screenId == 0 && FeatureFlags.QSB_ON_FIRST_SCREEN
+ ? 1 /* smartspace */ : 0);
+ List<DbEntry> existedEntries = destReader.mWorkspaceEntriesByScreenId.get(screenId);
+ if (existedEntries != null) {
+ for (DbEntry entry : existedEntries) {
+ occupied.markCells(entry, true);
}
}
-
- /**
- * Search for the next possible placement of an icon. (mNextStartX, mNextStartY) serves as
- * a memoization of last placement, we can start our search for next placement from there
- * to speed up the search.
- */
- private boolean findPlacement(DbEntry entry) {
- for (int y = mNextStartY; y < mTrgY; y++) {
- for (int x = mNextStartX; x < mTrgX; x++) {
- boolean fits = mOccupied.isRegionVacant(x, y, entry.spanX, entry.spanY);
- boolean minFits = mOccupied.isRegionVacant(x, y, entry.minSpanX,
- entry.minSpanY);
- if (minFits) {
- entry.spanX = entry.minSpanX;
- entry.spanY = entry.minSpanY;
- }
- if (fits || minFits) {
- entry.screenId = mScreenId;
- entry.cellX = x;
- entry.cellY = y;
- mOccupied.markCells(entry, true);
- mNextStartX = x + entry.spanX;
- mNextStartY = y;
- return true;
- }
- }
- mNextStartX = 0;
+ Iterator<DbEntry> iterator = sortedItemsToPlace.iterator();
+ while (iterator.hasNext()) {
+ final DbEntry entry = iterator.next();
+ if (matchingScreenIdOnly && entry.screenId < screenId) continue;
+ if (matchingScreenIdOnly && entry.screenId > screenId) break;
+ if (entry.minSpanX > trgX || entry.minSpanY > trgY) {
+ iterator.remove();
+ continue;
}
- return false;
+ if (findPlacementForEntry(entry, next, trg, occupied, screenId)) {
+ insertEntryInDb(db, context, entry, srcReader.mTableName, destReader.mTableName);
+ iterator.remove();
+ }
}
}
- protected static class HotseatPlacementSolution {
-
- private final SQLiteDatabase mDb;
- private final DbReader mSrcReader;
- private final DbReader mDestReader;
- private final Context mContext;
- private final HotseatOccupancy mOccupied;
- private final List<DbEntry> mItemsToPlace;
-
- HotseatPlacementSolution(SQLiteDatabase db, DbReader srcReader, DbReader destReader,
- Context context, int hotseatSize, List<DbEntry> placedHotseatItems,
- List<DbEntry> itemsToPlace) {
- mDb = db;
- mSrcReader = srcReader;
- mDestReader = destReader;
- mContext = context;
- mOccupied = new HotseatOccupancy(hotseatSize);
- for (DbEntry entry : placedHotseatItems) {
- mOccupied.markCells(entry, true);
- }
- mItemsToPlace = itemsToPlace;
- }
-
- public void find() {
- for (int i = 0; i < mOccupied.mCells.length; i++) {
- if (!mOccupied.mCells[i] && !mItemsToPlace.isEmpty()) {
- DbEntry entry = mItemsToPlace.remove(0);
- entry.screenId = i;
- // These values does not affect the item position, but we should set them
- // to something other than -1.
- entry.cellX = i;
- entry.cellY = 0;
- insertEntryInDb(mDb, mContext, entry, mSrcReader.mTableName,
- mDestReader.mTableName);
- mOccupied.markCells(entry, true);
+ /**
+ * Search for the next possible placement of an icon. (mNextStartX, mNextStartY) serves as
+ * a memoization of last placement, we can start our search for next placement from there
+ * to speed up the search.
+ */
+ private static boolean findPlacementForEntry(@NonNull final DbEntry entry,
+ @NonNull final Point next, @NonNull final Point trg,
+ @NonNull final GridOccupancy occupied, final int screenId) {
+ for (int y = next.y; y < trg.y; y++) {
+ for (int x = next.x; x < trg.x; x++) {
+ boolean fits = occupied.isRegionVacant(x, y, entry.spanX, entry.spanY);
+ boolean minFits = occupied.isRegionVacant(x, y, entry.minSpanX,
+ entry.minSpanY);
+ if (minFits) {
+ entry.spanX = entry.minSpanX;
+ entry.spanY = entry.minSpanY;
+ }
+ if (fits || minFits) {
+ entry.screenId = screenId;
+ entry.cellX = x;
+ entry.cellY = y;
+ occupied.markCells(entry, true);
+ next.set(x + entry.spanX, y);
+ return true;
}
}
+ next.set(0, next.y);
+ }
+ return false;
+ }
+
+ private static void solveHotseatPlacement(@NonNull final SQLiteDatabase db,
+ @NonNull final DbReader srcReader, @NonNull final DbReader destReader,
+ @NonNull final Context context, final int hotseatSize,
+ @NonNull final List<DbEntry> placedHotseatItems,
+ @NonNull final List<DbEntry> itemsToPlace) {
+
+ final boolean[] occupied = new boolean[hotseatSize];
+ for (DbEntry entry : placedHotseatItems) {
+ occupied[entry.screenId] = true;
}
- private class HotseatOccupancy {
-
- private final boolean[] mCells;
-
- private HotseatOccupancy(int hotseatSize) {
- mCells = new boolean[hotseatSize];
- }
-
- private void markCells(ItemInfo item, boolean value) {
- mCells[item.screenId] = value;
+ for (int i = 0; i < occupied.length; i++) {
+ if (!occupied[i] && !itemsToPlace.isEmpty()) {
+ DbEntry entry = itemsToPlace.remove(0);
+ entry.screenId = i;
+ // These values does not affect the item position, but we should set them
+ // to something other than -1.
+ entry.cellX = i;
+ entry.cellY = 0;
+ insertEntryInDb(db, context, entry, srcReader.mTableName, destReader.mTableName);
+ occupied[entry.screenId] = true;
}
}
}
@@ -513,8 +461,6 @@
private final Set<String> mValidPackages;
private int mLastScreenId = -1;
- private final ArrayList<DbEntry> mHotseatEntries = new ArrayList<>();
- private final ArrayList<DbEntry> mWorkspaceEntries = new ArrayList<>();
private final Map<Integer, ArrayList<DbEntry>> mWorkspaceEntriesByScreenId =
new ArrayMap<>();
@@ -526,7 +472,8 @@
mValidPackages = validPackages;
}
- protected ArrayList<DbEntry> loadHotseatEntries() {
+ protected List<DbEntry> loadHotseatEntries() {
+ final List<DbEntry> hotseatEntries = new ArrayList<>();
Cursor c = queryWorkspace(
new String[]{
LauncherSettings.Favorites._ID, // 0
@@ -575,14 +522,15 @@
entriesToRemove.add(entry.id);
continue;
}
- mHotseatEntries.add(entry);
+ hotseatEntries.add(entry);
}
removeEntryFromDb(mDb, mTableName, entriesToRemove);
c.close();
- return mHotseatEntries;
+ return hotseatEntries;
}
- protected ArrayList<DbEntry> loadAllWorkspaceEntries() {
+ protected List<DbEntry> loadAllWorkspaceEntries() {
+ final List<DbEntry> workspaceEntries = new ArrayList<>();
Cursor c = queryWorkspace(
new String[]{
LauncherSettings.Favorites._ID, // 0
@@ -597,10 +545,6 @@
LauncherSettings.Favorites.APPWIDGET_ID}, // 9
LauncherSettings.Favorites.CONTAINER + " = "
+ LauncherSettings.Favorites.CONTAINER_DESKTOP);
- return loadWorkspaceEntries(c);
- }
-
- private ArrayList<DbEntry> loadWorkspaceEntries(Cursor c) {
final int indexId = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
final int indexItemType = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
final int indexScreen = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
@@ -676,7 +620,7 @@
entriesToRemove.add(entry.id);
continue;
}
- mWorkspaceEntries.add(entry);
+ workspaceEntries.add(entry);
if (!mWorkspaceEntriesByScreenId.containsKey(entry.screenId)) {
mWorkspaceEntriesByScreenId.put(entry.screenId, new ArrayList<>());
}
@@ -684,7 +628,7 @@
}
removeEntryFromDb(mDb, mTableName, entriesToRemove);
c.close();
- return mWorkspaceEntries;
+ return workspaceEntries;
}
private int getFolderItemsCount(DbEntry entry) {
@@ -760,12 +704,12 @@
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
DbEntry entry = (DbEntry) o;
- return Objects.equals(mIntent, entry.mIntent);
+ return Objects.equals(getEntryMigrationId(), entry.getEntryMigrationId());
}
@Override
public int hashCode() {
- return Objects.hash(mIntent);
+ return Objects.hash(getEntryMigrationId());
}
public void updateContentValues(ContentValues values) {
@@ -776,17 +720,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 +730,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..46a6a66 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -125,7 +125,7 @@
private FirstScreenBroadcast mFirstScreenBroadcast;
- private final LoaderResults mResults;
+ private final LauncherBinder mLauncherBinder;
private final LauncherApps mLauncherApps;
private final UserManager mUserManager;
@@ -145,12 +145,12 @@
private String mDbName;
public LoaderTask(LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel dataModel,
- ModelDelegate modelDelegate, LoaderResults results) {
+ ModelDelegate modelDelegate, LauncherBinder launcherBinder) {
mApp = app;
mBgAllAppsList = bgAllAppsList;
mBgDataModel = dataModel;
mModelDelegate = modelDelegate;
- mResults = results;
+ mLauncherBinder = launcherBinder;
mLauncherApps = mApp.getContext().getSystemService(LauncherApps.class);
mUserManager = mApp.getContext().getSystemService(UserManager.class);
@@ -163,7 +163,7 @@
// Wait until the either we're stopped or the other threads are done.
// This way we don't start loading all apps until the workspace has settled
// down.
- LooperIdleLock idleLock = mResults.newIdleLock(this);
+ LooperIdleLock idleLock = mLauncherBinder.newIdleLock(this);
// Just in case mFlushingWorkerThread changes but we aren't woken up,
// wait no longer than 1sec at a time
while (!mStopped && idleLock.awaitLocked(1000));
@@ -221,7 +221,7 @@
}
verifyNotStopped();
- mResults.bindWorkspace(true /* incrementBindId */);
+ mLauncherBinder.bindWorkspace(true /* incrementBindId */);
logASplit(logger, "bindWorkspace");
mModelDelegate.workspaceLoadComplete();
@@ -245,7 +245,7 @@
logASplit(logger, "loadAllApps");
verifyNotStopped();
- mResults.bindAllApps();
+ mLauncherBinder.bindAllApps();
logASplit(logger, "bindAllApps");
verifyNotStopped();
@@ -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();
@@ -273,15 +271,13 @@
logASplit(logger, "loadDeepShortcuts");
verifyNotStopped();
- mResults.bindDeepShortcuts();
+ mLauncherBinder.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();
@@ -294,7 +290,7 @@
logASplit(logger, "load widgets");
verifyNotStopped();
- mResults.bindWidgets();
+ mLauncherBinder.bindWidgets();
logASplit(logger, "bindWidgets");
verifyNotStopped();
@@ -304,9 +300,7 @@
logASplit(logger, "save widgets in icon cache");
// fifth step
- if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
- loadFolderNames();
- }
+ loadFolderNames();
verifyNotStopped();
updateHandler.finish();
@@ -355,7 +349,7 @@
final WidgetManagerHelper widgetHelper = new WidgetManagerHelper(context);
boolean clearDb = false;
- if (!GridSizeMigrationTaskV2.migrateGridIfNeeded(context)) {
+ if (!GridSizeMigrationUtil.migrateGridIfNeeded(context)) {
// Migration failed. Clear workspace.
clearDb = true;
}
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..2dd44a4 100644
--- a/src/com/android/launcher3/model/data/ItemInfo.java
+++ b/src/com/android/launcher3/model/data/ItemInfo.java
@@ -21,7 +21,6 @@
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_PREDICTION;
-import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_SEARCH_RESULTS;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_SETTINGS;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_SHORTCUTS;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_TASKSWITCHER;
@@ -39,9 +38,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;
@@ -51,7 +53,6 @@
import com.android.launcher3.logger.LauncherAtom.AllAppsContainer;
import com.android.launcher3.logger.LauncherAtom.ContainerInfo;
import com.android.launcher3.logger.LauncherAtom.PredictionContainer;
-import com.android.launcher3.logger.LauncherAtom.SearchResultContainer;
import com.android.launcher3.logger.LauncherAtom.SettingsContainer;
import com.android.launcher3.logger.LauncherAtom.Shortcut;
import com.android.launcher3.logger.LauncherAtom.ShortcutsContainer;
@@ -60,6 +61,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 +75,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 +146,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 +191,7 @@
mComponentName = info.getTargetComponent();
}
+ @Nullable
public Intent getIntent() {
return null;
}
@@ -209,7 +219,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 +230,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 +244,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 +255,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 +300,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 +354,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 +390,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 +404,7 @@
/**
* Returns {@link ContainerInfo} used when logging this item.
*/
+ @NonNull
public ContainerInfo getContainerInfo() {
switch (container) {
case CONTAINER_HOTSEAT:
@@ -415,10 +437,6 @@
return ContainerInfo.newBuilder()
.setPredictionContainer(PredictionContainer.getDefaultInstance())
.build();
- case CONTAINER_SEARCH_RESULTS:
- return ContainerInfo.newBuilder()
- .setSearchResultContainer(SearchResultContainer.getDefaultInstance())
- .build();
case CONTAINER_SHORTCUTS:
return ContainerInfo.newBuilder()
.setShortcutsContainer(ShortcutsContainer.getDefaultInstance())
@@ -435,10 +453,12 @@
return ContainerInfo.newBuilder()
.setWallpapersContainer(WallpapersContainer.getDefaultInstance())
.build();
- case EXTENDED_CONTAINERS:
- return ContainerInfo.newBuilder()
- .setExtendedContainers(getExtendedContainer())
- .build();
+ default:
+ if (container <= EXTENDED_CONTAINERS) {
+ return ContainerInfo.newBuilder()
+ .setExtendedContainers(getExtendedContainer())
+ .build();
+ }
}
return ContainerInfo.getDefaultInstance();
}
@@ -447,6 +467,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 +475,7 @@
/**
* Returns shallow copy of the object.
*/
+ @NonNull
public ItemInfo makeShallowCopy() {
ItemInfo itemInfo = new ItemInfo();
itemInfo.copyFrom(this);
@@ -463,7 +485,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
deleted file mode 100644
index e879313..0000000
--- a/src/com/android/launcher3/model/data/SearchActionItemInfo.java
+++ /dev/null
@@ -1,186 +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.model.data;
-
-import static com.android.launcher3.LauncherSettings.Favorites.EXTENDED_CONTAINERS;
-
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.drawable.Icon;
-import android.os.Process;
-import android.os.UserHandle;
-
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.logger.LauncherAtom.ItemInfo;
-import com.android.launcher3.logger.LauncherAtom.SearchActionItem;
-
-/**
- * Represents a SearchAction with in launcher
- */
-public class SearchActionItemInfo extends ItemInfoWithIcon implements WorkspaceItemFactory {
-
- public static final int FLAG_SHOULD_START = 1 << 1;
- public static final int FLAG_SHOULD_START_FOR_RESULT = FLAG_SHOULD_START | 1 << 2;
- public static final int FLAG_BADGE_WITH_PACKAGE = 1 << 3;
- public static final int FLAG_PRIMARY_ICON_FROM_TITLE = 1 << 4;
- public static final int FLAG_BADGE_WITH_COMPONENT_NAME = 1 << 5;
- public static final int FLAG_ALLOW_PINNING = 1 << 6;
- public static final int FLAG_SEARCH_IN_APP = 1 << 7;
-
- private String mFallbackPackageName;
- private int mFlags = 0;
- private Icon mIcon;
-
- // If true title does not contain any personal info and eligible for logging.
- private boolean mIsPersonalTitle;
- private Intent mIntent;
-
- private PendingIntent mPendingIntent;
-
- public SearchActionItemInfo(Icon icon, String packageName, UserHandle user,
- CharSequence title, boolean isPersonalTitle) {
- mIsPersonalTitle = isPersonalTitle;
- this.itemType = LauncherSettings.Favorites.ITEM_TYPE_SEARCH_ACTION;
- this.user = user == null ? Process.myUserHandle() : user;
- this.title = title;
- this.container = EXTENDED_CONTAINERS;
- mFallbackPackageName = packageName;
- mIcon = icon;
- }
-
- private SearchActionItemInfo(SearchActionItemInfo info) {
- super(info);
- }
-
- @Override
- public void copyFrom(com.android.launcher3.model.data.ItemInfo info) {
- super.copyFrom(info);
- SearchActionItemInfo itemInfo = (SearchActionItemInfo) info;
- this.mFallbackPackageName = itemInfo.mFallbackPackageName;
- this.mIcon = itemInfo.mIcon;
- this.mFlags = itemInfo.mFlags;
- this.mIsPersonalTitle = itemInfo.mIsPersonalTitle;
- }
-
- /**
- * Returns if multiple flags are all available.
- */
- public boolean hasFlags(int flags) {
- return (mFlags & flags) != 0;
- }
-
- public void setFlags(int flags) {
- mFlags |= flags;
- }
-
- @Override
- public Intent getIntent() {
- return mIntent;
- }
-
- /**
- * Setter for mIntent with assertion for null value mPendingIntent
- */
- public void setIntent(Intent intent) {
- if (mPendingIntent != null && intent != null) {
- throw new RuntimeException(
- "SearchActionItemInfo can only have either an Intent or a PendingIntent");
- }
- mIntent = intent;
- }
-
- public PendingIntent getPendingIntent() {
- return mPendingIntent;
- }
-
- /**
- * Setter of mPendingIntent with assertion for null value mIntent
- */
- public void setPendingIntent(PendingIntent pendingIntent) {
- if (mIntent != null && pendingIntent != null) {
- throw new RuntimeException(
- "SearchActionItemInfo can only have either an Intent or a PendingIntent");
- }
- mPendingIntent = pendingIntent;
- }
-
- @Nullable
- public Icon getIcon() {
- return mIcon;
- }
-
- @Override
- public ItemInfoWithIcon clone() {
- return new SearchActionItemInfo(this);
- }
-
- @Override
- public ItemInfo buildProto(FolderInfo fInfo) {
- SearchActionItem.Builder itemBuilder = SearchActionItem.newBuilder()
- .setPackageName(mFallbackPackageName);
-
- if (!mIsPersonalTitle) {
- itemBuilder.setTitle(title.toString());
- }
- return getDefaultItemInfoBuilder()
- .setSearchActionItem(itemBuilder)
- .setContainerInfo(getContainerInfo())
- .build();
- }
-
- /**
- * Returns true if result supports drag/drop to home screen
- */
- public boolean supportsPinning() {
- return hasFlags(FLAG_ALLOW_PINNING) && getIntentPackageName() != null;
- }
-
- /**
- * Creates a {@link WorkspaceItemInfo} coorsponding to search action to be stored in launcher db
- */
- @Override
- public WorkspaceItemInfo makeWorkspaceItem(Context context) {
- WorkspaceItemInfo info = new WorkspaceItemInfo();
- info.title = title;
- info.bitmap = bitmap;
- info.intent = mIntent;
-
- if (hasFlags(FLAG_SHOULD_START_FOR_RESULT)) {
- info.options |= WorkspaceItemInfo.FLAG_START_FOR_RESULT;
- }
- LauncherAppState app = LauncherAppState.getInstance(context);
- app.getModel().updateAndBindWorkspaceItem(() -> {
- PackageItemInfo pkgInfo = new PackageItemInfo(getIntentPackageName(), user);
- app.getIconCache().getTitleAndIconForApp(pkgInfo, false);
- info.bitmap = info.bitmap.withBadgeInfo(pkgInfo.bitmap);
- return info;
- });
- return info;
- }
-
- @Nullable
- private String getIntentPackageName() {
- if (mIntent != null) {
- if (mIntent.getPackage() != null) return mIntent.getPackage();
- return mFallbackPackageName;
- }
- return null;
- }
-}
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/PageIndicator.java b/src/com/android/launcher3/pageindicators/PageIndicator.java
index ec69193..570d6ff 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicator.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicator.java
@@ -46,4 +46,11 @@
default void skipAnimationsToEnd() {
// No-op by default
}
+
+ /**
+ * Sets the paint color.
+ */
+ default void setPaintColor(int color) {
+ // No-op by default
+ }
}
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
index 29eefe2..b2c64b3 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
@@ -16,6 +16,8 @@
package com.android.launcher3.pageindicators;
+import static com.android.launcher3.config.FeatureFlags.SHOW_DOT_PAGINATION;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
@@ -27,14 +29,22 @@
import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.Paint.Style;
+import android.graphics.Rect;
import android.graphics.RectF;
+import android.os.Handler;
+import android.os.Looper;
import android.util.AttributeSet;
-import android.util.Property;
+import android.util.FloatProperty;
+import android.util.IntProperty;
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.util.Themes;
@@ -43,61 +53,91 @@
* {@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 PAGINATION_FADE_IN_DURATION = 83;
+ private static final int PAGINATION_FADE_OUT_DURATION = 167;
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 float DOT_ALPHA_FRACTION = 0.5f;
+ private static final int DOT_GAP_FACTOR = SHOW_DOT_PAGINATION.get() ? 4 : 3;
+ private static final int VISIBLE_ALPHA = 255;
+ 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 RectF sTempRect = new RectF();
- private static final Property<PageIndicatorDots, Float> CURRENT_POSITION
- = new Property<PageIndicatorDots, Float>(float.class, "current_position") {
+ private static final FloatProperty<PageIndicatorDots> CURRENT_POSITION =
+ new FloatProperty<PageIndicatorDots>("current_position") {
+ @Override
+ public Float get(PageIndicatorDots obj) {
+ return obj.mCurrentPosition;
+ }
+
+ @Override
+ public void setValue(PageIndicatorDots obj, float pos) {
+ obj.mCurrentPosition = pos;
+ obj.invalidate();
+ obj.invalidateOutline();
+ }
+ };
+
+ private static final IntProperty<PageIndicatorDots> PAGINATION_ALPHA =
+ new IntProperty<PageIndicatorDots>("pagination_alpha") {
@Override
- public Float get(PageIndicatorDots obj) {
- return obj.mCurrentPosition;
+ public Integer get(PageIndicatorDots obj) {
+ return obj.mPaginationPaint.getAlpha();
}
@Override
- public void set(PageIndicatorDots obj, Float pos) {
- obj.mCurrentPosition = pos;
+ public void setValue(PageIndicatorDots obj, int alpha) {
+ obj.mPaginationPaint.setAlpha(alpha);
obj.invalidate();
- obj.invalidateOutline();
}
};
- private final Paint mCirclePaint;
+ private final Handler mDelayedPaginationFadeHandler = new Handler(Looper.getMainLooper());
private final float mDotRadius;
+ private final float mCircleGap;
private final boolean mIsRtl;
private int mNumPages;
private int mActivePage;
+ private int mTotalScroll;
+ private boolean mShouldAutoHide;
+ 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 final Runnable mHidePaginationRunnable =
+ () -> animatePaginationToAlpha(INVISIBLE_ALPHA);
+
public PageIndicatorDots(Context context) {
this(context, null);
}
@@ -109,37 +149,129 @@
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));
- mDotRadius = getResources().getDimension(R.dimen.page_indicator_dot_size) / 2;
+ mPaginationPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mPaginationPaint.setStyle(Style.FILL);
+ mPaginationPaint.setColor(Themes.getAttrColor(context, R.attr.folderPaginationColor));
+ mDotRadius = (SHOW_DOT_PAGINATION.get()
+ ? getResources().getDimension(R.dimen.page_indicator_dot_size_v2)
+ : getResources().getDimension(R.dimen.page_indicator_dot_size))
+ / 2;
+ mCircleGap = DOT_GAP_FACTOR * mDotRadius;
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_DOT_PAGINATION.get() && mActivePage != 0 && currentScroll == 0) {
+ CURRENT_POSITION.set(this, (float) mActivePage);
+ return;
+ }
- 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) {
+ return;
+ }
+
+ if (mShouldAutoHide) {
+ animatePaginationToAlpha(VISIBLE_ALPHA);
+ }
+
+ if (mIsRtl) {
+ currentScroll = totalScroll - currentScroll;
+ }
+
+ mTotalScroll = totalScroll;
+
+ int scrollPerPage = totalScroll / (mNumPages - 1);
+ int pageToLeft = scrollPerPage == 0 ? 0 : 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 (mShouldAutoHide) {
+ hideAfterDelay();
}
+ } else if (currentScroll > pageToRightScroll - scrollThreshold) {
+ // scroll is far enough from left page to go to the right page
+ animateToPosition(pageToLeft + 1);
+ if (mShouldAutoHide) {
+ hideAfterDelay();
+ }
+ } else {
+ // scroll is between left and right page
+ animateToPosition(pageToLeft + SHIFT_PER_ANIMATION);
+ if (mShouldAutoHide) {
+ mDelayedPaginationFadeHandler.removeCallbacksAndMessages(null);
+ }
+ }
+ }
+
+ @Override
+ public void setShouldAutoHide(boolean shouldAutoHide) {
+ mShouldAutoHide = shouldAutoHide && SHOW_DOT_PAGINATION.get();
+ if (shouldAutoHide && mPaginationPaint.getAlpha() > INVISIBLE_ALPHA) {
+ hideAfterDelay();
+ } else if (!shouldAutoHide) {
+ mDelayedPaginationFadeHandler.removeCallbacksAndMessages(null);
+ }
+ }
+
+ @Override
+ public void setPaintColor(int color) {
+ mPaginationPaint.setColor(color);
+ }
+
+ 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;
+ }
+
+ if (mAlphaAnimator != null) {
+ mAlphaAnimator.cancel();
+ }
+ mAlphaAnimator = ObjectAnimator.ofInt(this, PAGINATION_ALPHA,
+ alpha);
+ // If we are animating to decrease the alpha, then it's a fade out animation
+ // whereas if we are animating to increase the alpha, it's a fade in animation.
+ mAlphaAnimator.setDuration(alpha < mToAlpha
+ ? PAGINATION_FADE_OUT_DURATION
+ : PAGINATION_FADE_IN_DURATION);
+ mAlphaAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mAlphaAnimator = null;
+ }
+ });
+ mAlphaAnimator.start();
+ mToAlpha = alpha;
+ }
+
+ /**
+ * 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 +309,7 @@
}
public void playEntryAnimation() {
- int count = mEntryAnimationRadiusFactors.length;
+ int count = mEntryAnimationRadiusFactors.length;
if (count == 0) {
mEntryAnimationRadiusFactors = null;
invalidate();
@@ -231,16 +363,25 @@
// 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 (mNumPages < 2) {
+ return;
+ }
+
+ if (mShouldAutoHide && mTotalScroll == 0) {
+ mPaginationPaint.setAlpha(INVISIBLE_ALPHA);
+ 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,19 +393,26 @@
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);
+ canvas.drawCircle(x, y, mDotRadius * mEntryAnimationRadiusFactors[i],
+ mPaginationPaint);
x += circleGap;
}
} else {
- mCirclePaint.setAlpha(DOT_INACTIVE_ALPHA);
+ int alpha = mPaginationPaint.getAlpha();
+
+ // Here we draw the dots
+ mPaginationPaint.setAlpha(SHOW_DOT_PAGINATION.get()
+ ? ((int) (alpha * DOT_ALPHA_FRACTION))
+ : DOT_ALPHA);
for (int i = 0; i < mNumPages; i++) {
- canvas.drawCircle(x, y, mDotRadius, mCirclePaint);
+ 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(SHOW_DOT_PAGINATION.get() ? alpha : PAGE_INDICATOR_ALPHA);
+ canvas.drawRoundRect(getActiveRect(), mDotRadius, mDotRadius, mPaginationPaint);
}
}
@@ -272,23 +420,23 @@
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;
+ 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;
if (delta < SHIFT_PER_ANIMATION) {
// dot is capturing the right circle.
- sTempRect.right += delta * circleGap * 2;
+ sTempRect.right += delta * mCircleGap * 2;
} else {
// Dot is leaving the left circle.
- sTempRect.right += circleGap;
+ sTempRect.right += mCircleGap;
delta -= SHIFT_PER_ANIMATION;
- sTempRect.left += delta * circleGap * 2;
+ sTempRect.left += delta * mCircleGap * 2;
}
if (mIsRtl) {
@@ -296,6 +444,7 @@
sTempRect.right = getWidth() - sTempRect.left;
sTempRect.left = sTempRect.right - rectWidth;
}
+
return sTempRect;
}
@@ -331,9 +480,20 @@
@Override
public void onAnimationEnd(Animator animation) {
if (!mCancelled) {
+ if (mShouldAutoHide && SHOW_DOT_PAGINATION.get()) {
+ hideAfterDelay();
+ }
mAnimator = null;
animateToPosition(mFinalPosition);
}
}
}
+
+ /**
+ * 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..db23566 100644
--- a/src/com/android/launcher3/pm/InstallSessionHelper.java
+++ b/src/com/android/launcher3/pm/InstallSessionHelper.java
@@ -16,8 +16,6 @@
package com.android.launcher3.pm;
-import static com.android.launcher3.Utilities.getPrefs;
-
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherApps;
@@ -30,9 +28,11 @@
import android.util.Log;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.WorkerThread;
+import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.SessionCommitReceiver;
import com.android.launcher3.Utilities;
@@ -51,45 +51,57 @@
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.
- protected static final String PROMISE_ICON_IDS = "promise_icon_ids";
+ @NonNull
+ public 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) {
return mPromiseIconIds;
}
mPromiseIconIds = IntSet.wrap(IntArray.fromConcatString(
- getPrefs(mAppContext).getString(PROMISE_ICON_IDS, "")));
+ LauncherPrefs.get(mAppContext).get(LauncherPrefs.PROMISE_ICON_IDS)));
IntArray existingIds = new IntArray();
for (SessionInfo info : getActiveSessions().values()) {
@@ -108,6 +120,7 @@
return mPromiseIconIds;
}
+ @NonNull
public HashMap<PackageUserKey, SessionInfo> getActiveSessions() {
HashMap<PackageUserKey, SessionInfo> activePackages = new HashMap<>();
for (SessionInfo info : getAllVerifiedSessions()) {
@@ -117,6 +130,7 @@
return activePackages;
}
+ @Nullable
public SessionInfo getActiveSessionInfo(UserHandle user, String pkg) {
for (SessionInfo info : getAllVerifiedSessions()) {
boolean match = pkg.equals(info.getAppPackageName());
@@ -131,16 +145,17 @@
}
private void updatePromiseIconPrefs() {
- getPrefs(mAppContext).edit()
- .putString(PROMISE_ICON_IDS, getPromiseIconIds().getArray().toConcatString())
- .apply();
+ LauncherPrefs.get(mAppContext).put(LauncherPrefs.PROMISE_ICON_IDS,
+ getPromiseIconIds().getArray().toConcatString());
}
- 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 +182,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 +217,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 +238,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 +260,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 +283,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/popup/ArrowPopup.java b/src/com/android/launcher3/popup/ArrowPopup.java
index 196cc56..9a745ab 100644
--- a/src/com/android/launcher3/popup/ArrowPopup.java
+++ b/src/com/android/launcher3/popup/ArrowPopup.java
@@ -21,14 +21,12 @@
import static com.android.launcher3.anim.Interpolators.ACCELERATED_EASE;
import static com.android.launcher3.anim.Interpolators.DECELERATED_EASE;
import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_LOCAL_COLOR_POPUPS;
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.content.Context;
import android.content.res.Resources;
import android.graphics.Color;
@@ -36,15 +34,12 @@
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
-import android.os.Build;
import android.util.AttributeSet;
import android.util.Pair;
-import android.util.SparseIntArray;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
@@ -52,21 +47,17 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.InsettableFrameLayout;
-import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
-import com.android.launcher3.Workspace;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.shortcuts.DeepShortcutView;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.BaseDragLayer;
-import com.android.launcher3.widget.LocalColorExtractor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
-import java.util.List;
/**
* A container for shortcuts to deep links and notifications associated with an app.
@@ -89,10 +80,6 @@
protected int CLOSE_CHILD_FADE_START_DELAY = 0;
protected int CLOSE_CHILD_FADE_DURATION = 140;
- // Index used to get background color when using local wallpaper color extraction,
- private static final int DARK_COLOR_EXTRACTION_INDEX = android.R.color.system_neutral2_800;
- private static final int LIGHT_COLOR_EXTRACTION_INDEX = android.R.color.system_accent2_50;
-
protected final Rect mTempRect = new Rect();
protected final LayoutInflater mInflater;
@@ -124,10 +111,8 @@
// The rect string of the view that the arrow is attached to, in screen reference frame.
protected int mArrowColor;
- protected final List<LocalColorExtractor> mColorExtractors;
protected final float mElevation;
- private final int mBackgroundColor;
private final String mIterateChildrenTag;
@@ -140,8 +125,8 @@
mActivityContext = ActivityContext.lookupContext(context);
mIsRtl = Utilities.isRtl(getResources());
- mBackgroundColor = Themes.getAttrColor(context, R.attr.popupColorPrimary);
- mArrowColor = mBackgroundColor;
+ int backgroundColor = Themes.getAttrColor(context, R.attr.popupColorPrimary);
+ mArrowColor = backgroundColor;
mElevation = getResources().getDimension(R.dimen.deep_shortcuts_elevation);
// Initialize arrow view
@@ -158,25 +143,18 @@
int smallerRadius = resources.getDimensionPixelSize(R.dimen.popup_smaller_radius);
mRoundedTop = new GradientDrawable();
- mRoundedTop.setColor(mBackgroundColor);
+ mRoundedTop.setColor(backgroundColor);
mRoundedTop.setCornerRadii(new float[] { mOutlineRadius, mOutlineRadius, mOutlineRadius,
mOutlineRadius, smallerRadius, smallerRadius, smallerRadius, smallerRadius});
mRoundedBottom = new GradientDrawable();
- mRoundedBottom.setColor(mBackgroundColor);
+ mRoundedBottom.setColor(backgroundColor);
mRoundedBottom.setCornerRadii(new float[] { smallerRadius, smallerRadius, smallerRadius,
smallerRadius, mOutlineRadius, mOutlineRadius, mOutlineRadius, mOutlineRadius});
mIterateChildrenTag = getContext().getString(R.string.popup_container_iterate_children);
- boolean shouldUseColorExtraction = mActivityContext.shouldUseColorExtractionForPopup();
- if (shouldUseColorExtraction && Utilities.ATLEAST_S && ENABLE_LOCAL_COLOR_POPUPS.get()) {
- mColorExtractors = new ArrayList<>();
- } else {
- mColorExtractors = null;
- }
-
- if (shouldUseColorExtraction) {
+ if (mActivityContext.shouldUseColorExtractionForPopup()) {
mColorIds = new int[]{R.color.popup_shade_first, R.color.popup_shade_second,
R.color.popup_shade_third};
} else {
@@ -220,11 +198,6 @@
}
/**
- * Called when all view inflation and reordering in complete.
- */
- protected void onInflationComplete(boolean isReversed) { }
-
- /**
* Set the margins and radius of backgrounds after views are properly ordered.
*/
public void assignMarginsAndBackgrounds(ViewGroup viewGroup) {
@@ -271,13 +244,9 @@
backgroundColor = colors[numVisibleChild % colors.length];
}
- if (!ENABLE_LOCAL_COLOR_POPUPS.get()) {
- // Arrow color matches the first child or the last child.
- if (!mIsAboveIcon && numVisibleChild == 0 && viewGroup == this) {
- mArrowColor = backgroundColor;
- } else if (mIsAboveIcon) {
- mArrowColor = backgroundColor;
- }
+ // Arrow color matches the first child or the last child.
+ if (mIsAboveIcon || (numVisibleChild == 0 && viewGroup == this)) {
+ mArrowColor = backgroundColor;
}
if (view instanceof ViewGroup && mIterateChildrenTag.equals(view.getTag())) {
@@ -301,10 +270,7 @@
}
}
- if (!ENABLE_LOCAL_COLOR_POPUPS.get()) {
- setChildColor(view, backgroundColor, colorAnimator);
- }
-
+ setChildColor(view, backgroundColor, colorAnimator);
numVisibleChild++;
}
}
@@ -320,85 +286,6 @@
return view instanceof DeepShortcutView;
}
- @TargetApi(Build.VERSION_CODES.S)
- private int getExtractedColor(SparseIntArray colors) {
- int index = Utilities.isDarkTheme(getContext())
- ? DARK_COLOR_EXTRACTION_INDEX
- : LIGHT_COLOR_EXTRACTION_INDEX;
- return colors.get(index, mBackgroundColor);
- }
-
- protected void addPreDrawForColorExtraction(Launcher launcher) {
- getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- getViewTreeObserver().removeOnPreDrawListener(this);
- initColorExtractionLocations(launcher);
- return true;
- }
- });
- }
-
- /**
- * Returns list of child views that will receive local color extraction treatment.
- * Note: Order should match the view hierarchy.
- */
- protected List<View> getChildrenForColorExtraction() {
- return Collections.emptyList();
- }
-
- private void initColorExtractionLocations(Launcher launcher) {
- if (mColorExtractors == null) {
- return;
- }
- Workspace<?> workspace = launcher.getWorkspace();
- if (workspace == null) {
- return;
- }
-
- boolean firstVisibleChild = true;
- int screenId = workspace.getScreenIdForPageIndex(workspace.getCurrentPage());
- DragLayer dragLayer = launcher.getDragLayer();
-
- final View[] viewAlignedWithArrow = new View[1];
-
- // Order matters here, since we need the arrow to match the color of its adjacent view.
- for (final View view : getChildrenForColorExtraction()) {
- if (view != null && view.getVisibility() == VISIBLE) {
- Rect pos = new Rect();
- dragLayer.getDescendantRectRelativeToSelf(view, pos);
- if (!pos.isEmpty()) {
- LocalColorExtractor extractor = LocalColorExtractor.newInstance(launcher);
- extractor.setWorkspaceLocation(pos, dragLayer, screenId);
- extractor.setListener(extractedColors -> {
- AnimatorSet colors = new AnimatorSet();
- int newColor = getExtractedColor(extractedColors);
- setChildColor(view, newColor, colors);
- int numChildren = view instanceof ViewGroup
- ? ((ViewGroup) view).getChildCount() : 0;
- for (int i = 0; i < numChildren; ++i) {
- View childView = ((ViewGroup) view).getChildAt(i);
- setChildColor(childView, newColor, colors);
- }
- if (viewAlignedWithArrow[0] == view) {
- mArrowColor = newColor;
- updateArrowColor();
- }
- colors.setDuration(150);
- view.post(colors::start);
- });
- mColorExtractors.add(extractor);
-
- if (mIsAboveIcon || firstVisibleChild) {
- viewAlignedWithArrow[0] = view;
- }
- firstVisibleChild = false;
- }
- }
- }
-
- }
-
/**
* Sets the background color of the child.
*/
@@ -425,7 +312,6 @@
if (reverseOrder) {
reverseOrder(viewsToFlip);
}
- onInflationComplete(reverseOrder);
assignMarginsAndBackgrounds(this);
if (shouldAddArrow()) {
addArrow();
@@ -438,7 +324,6 @@
*/
public void show() {
setupForDisplay();
- onInflationComplete(false);
assignMarginsAndBackgrounds(this);
if (shouldAddArrow()) {
addArrow();
@@ -819,9 +704,6 @@
if (mOnCloseCallback != null) {
mOnCloseCallback.run();
}
- if (mColorExtractors != null) {
- mColorExtractors.forEach(e -> e.setListener(null));
- }
}
/**
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index 8e7a10c..4da588e 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -69,7 +69,6 @@
import com.android.launcher3.views.BaseDragLayer;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
@@ -236,13 +235,6 @@
mPopupItemDragHandler = new LauncherPopupItemDragHandler(launcher, this);
mAccessibilityDelegate = new ShortcutMenuAccessibilityDelegate(launcher);
launcher.getDragController().addDragListener(this);
- addPreDrawForColorExtraction(launcher);
- }
-
- @Override
- protected List<View> getChildrenForColorExtraction() {
- return Arrays.asList(mSystemShortcutContainer, mWidgetContainer, mDeepShortcutContainer,
- mNotificationContainer);
}
private void initializeSystemShortcuts(List<SystemShortcut> shortcuts) {
diff --git a/src/com/android/launcher3/popup/RemoteActionShortcut.java b/src/com/android/launcher3/popup/RemoteActionShortcut.java
index e5e2c35..7c9ab87 100644
--- a/src/com/android/launcher3/popup/RemoteActionShortcut.java
+++ b/src/com/android/launcher3/popup/RemoteActionShortcut.java
@@ -38,6 +38,8 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.model.data.ItemInfo;
+import java.lang.ref.WeakReference;
+
@TargetApi(Build.VERSION_CODES.Q)
public class RemoteActionShortcut extends SystemShortcut<BaseDraggingActivity> {
private static final String TAG = "RemoteActionShortcut";
@@ -79,6 +81,7 @@
mTarget.getStatsLogManager().logger().withItemInfo(mItemInfo)
.log(LAUNCHER_SYSTEM_SHORTCUT_PAUSE_TAP);
+ final WeakReference<BaseDraggingActivity> weakTarget = new WeakReference<>(mTarget);
final String actionIdentity = mAction.getTitle() + ", "
+ mItemInfo.getTargetComponent().getPackageName();
try {
@@ -91,10 +94,13 @@
mItemInfo.getTargetComponent().getPackageName()),
(pendingIntent, intent, resultCode, resultData, resultExtras) -> {
if (DEBUG) Log.d(TAG, "Action is complete: " + actionIdentity);
+ final BaseDraggingActivity target = weakTarget.get();
if (resultData != null && !resultData.isEmpty()) {
Log.e(TAG, "Remote action returned result: " + actionIdentity
+ " : " + resultData);
- Toast.makeText(mTarget, resultData, Toast.LENGTH_SHORT).show();
+ if (target != null) {
+ Toast.makeText(target, resultData, Toast.LENGTH_SHORT).show();
+ }
}
},
MAIN_EXECUTOR.getHandler());
diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java
index 48b3acf..2a452be 100644
--- a/src/com/android/launcher3/provider/RestoreDbTask.java
+++ b/src/com/android/launcher3/provider/RestoreDbTask.java
@@ -17,13 +17,14 @@
package com.android.launcher3.provider;
import static com.android.launcher3.InvariantDeviceProfile.TYPE_MULTI_DISPLAY;
-import static com.android.launcher3.InvariantDeviceProfile.TYPE_PHONE;
+import static com.android.launcher3.LauncherPrefs.APP_WIDGET_IDS;
+import static com.android.launcher3.LauncherPrefs.OLD_APP_WIDGET_IDS;
+import static com.android.launcher3.LauncherPrefs.RESTORE_DEVICE;
import static com.android.launcher3.provider.LauncherDbUtils.dropTable;
import android.app.backup.BackupManager;
import android.content.ContentValues;
import android.content.Context;
-import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.UserHandle;
@@ -36,6 +37,7 @@
import com.android.launcher3.AppWidgetsRestoredReceiver;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.LauncherProvider.DatabaseHelper;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.Utilities;
@@ -47,6 +49,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;
@@ -60,13 +63,13 @@
public class RestoreDbTask {
private static final String TAG = "RestoreDbTask";
- private static final String RESTORED_DEVICE_TYPE = "restored_task_pending";
+ public static final String RESTORED_DEVICE_TYPE = "restored_task_pending";
private static final String INFO_COLUMN_NAME = "name";
private static final String INFO_COLUMN_DEFAULT_VALUE = "dflt_value";
- private static final String APPWIDGET_OLD_IDS = "appwidget_old_ids";
- private static final String APPWIDGET_IDS = "appwidget_ids";
+ public static final String APPWIDGET_OLD_IDS = "appwidget_old_ids";
+ public static final String APPWIDGET_IDS = "appwidget_ids";
/**
* Tries to restore the backup DB if needed
@@ -85,7 +88,7 @@
// Set is pending to false irrespective of the result, so that it doesn't get
// executed again.
- Utilities.getPrefs(context).edit().remove(RESTORED_DEVICE_TYPE).commit();
+ LauncherPrefs.get(context).removeSync(RESTORE_DEVICE);
idp.reinitializeAfterRestore(context);
}
@@ -238,8 +241,7 @@
}
// If restored from a single display backup, remove gaps between screenIds
- if (Utilities.getPrefs(context).getInt(RESTORED_DEVICE_TYPE, TYPE_PHONE)
- != TYPE_MULTI_DISPLAY) {
+ if (LauncherPrefs.get(context).get(RESTORE_DEVICE) != TYPE_MULTI_DISPLAY) {
removeScreenIdGaps(db);
}
@@ -337,7 +339,7 @@
}
public static boolean isPending(Context context) {
- return Utilities.getPrefs(context).contains(RESTORED_DEVICE_TYPE);
+ return LauncherPrefs.get(context).has(RESTORE_DEVICE);
}
/**
@@ -345,31 +347,31 @@
*/
public static void setPending(Context context) {
FileLog.d(TAG, "Restore data received through full backup ");
- Utilities.getPrefs(context).edit()
- .putInt(RESTORED_DEVICE_TYPE, new DeviceGridState(context).getDeviceType())
- .commit();
+ LauncherPrefs.get(context)
+ .putSync(RESTORE_DEVICE.to(new DeviceGridState(context).getDeviceType()));
}
private void restoreAppWidgetIdsIfExists(Context context) {
- SharedPreferences prefs = Utilities.getPrefs(context);
- if (prefs.contains(APPWIDGET_OLD_IDS) && prefs.contains(APPWIDGET_IDS)) {
+ LauncherPrefs lp = LauncherPrefs.get(context);
+ if (lp.has(APP_WIDGET_IDS, OLD_APP_WIDGET_IDS)) {
+ LauncherWidgetHolder holder = LauncherWidgetHolder.newInstance(context);
AppWidgetsRestoredReceiver.restoreAppWidgetIds(context,
- IntArray.fromConcatString(prefs.getString(APPWIDGET_OLD_IDS, "")).toArray(),
- IntArray.fromConcatString(prefs.getString(APPWIDGET_IDS, "")).toArray());
+ IntArray.fromConcatString(lp.get(OLD_APP_WIDGET_IDS)).toArray(),
+ IntArray.fromConcatString(lp.get(APP_WIDGET_IDS)).toArray(),
+ holder);
+ holder.destroy();
} else {
FileLog.d(TAG, "No app widget ids to restore.");
}
- prefs.edit().remove(APPWIDGET_OLD_IDS)
- .remove(APPWIDGET_IDS).apply();
+ lp.remove(APP_WIDGET_IDS, OLD_APP_WIDGET_IDS);
}
public static void setRestoredAppWidgetIds(Context context, @NonNull int[] oldIds,
@NonNull int[] newIds) {
- Utilities.getPrefs(context).edit()
- .putString(APPWIDGET_OLD_IDS, IntArray.wrap(oldIds).toConcatString())
- .putString(APPWIDGET_IDS, IntArray.wrap(newIds).toConcatString())
- .commit();
+ LauncherPrefs.get(context).putSync(
+ OLD_APP_WIDGET_IDS.to(IntArray.wrap(oldIds).toConcatString()),
+ APP_WIDGET_IDS.to(IntArray.wrap(newIds).toConcatString()));
}
}
diff --git a/src/com/android/launcher3/qsb/QsbContainerView.java b/src/com/android/launcher3/qsb/QsbContainerView.java
index 23ee251..f295204 100644
--- a/src/com/android/launcher3/qsb/QsbContainerView.java
+++ b/src/com/android/launcher3/qsb/QsbContainerView.java
@@ -43,8 +43,8 @@
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.graphics.FragmentWithPreview;
import com.android.launcher3.widget.util.WidgetSizes;
@@ -200,7 +200,7 @@
Context context = getContext();
AppWidgetManager widgetManager = AppWidgetManager.getInstance(context);
- int widgetId = Utilities.getPrefs(context).getInt(mKeyWidgetId, -1);
+ int widgetId = LauncherPrefs.getPrefs(context).getInt(mKeyWidgetId, -1);
AppWidgetProviderInfo widgetInfo = widgetManager.getAppWidgetInfo(widgetId);
boolean isWidgetBound = (widgetInfo != null) &&
widgetInfo.provider.equals(mWidgetInfo.provider);
@@ -244,7 +244,7 @@
}
private void saveWidgetId(int widgetId) {
- Utilities.getPrefs(getContext()).edit().putInt(mKeyWidgetId, widgetId).apply();
+ LauncherPrefs.getPrefs(getContext()).edit().putInt(mKeyWidgetId, widgetId).apply();
}
@Override
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/search/StringMatcherUtility.java b/src/com/android/launcher3/search/StringMatcherUtility.java
index acab52b..c66f3a1 100644
--- a/src/com/android/launcher3/search/StringMatcherUtility.java
+++ b/src/com/android/launcher3/search/StringMatcherUtility.java
@@ -24,8 +24,8 @@
public class StringMatcherUtility {
/**
- * Returns {@code true} is {@code query} is a prefix substring of a complete word/phrase in
- * {@code target}.
+ * Returns {@code true} if {@code query} is a prefix of a substring in {@code target}. How to
+ * break target to valid substring is defined in the given {@code matcher}.
*/
public static boolean matches(String query, String target, StringMatcher matcher) {
int queryLength = query.length();
@@ -50,7 +50,7 @@
thisType = nextType;
nextType = i < (targetLength - 1)
? Character.getType(target.codePointAt(i + 1)) : Character.UNASSIGNED;
- if (isBreak(thisType, lastType, nextType)
+ if (matcher.isBreak(thisType, lastType, nextType)
&& matcher.matches(query, target.substring(i, i + queryLength))) {
return true;
}
@@ -59,52 +59,6 @@
}
/**
- * Returns true if the current point should be a break point. Following cases
- * are considered as break points:
- * 1) Any non space character after a space character
- * 2) Any digit after a non-digit character
- * 3) Any capital character after a digit or small character
- * 4) Any capital character before a small character
- */
- private static boolean isBreak(int thisType, int prevType, int nextType) {
- switch (prevType) {
- case Character.UNASSIGNED:
- case Character.SPACE_SEPARATOR:
- case Character.LINE_SEPARATOR:
- case Character.PARAGRAPH_SEPARATOR:
- return true;
- }
- switch (thisType) {
- case Character.UPPERCASE_LETTER:
- if (nextType == Character.UPPERCASE_LETTER) {
- return true;
- }
- // Follow through
- case Character.TITLECASE_LETTER:
- // Break point if previous was not a upper case
- return prevType != Character.UPPERCASE_LETTER;
- case Character.LOWERCASE_LETTER:
- // Break point if previous was not a letter.
- return prevType > Character.OTHER_LETTER || prevType <= Character.UNASSIGNED;
- case Character.DECIMAL_DIGIT_NUMBER:
- case Character.LETTER_NUMBER:
- case Character.OTHER_NUMBER:
- // Break point if previous was not a number
- return !(prevType == Character.DECIMAL_DIGIT_NUMBER
- || prevType == Character.LETTER_NUMBER
- || prevType == Character.OTHER_NUMBER);
- case Character.MATH_SYMBOL:
- case Character.CURRENCY_SYMBOL:
- case Character.OTHER_PUNCTUATION:
- case Character.DASH_PUNCTUATION:
- // Always a break point for a symbol
- return true;
- default:
- return false;
- }
- }
-
- /**
* Performs locale sensitive string comparison using {@link Collator}.
*/
public static class StringMatcher {
@@ -142,6 +96,75 @@
public static StringMatcher getInstance() {
return new StringMatcher();
}
+
+ /**
+ * Returns true if the current point should be a break point.
+ *
+ * Following cases are considered as break points:
+ * 1) Any non space character after a space character
+ * 2) Any digit after a non-digit character
+ * 3) Any capital character after a digit or small character
+ * 4) Any capital character before a small character
+ *
+ * E.g., "YouTube" matches the input "you" and "tube", but not "out".
+ */
+ protected boolean isBreak(int thisType, int prevType, int nextType) {
+ switch (prevType) {
+ case Character.UNASSIGNED:
+ case Character.SPACE_SEPARATOR:
+ case Character.LINE_SEPARATOR:
+ case Character.PARAGRAPH_SEPARATOR:
+ return true;
+ }
+ switch (thisType) {
+ case Character.UPPERCASE_LETTER:
+ if (nextType == Character.UPPERCASE_LETTER) {
+ return true;
+ }
+ // Follow through
+ case Character.TITLECASE_LETTER:
+ // Break point if previous was not a upper case
+ return prevType != Character.UPPERCASE_LETTER;
+ case Character.LOWERCASE_LETTER:
+ // Break point if previous was not a letter.
+ return prevType > Character.OTHER_LETTER || prevType <= Character.UNASSIGNED;
+ case Character.DECIMAL_DIGIT_NUMBER:
+ case Character.LETTER_NUMBER:
+ case Character.OTHER_NUMBER:
+ // Break point if previous was not a number
+ return !(prevType == Character.DECIMAL_DIGIT_NUMBER
+ || prevType == Character.LETTER_NUMBER
+ || prevType == Character.OTHER_NUMBER);
+ case Character.MATH_SYMBOL:
+ case Character.CURRENCY_SYMBOL:
+ case Character.OTHER_PUNCTUATION:
+ case Character.DASH_PUNCTUATION:
+ // Always a break point for a symbol
+ return true;
+ default:
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Subclass of {@code StringMatcher} using simple space break for prefix matching.
+ * E.g., "YouTube" matches the input "you". "Play Store" matches the input "play".
+ */
+ public static class StringMatcherSpace extends StringMatcher {
+
+ public static StringMatcherSpace getInstance() {
+ return new StringMatcherSpace();
+ }
+
+ /**
+ * The first character or any character after a space is considered as a break point.
+ * Returns true if the current point should be a break point.
+ */
+ @Override
+ protected boolean isBreak(int thisType, int prevType, int nextType) {
+ return prevType == Character.UNASSIGNED || prevType == Character.SPACE_SEPARATOR;
+ }
}
/**
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
index 846eac5..a2353d8 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
@@ -35,9 +35,9 @@
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel;
+import com.android.launcher3.LauncherPrefs;
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;
@@ -51,11 +51,11 @@
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.popup.PopupDataProvider;
+import com.android.launcher3.touch.ItemClickHandler.ItemClickProxy;
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.util.UiThreadHelper;
import com.android.launcher3.views.BaseDragLayer;
import java.util.HashMap;
@@ -88,7 +88,7 @@
super.onCreate(savedInstanceState);
mModel = LauncherAppState.getInstance(this).getModel();
mDragController = new SecondaryDragController(this);
- mOnboardingPrefs = new OnboardingPrefs<>(this, Utilities.getPrefs(this));
+ mOnboardingPrefs = new OnboardingPrefs<>(this, LauncherPrefs.getPrefs(this));
mSecondaryDisplayPredictions = SecondaryDisplayPredictions.newInstance(this);
if (getWindow().getDecorView().isAttachedToWindow()) {
initUi();
@@ -134,16 +134,6 @@
mModel.addCallbacksAndLoad(this);
}
- /**
- * hides keyboard
- */
- public void hideKeyboard() {
- final View v = getWindow().peekDecorView();
- if (v != null && v.getWindowToken() != null) {
- UiThreadHelper.hideKeyboardAsync(this, v.getWindowToken());
- }
- }
-
@Override
protected void onPause() {
super.onPause();
@@ -343,7 +333,9 @@
if (v.getWindowToken() == null) return;
Object tag = v.getTag();
- if (tag instanceof ItemInfo) {
+ if (tag instanceof ItemClickProxy) {
+ ((ItemClickProxy) tag).onItemClicked(v);
+ } else if (tag instanceof ItemInfo) {
ItemInfo item = (ItemInfo) tag;
Intent intent;
if (item instanceof ItemInfoWithIcon
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java b/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
index 5eac01e..c8455b8 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
@@ -38,12 +38,14 @@
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
@@ -194,15 +196,21 @@
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)));
+ Collections.emptyList(), systemShortcuts);
container.requestFocus();
if (!FeatureFlags.SECONDARY_DRAG_N_DROP_TO_PIN.get() || !mActivity.isAppDrawerShown()) {
diff --git a/src/com/android/launcher3/settings/DeveloperOptionsFragment.java b/src/com/android/launcher3/settings/DeveloperOptionsFragment.java
index 6057586..40fc16e 100644
--- a/src/com/android/launcher3/settings/DeveloperOptionsFragment.java
+++ b/src/com/android/launcher3/settings/DeveloperOptionsFragment.java
@@ -15,6 +15,9 @@
*/
package com.android.launcher3.settings;
+import static android.content.Intent.ACTION_PACKAGE_ADDED;
+import static android.content.Intent.ACTION_PACKAGE_CHANGED;
+import static android.content.Intent.ACTION_PACKAGE_REMOVED;
import static android.content.pm.PackageManager.GET_RESOLVED_FILTER;
import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
import static android.view.View.GONE;
@@ -25,11 +28,9 @@
import static com.android.launcher3.uioverrides.plugins.PluginManagerWrapper.pluginEnabledKey;
import android.annotation.TargetApi;
-import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
@@ -59,13 +60,14 @@
import androidx.preference.PreferenceViewHolder;
import androidx.preference.SwitchPreference;
+import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.R;
-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;
+import com.android.launcher3.util.SimpleBroadcastReceiver;
import java.util.ArrayList;
import java.util.List;
@@ -83,12 +85,8 @@
private static final String ACTION_PLUGIN_SETTINGS = "com.android.systemui.action.PLUGIN_SETTINGS";
private static final String PLUGIN_PERMISSION = "com.android.systemui.permission.PLUGIN";
- private final BroadcastReceiver mPluginReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- loadPluginPrefs();
- }
- };
+ private final SimpleBroadcastReceiver mPluginReceiver =
+ new SimpleBroadcastReceiver(i -> loadPluginPrefs());
private PreferenceScreen mPreferenceScreen;
@@ -97,13 +95,9 @@
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
- IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
- filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
- filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
- filter.addDataScheme("package");
- getContext().registerReceiver(mPluginReceiver, filter);
- getContext().registerReceiver(mPluginReceiver,
- new IntentFilter(Intent.ACTION_USER_UNLOCKED));
+ mPluginReceiver.registerPkgActions(getContext(), null,
+ ACTION_PACKAGE_ADDED, ACTION_PACKAGE_CHANGED, ACTION_PACKAGE_REMOVED);
+ mPluginReceiver.register(getContext(), Intent.ACTION_USER_UNLOCKED);
mPreferenceScreen = getPreferenceManager().createPreferenceScreen(getContext());
setPreferenceScreen(mPreferenceScreen);
@@ -185,7 +179,7 @@
@Override
public void onDestroy() {
super.onDestroy();
- getContext().unregisterReceiver(mPluginReceiver);
+ mPluginReceiver.unregisterReceiverSafely(getContext());
}
private PreferenceCategory newCategory(String title) {
@@ -392,7 +386,8 @@
onboardingPref.setTitle(title);
onboardingPref.setSummary("Tap to reset");
onboardingPref.setOnPreferenceClickListener(preference -> {
- SharedPreferences.Editor sharedPrefsEdit = Utilities.getPrefs(getContext()).edit();
+ SharedPreferences.Editor sharedPrefsEdit = LauncherPrefs.getPrefs(getContext())
+ .edit();
for (String key : keys) {
sharedPrefsEdit.remove(key);
}
diff --git a/src/com/android/launcher3/settings/SettingsActivity.java b/src/com/android/launcher3/settings/SettingsActivity.java
index 49d27b7..d2f82c2 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;
@@ -45,12 +46,14 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherFiles;
+import com.android.launcher3.LauncherPrefs;
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.states.RotationHelper;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
+import com.android.launcher3.util.DisplayController;
import java.util.Collections;
import java.util.List;
@@ -90,7 +93,8 @@
WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
Intent intent = getIntent();
- if (intent.hasExtra(EXTRA_FRAGMENT) || intent.hasExtra(EXTRA_FRAGMENT_ARGS)) {
+ if (intent.hasExtra(EXTRA_FRAGMENT) || intent.hasExtra(EXTRA_FRAGMENT_ARGS)
+ || intent.hasExtra(EXTRA_FRAGMENT_ARG_KEY)) {
getActionBar().setDisplayHomeAsUpEnabled(true);
}
@@ -112,7 +116,8 @@
// Display the fragment as the main content.
fm.beginTransaction().replace(R.id.content_frame, f).commit();
}
- Utilities.getPrefs(getApplicationContext()).registerOnSharedPreferenceChangeListener(this);
+ LauncherPrefs.getPrefs(getApplicationContext())
+ .registerOnSharedPreferenceChangeListener(this);
}
/**
@@ -207,7 +212,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);
}
}
@@ -260,15 +269,14 @@
return !WidgetsModel.GO_DISABLE_NOTIFICATION_DOTS;
case ALLOW_ROTATION_PREFERENCE_KEY:
- DeviceProfile deviceProfile = InvariantDeviceProfile.INSTANCE.get(
- getContext()).getDeviceProfile(getContext());
- if (deviceProfile.isTablet) {
+ DisplayController.Info info =
+ DisplayController.INSTANCE.get(getContext()).getInfo();
+ if (info.isTablet(info.realBounds)) {
// Launcher supports rotation by default. No need to show this setting.
return false;
}
// Initialize the UI once
- preference.setDefaultValue(
- RotationHelper.getAllowRotationDefaultValue(deviceProfile));
+ preference.setDefaultValue(RotationHelper.getAllowRotationDefaultValue(info));
return true;
case FLAGS_PREFERENCE_KEY:
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..2390425 100644
--- a/src/com/android/launcher3/statemanager/BaseState.java
+++ b/src/com/android/launcher3/statemanager/BaseState.java
@@ -18,7 +18,7 @@
import android.content.Context;
import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.DeviceProfile.DeviceProfileListenable;
+import com.android.launcher3.views.ActivityContext;
/**
* Interface representing a state of a StatefulActivity
@@ -37,7 +37,7 @@
/**
* @return How long the animation to this state should take (or from this state to NORMAL).
*/
- <DEVICE_PROFILE_CONTEXT extends Context & DeviceProfileListenable>
+ <DEVICE_PROFILE_CONTEXT extends Context & ActivityContext>
int getTransitionDuration(DEVICE_PROFILE_CONTEXT context, boolean isToState);
/**
@@ -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..34ac8c2 100644
--- a/src/com/android/launcher3/statemanager/StateManager.java
+++ b/src/com/android/launcher3/statemanager/StateManager.java
@@ -28,6 +28,8 @@
import android.os.Handler;
import android.os.Looper;
+import androidx.annotation.FloatRange;
+
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
@@ -195,6 +197,21 @@
}
}
+ /** Handles backProgress in predictive back gesture by passing it to state handlers. */
+ public void onBackProgressed(
+ STATE_TYPE toState, @FloatRange(from = 0.0, to = 1.0) float backProgress) {
+ for (StateHandler handler : getStateHandlers()) {
+ handler.onBackProgressed(toState, backProgress);
+ }
+ }
+
+ /** Handles back cancelled event in predictive back gesture by passing it to state handlers. */
+ public void onBackCancelled(STATE_TYPE toState) {
+ for (StateHandler handler : getStateHandlers()) {
+ handler.onBackCancelled(toState);
+ }
+ }
+
private void goToState(
STATE_TYPE state, boolean animated, long delay, AnimatorListener listener) {
animated &= areAnimatorsEnabled();
@@ -376,12 +393,16 @@
}
public void moveToRestState() {
+ moveToRestState(shouldAnimateStateChange());
+ }
+
+ public void moveToRestState(boolean isAnimated) {
if (mConfig.currentAnimation != null && mConfig.userControlled) {
// The user is doing something. Lets not mess it up
return;
}
if (mState.shouldDisableRestore()) {
- goToState(getRestState());
+ goToState(getRestState(), isAnimated);
// Reset history
mLastStableState = mBaseState;
}
@@ -582,6 +603,13 @@
*/
void setStateWithAnimation(
STATE_TYPE toState, StateAnimationConfig config, PendingAnimation animation);
+
+ /** Handles backProgress in predictive back gesture for target state. */
+ default void onBackProgressed(
+ STATE_TYPE toState, @FloatRange(from = 0.0, to = 1.0) float backProgress) {};
+
+ /** Handles back cancelled event in predictive back gesture for target state. */
+ default void onBackCancelled(STATE_TYPE toState) {};
}
public interface StateListener<STATE_TYPE> {
diff --git a/src/com/android/launcher3/statemanager/StatefulActivity.java b/src/com/android/launcher3/statemanager/StatefulActivity.java
index 2158dea..520f33c 100644
--- a/src/com/android/launcher3/statemanager/StatefulActivity.java
+++ b/src/com/android/launcher3/statemanager/StatefulActivity.java
@@ -15,9 +15,14 @@
*/
package com.android.launcher3.statemanager;
+import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
+import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
+
import static com.android.launcher3.LauncherState.FLAG_CLOSE_POPUPS;
import static com.android.launcher3.LauncherState.FLAG_NON_INTERACTIVE;
+import android.content.res.Configuration;
+import android.os.Bundle;
import android.os.Handler;
import android.view.LayoutInflater;
import android.view.View;
@@ -30,6 +35,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory;
import com.android.launcher3.statemanager.StateManager.StateHandler;
+import com.android.launcher3.util.window.WindowManagerProxy;
import com.android.launcher3.views.BaseDragLayer;
import java.util.List;
@@ -47,6 +53,17 @@
private LauncherRootView mRootView;
+ protected Configuration mOldConfig;
+ private int mOldRotation;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mOldConfig = new Configuration(getResources().getConfiguration());
+ mOldRotation = WindowManagerProxy.INSTANCE.get(this).getRotation(this);
+ }
+
/**
* Create handlers to control the property changes for this activity
*/
@@ -186,4 +203,38 @@
public void runOnBindToTouchInteractionService(Runnable r) {
r.run();
}
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ handleConfigurationChanged(newConfig);
+ super.onConfigurationChanged(newConfig);
+ }
+
+ /**
+ * Handles configuration change when system calls {@link #onConfigurationChanged}, or on other
+ * situations that configuration might change.
+ */
+ public void handleConfigurationChanged(Configuration newConfig) {
+ int diff = newConfig.diff(mOldConfig);
+ int rotation = WindowManagerProxy.INSTANCE.get(this).getRotation(this);
+ if ((diff & (CONFIG_ORIENTATION | CONFIG_SCREEN_SIZE)) != 0
+ || rotation != mOldRotation) {
+ onHandleConfigurationChanged();
+ }
+
+ mOldConfig.setTo(newConfig);
+ mOldRotation = rotation;
+ }
+
+ /**
+ * Logic for when device configuration changes (rotation, screen size change, multi-window,
+ * 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 38b62d4..e5b4eba 100644
--- a/src/com/android/launcher3/states/RotationHelper.java
+++ b/src/com/android/launcher3/states/RotationHelper.java
@@ -21,34 +21,39 @@
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.UiThreadHelper;
+import com.android.launcher3.LauncherPrefs;
+import com.android.launcher3.util.DisplayController;
/**
* Utility class to manage launcher rotation
*/
public class RotationHelper implements OnSharedPreferenceChangeListener,
- DeviceProfile.OnDeviceProfileChangeListener {
-
- private static final String TAG = "RotationHelper";
+ DisplayController.DisplayInfoChangeListener {
public static final String ALLOW_ROTATION_PREFERENCE_KEY = "pref_allowRotation";
/**
* Returns the default value of {@link #ALLOW_ROTATION_PREFERENCE_KEY} preference.
*/
- public static boolean getAllowRotationDefaultValue(DeviceProfile deviceProfile) {
+ public static boolean getAllowRotationDefaultValue(DisplayController.Info info) {
// If the device's pixel density was scaled (usually via settings for A11y), use the
// original dimensions to determine if rotation is allowed of not.
- float originalSmallestWidth = dpiFromPx(
- Math.min(deviceProfile.widthPx, deviceProfile.heightPx), DENSITY_DEVICE_STABLE);
+ float originalSmallestWidth = dpiFromPx(Math.min(info.currentSize.x, info.currentSize.y),
+ DENSITY_DEVICE_STABLE);
return originalSmallestWidth >= MIN_TABLET_WIDTH;
}
@@ -56,8 +61,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;
@@ -87,18 +94,21 @@
public RotationHelper(BaseActivity activity) {
mActivity = activity;
+ mRequestOrientationHandler =
+ new Handler(UI_HELPER_EXECUTOR.getLooper(), this::setOrientationAsync);
}
- private void setIgnoreAutoRotateSettings(boolean ignoreAutoRotateSettings) {
+ private void setIgnoreAutoRotateSettings(boolean ignoreAutoRotateSettings,
+ DisplayController.Info info) {
// On large devices we do not handle auto-rotate differently.
mIgnoreAutoRotateSettings = ignoreAutoRotateSettings;
if (!mIgnoreAutoRotateSettings) {
if (mSharedPrefs == null) {
- mSharedPrefs = Utilities.getPrefs(mActivity);
+ mSharedPrefs = LauncherPrefs.getPrefs(mActivity);
mSharedPrefs.registerOnSharedPreferenceChangeListener(this);
}
mHomeRotationEnabled = mSharedPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY,
- getAllowRotationDefaultValue(mActivity.getDeviceProfile()));
+ getAllowRotationDefaultValue(info));
} else {
if (mSharedPrefs != null) {
mSharedPrefs.unregisterOnSharedPreferenceChangeListener(this);
@@ -112,17 +122,17 @@
if (mDestroyed || mIgnoreAutoRotateSettings) return;
boolean wasRotationEnabled = mHomeRotationEnabled;
mHomeRotationEnabled = mSharedPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY,
- getAllowRotationDefaultValue(mActivity.getDeviceProfile()));
+ getAllowRotationDefaultValue(mActivity.getDeviceProfile().getDisplayInfo()));
if (mHomeRotationEnabled != wasRotationEnabled) {
notifyChange();
}
}
@Override
- public void onDeviceProfileChanged(DeviceProfile dp) {
- boolean ignoreAutoRotateSettings = dp.isTablet;
+ public void onDisplayInfoChanged(Context context, DisplayController.Info info, int flags) {
+ boolean ignoreAutoRotateSettings = info.isTablet(info.realBounds);
if (mIgnoreAutoRotateSettings != ignoreAutoRotateSettings) {
- setIgnoreAutoRotateSettings(ignoreAutoRotateSettings);
+ setIgnoreAutoRotateSettings(ignoreAutoRotateSettings, info);
notifyChange();
}
}
@@ -157,8 +167,10 @@
public void initialize() {
if (!mInitialized) {
mInitialized = true;
- setIgnoreAutoRotateSettings(mActivity.getDeviceProfile().isTablet);
- mActivity.addOnDeviceProfileChangeListener(this);
+ DisplayController displayController = DisplayController.INSTANCE.get(mActivity);
+ DisplayController.Info info = displayController.getInfo();
+ setIgnoreAutoRotateSettings(info.isTablet(info.realBounds), info);
+ displayController.addChangeListener(this);
notifyChange();
}
}
@@ -166,7 +178,7 @@
public void destroy() {
if (!mDestroyed) {
mDestroyed = true;
- mActivity.removeOnDeviceProfileChangeListener(this);
+ DisplayController.INSTANCE.get(mActivity).removeChangeListener(this);
mActivity = null;
if (mSharedPrefs != null) {
mSharedPrefs.unregisterOnSharedPreferenceChangeListener(this);
@@ -198,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 a205ab5..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) {
@@ -52,17 +57,20 @@
}
float shrunkTop = grid.getCellLayoutSpringLoadShrunkTop();
- float scale = grid.getWorkspaceSpringLoadScale();
+ float scale = grid.getWorkspaceSpringLoadScale(launcher);
float halfHeight = ws.getHeight() / 2;
float myCenter = ws.getTop() + halfHeight;
float cellTopFromCenter = halfHeight - ws.getChildAt(0).getTop();
float actualCellTop = myCenter - cellTopFromCenter * scale;
- return new ScaleAndTranslation(scale, 0, (shrunkTop - actualCellTop) / scale);
+ return new ScaleAndTranslation(scale, 0, shrunkTop - actualCellTop);
}
@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/states/StateAnimationConfig.java b/src/com/android/launcher3/states/StateAnimationConfig.java
index f99519d..54735f0 100644
--- a/src/com/android/launcher3/states/StateAnimationConfig.java
+++ b/src/com/android/launcher3/states/StateAnimationConfig.java
@@ -64,6 +64,8 @@
ANIM_DEPTH,
ANIM_OVERVIEW_ACTIONS_FADE,
ANIM_WORKSPACE_PAGE_TRANSLATE_X,
+ ANIM_OVERVIEW_SPLIT_SELECT_FLOATING_TASK_TRANSLATE_OFFSCREEN,
+ ANIM_OVERVIEW_SPLIT_SELECT_INSTRUCTIONS_FADE
})
@Retention(RetentionPolicy.SOURCE)
public @interface AnimType {}
@@ -84,8 +86,10 @@
public static final int ANIM_DEPTH = 13;
public static final int ANIM_OVERVIEW_ACTIONS_FADE = 14;
public static final int ANIM_WORKSPACE_PAGE_TRANSLATE_X = 15;
+ public static final int ANIM_OVERVIEW_SPLIT_SELECT_FLOATING_TASK_TRANSLATE_OFFSCREEN = 17;
+ public static final int ANIM_OVERVIEW_SPLIT_SELECT_INSTRUCTIONS_FADE = 18;
- private static final int ANIM_TYPES_COUNT = 17;
+ private static final int ANIM_TYPES_COUNT = 19;
protected final Interpolator[] mInterpolators = new Interpolator[ANIM_TYPES_COUNT];
diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java
index d3c9bc9..9a34478 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: {
@@ -199,6 +201,13 @@
});
}
+ case TestProtocol.REQUEST_WORKSPACE_COLUMNS_ROWS: {
+ return getLauncherUIProperty(Bundle::putParcelable, launcher -> new Point(
+ InvariantDeviceProfile.INSTANCE.get(mContext).numColumns,
+ InvariantDeviceProfile.INSTANCE.get(mContext).numRows)
+ );
+ }
+
case TestProtocol.REQUEST_HOTSEAT_CELL_CENTER: {
final HotseatCellCenterRequest request = extra.getParcelable(
TestProtocol.TEST_INFO_REQUEST_FIELD);
@@ -214,11 +223,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 +274,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 af462cc..d0ae258 100644
--- a/src/com/android/launcher3/testing/shared/ResourceUtils.java
+++ b/src/com/android/launcher3/testing/shared/ResourceUtils.java
@@ -21,6 +21,7 @@
import android.util.TypedValue;
public class ResourceUtils {
+ private static final float EPSILON = 0.0001f;
public static final int DEFAULT_NAVBAR_VALUE = 48;
public static final int INVALID_RESOURCE_HANDLE = -1;
public static final String NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE = "navigation_bar_width";
@@ -35,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);
}
@@ -71,7 +74,25 @@
}
public static int pxFromDp(float size, DisplayMetrics metrics, float scale) {
- return size < 0 ? INVALID_RESOURCE_HANDLE : Math.round(scale
- * TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, size, metrics));
+ float value = scale * TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, size, metrics);
+ return size < 0 ? INVALID_RESOURCE_HANDLE : roundPxValueFromFloat(value);
+ }
+
+ /**
+ * Rounds a pixel value, taking into account floating point errors.
+ *
+ * <p>If a dp (or sp) value typically returns a half pixel, such as 20dp at a 2.625 density
+ * returning 52.5px, there is a small chance that due to floating-point errors, the value will
+ * be stored as 52.499999. As we round to the nearest pixel, this could cause a 1px difference
+ * in final values, which we correct for in this method.
+ */
+ public static int roundPxValueFromFloat(float value) {
+ float fraction = (float) (value - Math.floor(value));
+ if (Math.abs(0.5f - fraction) < EPSILON) {
+ // Note: we add for negative values as well, as Math.round brings -.5 to the next
+ // "highest" value, e.g. Math.round(-2.5) == -2 [i.e. (int)Math.floor(a + 0.5d)]
+ value += EPSILON;
+ }
+ return Math.round(value);
}
}
diff --git a/src/com/android/launcher3/testing/shared/TestProtocol.java b/src/com/android/launcher3/testing/shared/TestProtocol.java
index d872476..9b2ce9a 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";
@@ -101,6 +106,7 @@
public static final String REQUEST_STOP_EVENT_LOGGING = "stop-event-logging";
public static final String REQUEST_CLEAR_DATA = "clear-data";
public static final String REQUEST_USE_TEST_WORKSPACE_LAYOUT = "use-test-workspace-layout";
+ public static final String REQUEST_USE_TEST2_WORKSPACE_LAYOUT = "use-test2-workspace-layout";
public static final String REQUEST_USE_DEFAULT_WORKSPACE_LAYOUT =
"use-default-workspace-layout";
public static final String REQUEST_HOTSEAT_ICON_NAMES = "get-hotseat-icon-names";
@@ -111,9 +117,13 @@
"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";
+ public static final String REQUEST_WORKSPACE_COLUMNS_ROWS = "workspace-columns-rows";
public static final String REQUEST_HOTSEAT_CELL_CENTER = "hotseat-cell-center";
@@ -123,6 +133,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";
@@ -136,8 +148,8 @@
public static final String NO_DROP_TARGET = "b/195031154";
public static final String NULL_INT_SET = "b/200572078";
public static final String MISSING_PROMISE_ICON = "b/202985412";
- public static final String BAD_STATE = "b/223498680";
public static final String TASKBAR_IN_APP_STATE = "b/227657604";
+ 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/testing/shared/WorkspaceCellCenterRequest.java b/src/com/android/launcher3/testing/shared/WorkspaceCellCenterRequest.java
index 80dbef8..e2cd8ea 100644
--- a/src/com/android/launcher3/testing/shared/WorkspaceCellCenterRequest.java
+++ b/src/com/android/launcher3/testing/shared/WorkspaceCellCenterRequest.java
@@ -124,7 +124,7 @@
* Set span Height in cells
*/
public WorkspaceCellCenterRequest.Builder setSpanY(int y) {
- this.mCellY = y;
+ this.mSpanY = y;
return this;
}
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index 09b8228..c499e35 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -211,6 +211,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;
@@ -287,11 +291,18 @@
? mToState : mFromState;
// snap to top or bottom using the release velocity
} else {
- float successTransitionProgress =
- mLauncher.getDeviceProfile().isTablet
- && (mToState == ALL_APPS || mFromState == ALL_APPS)
- ? TABLET_BOTTOM_SHEET_SUCCESS_TRANSITION_PROGRESS
- : SUCCESS_TRANSITION_PROGRESS;
+ float successTransitionProgress = SUCCESS_TRANSITION_PROGRESS;
+ if (mLauncher.getDeviceProfile().isTablet
+ && (mToState == ALL_APPS || mFromState == ALL_APPS)) {
+ successTransitionProgress = TABLET_BOTTOM_SHEET_SUCCESS_TRANSITION_PROGRESS;
+ } else if (!mLauncher.getDeviceProfile().isTablet
+ && mToState == ALL_APPS && mFromState == NORMAL) {
+ successTransitionProgress = AllAppsSwipeController.ALL_APPS_STATE_TRANSITION_MANUAL;
+ } else if (!mLauncher.getDeviceProfile().isTablet
+ && mToState == NORMAL && mFromState == ALL_APPS) {
+ successTransitionProgress =
+ 1 - AllAppsSwipeController.ALL_APPS_STATE_TRANSITION_MANUAL;
+ }
targetState =
(interpolatedProgress > successTransitionProgress) ? mToState : mFromState;
}
diff --git a/src/com/android/launcher3/touch/AllAppsSwipeController.java b/src/com/android/launcher3/touch/AllAppsSwipeController.java
index 37b76fb..bfd0e1b 100644
--- a/src/com/android/launcher3/touch/AllAppsSwipeController.java
+++ b/src/com/android/launcher3/touch/AllAppsSwipeController.java
@@ -17,7 +17,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;
@@ -63,11 +63,14 @@
// ---- Custom interpolators for NORMAL -> ALL_APPS on phones only. ----
- private static final float WORKSPACE_MOTION_START_ATOMIC = 0.1667f;
- private static final float ALL_APPS_STATE_TRANSITION_ATOMIC = 0.305f;
- private static final float ALL_APPS_STATE_TRANSITION_MANUAL = 0.4f;
- private static final float ALL_APPS_FADE_END_ATOMIC = 0.4717f;
+ public static final float ALL_APPS_STATE_TRANSITION_ATOMIC = 0.3333f;
+ public static final float ALL_APPS_STATE_TRANSITION_MANUAL = 0.4f;
+ private static final float ALL_APPS_FADE_END_ATOMIC = 0.8333f;
+ private static final float ALL_APPS_FADE_END_MANUAL = 0.8f;
private static final float ALL_APPS_FULL_DEPTH_PROGRESS = 0.5f;
+ private static final float SCRIM_FADE_START_ATOMIC = 0.2642f;
+ private static final float SCRIM_FADE_START_MANUAL = 0.117f;
+ private static final float WORKSPACE_MOTION_START_ATOMIC = 0.1667f;
private static final Interpolator LINEAR_EARLY_MANUAL =
Interpolators.clampToProgress(LINEAR, 0f, ALL_APPS_STATE_TRANSITION_MANUAL);
@@ -97,27 +100,30 @@
public static final Interpolator HOTSEAT_FADE_ATOMIC = STEP_TRANSITION_ATOMIC;
public static final Interpolator HOTSEAT_FADE_MANUAL = STEP_TRANSITION_MANUAL;
- public static final Interpolator HOTSEAT_SCALE_ATOMIC = STEP_TRANSITION_ATOMIC;
- public static final Interpolator HOTSEAT_SCALE_MANUAL = LINEAR_EARLY_MANUAL;
-
- public static final Interpolator HOTSEAT_TRANSLATE_ATOMIC =
+ public static final Interpolator HOTSEAT_SCALE_ATOMIC =
Interpolators.clampToProgress(
EMPHASIZED_ACCELERATE, WORKSPACE_MOTION_START_ATOMIC,
ALL_APPS_STATE_TRANSITION_ATOMIC);
+ public static final Interpolator HOTSEAT_SCALE_MANUAL = LINEAR_EARLY_MANUAL;
+
+ public static final Interpolator HOTSEAT_TRANSLATE_ATOMIC = STEP_TRANSITION_ATOMIC;
public static final Interpolator HOTSEAT_TRANSLATE_MANUAL = STEP_TRANSITION_MANUAL;
public static final Interpolator SCRIM_FADE_ATOMIC =
Interpolators.clampToProgress(
Interpolators.mapToProgress(LINEAR, 0f, 0.8f),
- WORKSPACE_MOTION_START_ATOMIC, ALL_APPS_STATE_TRANSITION_ATOMIC);
- public static final Interpolator SCRIM_FADE_MANUAL = LINEAR_EARLY_MANUAL;
+ SCRIM_FADE_START_ATOMIC, ALL_APPS_STATE_TRANSITION_ATOMIC);
+ public static final Interpolator SCRIM_FADE_MANUAL =
+ Interpolators.clampToProgress(
+ LINEAR, SCRIM_FADE_START_MANUAL, ALL_APPS_STATE_TRANSITION_MANUAL);
public static final Interpolator ALL_APPS_FADE_ATOMIC =
Interpolators.clampToProgress(
- Interpolators.mapToProgress(DECELERATED_EASE, 0.2f, 1f),
+ Interpolators.mapToProgress(EMPHASIZED_DECELERATE, 0.2f, 1f),
ALL_APPS_STATE_TRANSITION_ATOMIC, ALL_APPS_FADE_END_ATOMIC);
public static final Interpolator ALL_APPS_FADE_MANUAL =
- Interpolators.clampToProgress(LINEAR, ALL_APPS_STATE_TRANSITION_MANUAL, 1f);
+ Interpolators.clampToProgress(
+ LINEAR, ALL_APPS_STATE_TRANSITION_MANUAL, ALL_APPS_FADE_END_MANUAL);
public static final Interpolator ALL_APPS_VERTICAL_PROGRESS_ATOMIC =
Interpolators.clampToProgress(
@@ -199,8 +205,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 +246,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..64951ca 100644
--- a/src/com/android/launcher3/touch/ItemClickHandler.java
+++ b/src/com/android/launcher3/touch/ItemClickHandler.java
@@ -17,24 +17,20 @@
import static com.android.launcher3.Launcher.REQUEST_BIND_PENDING_APPWIDGET;
import static com.android.launcher3.Launcher.REQUEST_RECONFIGURE_APPWIDGET;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_SEARCHINAPP_LAUNCH;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_OPEN;
import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_BY_PUBLISHER;
import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER;
import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_QUIET_USER;
import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_SAFEMODE;
import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import android.app.AlertDialog;
-import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentSender;
import android.content.pm.LauncherApps;
import android.content.pm.PackageInstaller.SessionInfo;
-import android.os.Process;
-import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
@@ -56,7 +52,6 @@
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
-import com.android.launcher3.model.data.SearchActionItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.pm.InstallSessionHelper;
import com.android.launcher3.shortcuts.ShortcutKey;
@@ -71,6 +66,8 @@
import com.android.launcher3.widget.WidgetManagerHelper;
import java.util.Collections;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Consumer;
/**
* Class for handling clicks on workspace and all-apps items
@@ -105,8 +102,8 @@
if (v instanceof PendingAppWidgetHostView) {
onClickPendingWidget((PendingAppWidgetHostView) v, launcher);
}
- } else if (tag instanceof SearchActionItemInfo) {
- onClickSearchAction(launcher, (SearchActionItemInfo) tag);
+ } else if (tag instanceof ItemClickProxy) {
+ ((ItemClickProxy) tag).onItemClicked(v);
}
}
@@ -161,32 +158,18 @@
private static void onClickPendingAppItem(View v, Launcher launcher, String packageName,
boolean downloadStarted) {
- if (downloadStarted) {
- // If the download has started, simply direct to the market app.
- startMarketIntentForPackage(v, launcher, packageName);
- return;
- }
- UserHandle user = v.getTag() instanceof ItemInfo
- ? ((ItemInfo) v.getTag()).user : Process.myUserHandle();
- new AlertDialog.Builder(launcher)
- .setTitle(R.string.abandoned_promises_title)
- .setMessage(R.string.abandoned_promise_explanation)
- .setPositiveButton(R.string.abandoned_search,
- (d, i) -> startMarketIntentForPackage(v, launcher, packageName))
- .setNeutralButton(R.string.abandoned_clean_this,
- (d, i) -> launcher.getWorkspace()
- .persistRemoveItemsByMatcher(ItemInfoMatcher.ofPackages(
- Collections.singleton(packageName), user),
- "user explicitly removes the promise app icon"))
- .create().show();
- }
-
- private static void startMarketIntentForPackage(View v, Launcher launcher, String packageName) {
ItemInfo item = (ItemInfo) v.getTag();
+ CompletableFuture<SessionInfo> siFuture;
if (Utilities.ATLEAST_Q) {
- SessionInfo sessionInfo = InstallSessionHelper.INSTANCE.get(launcher)
- .getActiveSessionInfo(item.user, packageName);
- if (sessionInfo != null) {
+ siFuture = CompletableFuture.supplyAsync(() ->
+ InstallSessionHelper.INSTANCE.get(launcher)
+ .getActiveSessionInfo(item.user, packageName),
+ UI_HELPER_EXECUTOR);
+ } else {
+ siFuture = CompletableFuture.completedFuture(null);
+ }
+ Consumer<SessionInfo> marketLaunchAction = sessionInfo -> {
+ if (sessionInfo != null && Utilities.ATLEAST_Q) {
LauncherApps launcherApps = launcher.getSystemService(LauncherApps.class);
try {
launcherApps.startPackageInstallerSessionDetailsActivity(sessionInfo, null,
@@ -196,11 +179,27 @@
Log.e(TAG, "Unable to launch market intent for package=" + packageName, e);
}
}
- }
+ // Fallback to using custom market intent.
+ Intent intent = new PackageManagerHelper(launcher).getMarketIntent(packageName);
+ launcher.startActivitySafely(v, intent, item);
+ };
- // Fallback to using custom market intent.
- Intent intent = new PackageManagerHelper(launcher).getMarketIntent(packageName);
- launcher.startActivitySafely(v, intent, item);
+ if (downloadStarted) {
+ // If the download has started, simply direct to the market app.
+ siFuture.thenAcceptAsync(marketLaunchAction, MAIN_EXECUTOR);
+ return;
+ }
+ new AlertDialog.Builder(launcher)
+ .setTitle(R.string.abandoned_promises_title)
+ .setMessage(R.string.abandoned_promise_explanation)
+ .setPositiveButton(R.string.abandoned_search,
+ (d, i) -> siFuture.thenAcceptAsync(marketLaunchAction, MAIN_EXECUTOR))
+ .setNeutralButton(R.string.abandoned_clean_this,
+ (d, i) -> launcher.getWorkspace()
+ .persistRemoveItemsByMatcher(ItemInfoMatcher.ofPackages(
+ Collections.singleton(packageName), item.user),
+ "user explicitly removes the promise app icon"))
+ .create().show();
}
/**
@@ -248,13 +247,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
@@ -304,42 +309,6 @@
startAppShortcutOrInfoActivity(v, shortcut, launcher);
}
- /**
- * Event handler for a {@link SearchActionItemInfo} click
- */
- public static void onClickSearchAction(Launcher launcher, SearchActionItemInfo itemInfo) {
- if (itemInfo.getIntent() != null) {
- if (itemInfo.hasFlags(SearchActionItemInfo.FLAG_SHOULD_START_FOR_RESULT)) {
- launcher.startActivityForResult(itemInfo.getIntent(), 0);
- } else {
- launcher.startActivity(itemInfo.getIntent());
- }
- } else if (itemInfo.getPendingIntent() != null) {
- try {
- PendingIntent pendingIntent = itemInfo.getPendingIntent();
- if (!itemInfo.hasFlags(SearchActionItemInfo.FLAG_SHOULD_START)) {
- pendingIntent.send();
- } else if (itemInfo.hasFlags(SearchActionItemInfo.FLAG_SHOULD_START_FOR_RESULT)) {
- launcher.startIntentSenderForResult(pendingIntent.getIntentSender(), 0, null, 0,
- 0, 0);
- } else {
- launcher.startIntentSender(pendingIntent.getIntentSender(), null, 0, 0, 0);
- }
- } catch (PendingIntent.CanceledException | IntentSender.SendIntentException e) {
- Toast.makeText(launcher,
- launcher.getResources().getText(R.string.shortcut_not_available),
- Toast.LENGTH_SHORT).show();
- }
- }
- if (itemInfo.hasFlags(SearchActionItemInfo.FLAG_SEARCH_IN_APP)) {
- launcher.getStatsLogManager().logger().withItemInfo(itemInfo).log(
- LAUNCHER_ALLAPPS_SEARCHINAPP_LAUNCH);
- } else {
- launcher.getStatsLogManager().logger().withItemInfo(itemInfo).log(
- LAUNCHER_APP_LAUNCH_TAP);
- }
- }
-
private static void startAppShortcutOrInfoActivity(View v, ItemInfo item, Launcher launcher) {
TestLogging.recordEvent(
TestProtocol.SEQUENCE_MAIN, "start: startAppShortcutOrInfoActivity");
@@ -380,4 +349,15 @@
}
launcher.startActivitySafely(v, intent, item);
}
+
+ /**
+ * Interface to indicate that an item will handle the click itself.
+ */
+ public interface ItemClickProxy {
+
+ /**
+ * Called when the item is clicked
+ */
+ void onItemClicked(View view);
+ }
}
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 fa4eb70..097823b 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,18 +431,26 @@
float onScreenRectCenterY, float fullscreenScaleX, float fullscreenScaleY,
int drawableWidth, int drawableHeight, DeviceProfile dp,
@StagePosition int stagePosition) {
- float inset = dp.getInsets().top;
- out.setX(Math.round(onScreenRectCenterX / fullscreenScaleX
- - 1.0f * drawableWidth / 2));
- out.setY(Math.round((onScreenRectCenterY + (inset / 2f)) / fullscreenScaleY
- - 1.0f * drawableHeight / 2));
+ float insetAdjustment = getPlaceholderSizeAdjustment(dp) / 2f;
+ out.setX(onScreenRectCenterX / fullscreenScaleX
+ - 1.0f * drawableWidth / 2);
+ out.setY((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) {
out.setPivotX(0);
- out.setPivotY(0);
+ out.setPivotY(splitInstructionsHeight);
out.setRotation(getDegreesRotated());
int distanceToEdge = out.getResources().getDimensionPixelSize(
R.dimen.split_instructions_bottom_margin_phone_landscape);
@@ -448,8 +458,8 @@
int insetCorrectionX = dp.getInsets().left;
// Center the view in case of unbalanced insets on top or bottom of screen
int insetCorrectionY = (dp.getInsets().bottom - dp.getInsets().top) / 2;
- out.setTranslationX(splitInstructionsHeight + distanceToEdge - insetCorrectionX);
- out.setTranslationY(((splitInstructionsHeight - splitInstructionsWidth) / 2f)
+ out.setTranslationX(distanceToEdge - insetCorrectionX);
+ out.setTranslationY(((-splitInstructionsHeight - splitInstructionsWidth) / 2f)
+ insetCorrectionY);
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) out.getLayoutParams();
// Setting gravity to LEFT instead of the lint-recommended START because we always want this
@@ -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;
@@ -523,6 +533,7 @@
iconParams.rightMargin = -taskIconHeight - taskIconMargin / 2;
iconParams.leftMargin = 0;
iconParams.topMargin = thumbnailTopMargin / 2;
+ iconParams.bottomMargin = 0;
}
@Override
@@ -580,4 +591,22 @@
FloatProperty secondary, DeviceProfile deviceProfile) {
return new Pair<>(primary, secondary);
}
+
+ @Override
+ public float getFloatingTaskOffscreenTranslationTarget(View floatingTask, RectF onScreenRect,
+ @StagePosition int stagePosition, DeviceProfile dp) {
+ float currentTranslationY = floatingTask.getTranslationY();
+ return currentTranslationY - onScreenRect.height();
+ }
+
+ @Override
+ public void setFloatingTaskPrimaryTranslation(View floatingTask, float translation,
+ DeviceProfile dp) {
+ floatingTask.setTranslationY(translation);
+ }
+
+ @Override
+ public Float getFloatingTaskPrimaryTranslation(View floatingTask, DeviceProfile dp) {
+ return floatingTask.getTranslationY();
+ }
}
diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java
index 6bc021b..6234462 100644
--- a/src/com/android/launcher3/touch/PagedOrientationHandler.java
+++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java
@@ -34,9 +34,9 @@
import com.android.launcher3.DeviceProfile;
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.util.SplitConfigurationOptions.SplitBounds;
import java.util.List;
@@ -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
@@ -242,6 +235,41 @@
*/
void fixBoundsForHomeAnimStartRect(RectF outStartRect, DeviceProfile deviceProfile);
+ /**
+ * Determine the target translation for animating the FloatingTaskView out. This value could
+ * either be an x-coordinate or a y-coordinate, depending on which way the FloatingTaskView was
+ * docked.
+ *
+ * @param floatingTask The FloatingTaskView.
+ * @param onScreenRect The current on-screen dimensions of the FloatingTaskView.
+ * @param stagePosition STAGE_POSITION_TOP_OR_LEFT or STAGE_POSITION_BOTTOM_OR_RIGHT.
+ * @param dp The device profile.
+ * @return A float. When an animation translates the FloatingTaskView to this position, it will
+ * appear to tuck away off the edge of the screen.
+ */
+ float getFloatingTaskOffscreenTranslationTarget(View floatingTask, RectF onScreenRect,
+ @StagePosition int stagePosition, DeviceProfile dp);
+
+ /**
+ * Sets the translation of a FloatingTaskView along its "slide-in/slide-out" axis (could be
+ * either x or y), depending on how the view is oriented.
+ *
+ * @param floatingTask The FloatingTaskView to be translated.
+ * @param translation The target translation value.
+ * @param dp The current device profile.
+ */
+ void setFloatingTaskPrimaryTranslation(View floatingTask, float translation, DeviceProfile dp);
+
+ /**
+ * Gets the translation of a FloatingTaskView along its "slide-in/slide-out" axis (could be
+ * either x or y), depending on how the view is oriented.
+ *
+ * @param floatingTask The FloatingTaskView in question.
+ * @param dp The current device profile.
+ * @return The current translation value.
+ */
+ Float getFloatingTaskPrimaryTranslation(View floatingTask, DeviceProfile dp);
+
class ChildBounds {
public final int primaryDimension;
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index 181dca5..316cf0e 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.util.SplitConfigurationOptions.SplitBounds;
-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,36 +461,50 @@
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
- - 1.0f * drawableHeight / 2));
+ out.setX(onScreenRectCenterX / fullscreenScaleX
+ - 1.0f * drawableWidth / 2);
+ out.setY((onScreenRectCenterY + insetAdjustment) / fullscreenScaleY
+ - 1.0f * drawableHeight / 2);
} else {
if (pinToRight) {
- float inset = dp.getInsets().right;
- out.setX(Math.round((onScreenRectCenterX - (inset / 2f)) / fullscreenScaleX
- - 1.0f * drawableWidth / 2));
+ out.setX((onScreenRectCenterX - insetAdjustment) / fullscreenScaleX
+ - 1.0f * drawableWidth / 2);
} else {
- float inset = dp.getInsets().left;
- out.setX(Math.round((onScreenRectCenterX + (inset / 2f)) / fullscreenScaleX
- - 1.0f * drawableWidth / 2));
+ out.setX((onScreenRectCenterX + insetAdjustment) / fullscreenScaleX
+ - 1.0f * drawableWidth / 2);
}
- out.setY(Math.round(onScreenRectCenterY / fullscreenScaleY
- - 1.0f * drawableHeight / 2));
+ out.setY(onScreenRectCenterY / 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, 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) {
out.setPivotX(0);
- out.setPivotY(0);
+ out.setPivotY(splitInstructionsHeight);
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);
}
@@ -658,8 +685,9 @@
public void setTaskIconParams(FrameLayout.LayoutParams iconParams, int taskIconMargin,
int taskIconHeight, int thumbnailTopMargin, boolean isRtl) {
iconParams.gravity = TOP | CENTER_HORIZONTAL;
+ // Reset margins, since they may have been set on rotation
iconParams.leftMargin = iconParams.rightMargin = 0;
- iconParams.topMargin = taskIconMargin;
+ iconParams.topMargin = iconParams.bottomMargin = 0;
}
@Override
@@ -756,4 +784,36 @@
return new Pair<>(secondary, primary);
}
}
+
+ @Override
+ public float getFloatingTaskOffscreenTranslationTarget(View floatingTask, RectF onScreenRect,
+ @StagePosition int stagePosition, DeviceProfile dp) {
+ if (dp.isLandscape) {
+ float currentTranslationX = floatingTask.getTranslationX();
+ return stagePosition == STAGE_POSITION_TOP_OR_LEFT
+ ? currentTranslationX - onScreenRect.width()
+ : currentTranslationX + onScreenRect.width();
+ } else {
+ float currentTranslationY = floatingTask.getTranslationY();
+ return currentTranslationY - onScreenRect.height();
+ }
+ }
+
+ @Override
+ public void setFloatingTaskPrimaryTranslation(View floatingTask, float translation,
+ DeviceProfile dp) {
+ if (dp.isLandscape) {
+ floatingTask.setTranslationX(translation);
+ } else {
+ floatingTask.setTranslationY(translation);
+ }
+
+ }
+
+ @Override
+ public Float getFloatingTaskPrimaryTranslation(View floatingTask, DeviceProfile dp) {
+ return dp.isLandscape
+ ? floatingTask.getTranslationX()
+ : floatingTask.getTranslationY();
+ }
}
diff --git a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
index 339f910..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));
}
}
@@ -190,7 +186,7 @@
public void setSplitInstructionsParams(View out, DeviceProfile dp, int splitInstructionsHeight,
int splitInstructionsWidth, int threeButtonNavShift) {
out.setPivotX(0);
- out.setPivotY(0);
+ out.setPivotY(splitInstructionsHeight);
out.setRotation(getDegreesRotated());
int distanceToEdge = out.getResources().getDimensionPixelSize(
R.dimen.split_instructions_bottom_margin_phone_landscape);
@@ -198,9 +194,8 @@
int insetCorrectionX = dp.getInsets().right;
// Center the view in case of unbalanced insets on top or bottom of screen
int insetCorrectionY = (dp.getInsets().bottom - dp.getInsets().top) / 2;
- out.setTranslationX(splitInstructionsWidth - splitInstructionsHeight - distanceToEdge
- + insetCorrectionX);
- out.setTranslationY(((splitInstructionsHeight + splitInstructionsWidth) / 2f)
+ out.setTranslationX(splitInstructionsWidth - distanceToEdge + insetCorrectionX);
+ out.setTranslationY(((-splitInstructionsHeight + splitInstructionsWidth) / 2f)
+ insetCorrectionY);
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) out.getLayoutParams();
// Setting gravity to RIGHT instead of the lint-recommended END because we always want this
@@ -216,6 +211,7 @@
iconParams.leftMargin = -taskIconHeight - taskIconMargin / 2;
iconParams.rightMargin = 0;
iconParams.topMargin = thumbnailTopMargin / 2;
+ iconParams.bottomMargin = 0;
}
@Override
@@ -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/touch/WorkspaceTouchListener.java b/src/com/android/launcher3/touch/WorkspaceTouchListener.java
index 6cb021b..96ae4a3 100644
--- a/src/com/android/launcher3/touch/WorkspaceTouchListener.java
+++ b/src/com/android/launcher3/touch/WorkspaceTouchListener.java
@@ -44,6 +44,7 @@
import com.android.launcher3.logger.LauncherAtom;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
+import com.android.launcher3.util.TouchUtil;
/**
* Helper class to handle touch on empty space in workspace and show options popup on long press
@@ -105,6 +106,11 @@
if (handleLongPress) {
mLongPressState = STATE_REQUESTED;
mTouchDownPoint.set(ev.getX(), ev.getY());
+ // Mouse right button's ACTION_DOWN should immediately show menu
+ if (TouchUtil.isMouseRightClickDownOrMove(ev)) {
+ maybeShowMenu();
+ return true;
+ }
}
mWorkspace.onTouchEvent(ev);
@@ -185,6 +191,10 @@
@Override
public void onLongPress(MotionEvent event) {
+ maybeShowMenu();
+ }
+
+ private void maybeShowMenu() {
if (mLongPressState == STATE_REQUESTED) {
TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "Workspace.longPress");
if (canHandleLongPress()) {
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..1922310
--- /dev/null
+++ b/src/com/android/launcher3/util/DimensionUtils.kt
@@ -0,0 +1,63 @@
+/*
+ * 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
+ }
+}
diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java
index 30e7469..9ed6700 100644
--- a/src/com/android/launcher3/util/DisplayController.java
+++ b/src/com/android/launcher3/util/DisplayController.java
@@ -19,13 +19,10 @@
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.config.FeatureFlags.FORCE_PERSISTENT_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;
import android.annotation.SuppressLint;
@@ -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;
@@ -113,8 +110,7 @@
}
// Initialize navigation mode change listener
- mContext.registerReceiver(mReceiver,
- getPackageFilter(TARGET_OVERLAY_PACKAGE, ACTION_OVERLAY_CHANGED));
+ mReceiver.registerPkgActions(mContext, TARGET_OVERLAY_PACKAGE, ACTION_OVERLAY_CHANGED);
WindowManagerProxy wmProxy = WindowManagerProxy.INSTANCE.get(context);
Context displayInfoContext = getDisplayInfoContext(display);
@@ -129,6 +125,37 @@
return INSTANCE.get(context).getInfo().navigationMode;
}
+ /**
+ * Returns whether taskbar is transient.
+ */
+ public static boolean isTransientTaskbar(Context context) {
+ return INSTANCE.get(context).isTransientTaskbar();
+ }
+
+ /**
+ * Returns whether taskbar is transient.
+ */
+ public boolean isTransientTaskbar() {
+ // TODO(b/258604917): When running in test harness, use !sTransientTaskbarStatusForTests
+ // once tests are updated to expect new persistent behavior such as not allowing long press
+ // to stash.
+ if (!Utilities.IS_RUNNING_IN_TEST_HARNESS && FORCE_PERSISTENT_TASKBAR.get()) {
+ return false;
+ }
+ return getInfo().navigationMode == 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;
@@ -278,11 +305,11 @@
public final float fontScale;
private final int densityDpi;
public final NavigationMode navigationMode;
-
private final PortraitSize mScreenSizeDp;
+ // WindowBounds
+ public final WindowBounds realBounds;
public final Set<WindowBounds> supportedBounds = new ArraySet<>();
-
private final ArrayMap<CachedDisplayInfo, WindowBounds[]> mPerDisplayBounds =
new ArrayMap<>();
@@ -294,7 +321,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,12 +332,12 @@
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);
- WindowBounds realBounds = wmProxy.getRealBounds(displayInfoContext, displayInfo);
+ realBounds = wmProxy.getRealBounds(displayInfoContext, displayInfo);
if (cachedValue == null) {
// Unexpected normalizedDisplayInfo is found, recreate the cache
Log.e(TAG, "Unexpected normalizedDisplayInfo found, invalidating cache");
@@ -405,35 +432,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/LauncherBindableItemsContainer.java b/src/com/android/launcher3/util/LauncherBindableItemsContainer.java
index a4cb30a..f73940b 100644
--- a/src/com/android/launcher3/util/LauncherBindableItemsContainer.java
+++ b/src/com/android/launcher3/util/LauncherBindableItemsContainer.java
@@ -50,7 +50,12 @@
Drawable oldIcon = shortcut.getIcon();
boolean oldPromiseState = (oldIcon instanceof PreloadIconDrawable)
&& ((PreloadIconDrawable) oldIcon).hasNotCompleted();
- shortcut.applyFromWorkspaceItem(si, si.isPromise() != oldPromiseState);
+ shortcut.applyFromWorkspaceItem(
+ si,
+ si.isPromise() != oldPromiseState
+ && oldIcon instanceof PreloadIconDrawable
+ ? (PreloadIconDrawable) oldIcon
+ : null);
} else if (info instanceof FolderInfo && v instanceof FolderIcon) {
((FolderIcon) v).updatePreviewItems(updates::contains);
}
@@ -74,7 +79,7 @@
ItemOperator op = (info, v) -> {
if (info instanceof WorkspaceItemInfo && v instanceof BubbleTextView
&& updates.contains(info)) {
- ((BubbleTextView) v).applyLoadingState(false /* promiseStateChanged */);
+ ((BubbleTextView) v).applyLoadingState(null);
} else if (v instanceof PendingAppWidgetHostView
&& info instanceof LauncherAppWidgetInfo
&& updates.contains(info)) {
diff --git a/src/com/android/launcher3/util/LockedUserState.kt b/src/com/android/launcher3/util/LockedUserState.kt
new file mode 100644
index 0000000..7b49583
--- /dev/null
+++ b/src/com/android/launcher3/util/LockedUserState.kt
@@ -0,0 +1,57 @@
+package com.android.launcher3.util
+
+import android.content.Context
+import android.content.Intent
+import android.os.Process
+import android.os.UserManager
+import androidx.annotation.VisibleForTesting
+
+class LockedUserState(private val mContext: Context) : SafeCloseable {
+ var isUserUnlocked: Boolean
+ private set
+ private val mUserUnlockedActions: RunnableList = RunnableList()
+
+ @VisibleForTesting
+ val mUserUnlockedReceiver = SimpleBroadcastReceiver {
+ if (Intent.ACTION_USER_UNLOCKED == it.action) {
+ isUserUnlocked = true
+ notifyUserUnlocked()
+ }
+ }
+
+ init {
+ isUserUnlocked =
+ mContext
+ .getSystemService(UserManager::class.java)!!
+ .isUserUnlocked(Process.myUserHandle())
+ if (isUserUnlocked) {
+ notifyUserUnlocked()
+ } else {
+ mUserUnlockedReceiver.register(mContext, Intent.ACTION_USER_UNLOCKED)
+ }
+ }
+
+ private fun notifyUserUnlocked() {
+ mUserUnlockedActions.executeAllAndDestroy()
+ mUserUnlockedReceiver.unregisterReceiverSafely(mContext)
+ }
+
+ /** Stops the receiver from listening for ACTION_USER_UNLOCK broadcasts. */
+ override fun close() {
+ mUserUnlockedReceiver.unregisterReceiverSafely(mContext)
+ }
+
+ /**
+ * Adds a `Runnable` to be executed when a user is unlocked. If the user is already unlocked,
+ * this runnable will run immediately because RunnableList will already have been destroyed.
+ */
+ fun runOnUserUnlocked(action: Runnable) {
+ mUserUnlockedActions.add(action)
+ }
+
+ companion object {
+ @VisibleForTesting val INSTANCE = MainThreadInitializedObject { LockedUserState(it) }
+
+ @JvmStatic fun get(context: Context): LockedUserState = INSTANCE.get(context)
+ }
+}
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..c146216 100644
--- a/src/com/android/launcher3/util/OnboardingPrefs.java
+++ b/src/com/android/launcher3/util/OnboardingPrefs.java
@@ -41,16 +41,18 @@
public static final String SEARCH_KEYBOARD_EDU_SEEN = "launcher.search_edu_seen";
public static final String SEARCH_SNACKBAR_COUNT = "launcher.keyboard_snackbar_count";
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 TASKBAR_EDU_SEEN = "launcher.taskbar_edu_seen2";
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";
+ public static final String TASKBAR_EDU_TOOLTIP_STEP = "launcher.taskbar_edu_tooltip_step";
// 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},
- "Taskbar Education", new String[] { TASKBAR_EDU_SEEN },
+ SEARCH_ONBOARDING_COUNT, QSB_SEARCH_ONBOARDING_CARD_DISMISSED},
+ "Taskbar Education", new String[] { TASKBAR_EDU_SEEN, TASKBAR_EDU_TOOLTIP_STEP },
"All Apps Visited Count", new String[] {ALL_APPS_VISITED_COUNT}
);
@@ -61,7 +63,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 {}
@@ -74,7 +77,8 @@
HOTSEAT_DISCOVERY_TIP_COUNT,
SEARCH_SNACKBAR_COUNT,
SEARCH_ONBOARDING_COUNT,
- ALL_APPS_VISITED_COUNT
+ ALL_APPS_VISITED_COUNT,
+ TASKBAR_EDU_TOOLTIP_STEP,
})
@Retention(RetentionPolicy.SOURCE)
public @interface EventCountKey {}
@@ -89,6 +93,7 @@
// This is the sum of all onboarding cards. Currently there is only 1 card shown 3 times.
maxCounts.put(SEARCH_ONBOARDING_COUNT, 3);
maxCounts.put(ALL_APPS_VISITED_COUNT, 20);
+ maxCounts.put(TASKBAR_EDU_TOOLTIP_STEP, 2);
MAX_COUNTS = Collections.unmodifiableMap(maxCounts);
}
diff --git a/src/com/android/launcher3/util/PackageManagerHelper.java b/src/com/android/launcher3/util/PackageManagerHelper.java
index f42d304..a6a2751 100644
--- a/src/com/android/launcher3/util/PackageManagerHelper.java
+++ b/src/com/android/launcher3/util/PackageManagerHelper.java
@@ -16,14 +16,11 @@
package com.android.launcher3.util;
-import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
-
import android.app.AppOpsManager;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.LauncherApps;
@@ -31,18 +28,18 @@
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
-import android.content.res.Resources;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
-import android.os.PatternMatcher;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
-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 +51,7 @@
import java.net.URISyntaxException;
import java.util.List;
+import java.util.Objects;
/**
* Utility methods using package manager
@@ -62,22 +60,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 +90,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 +124,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));
@@ -238,20 +247,8 @@
}
}
- /**
- * Creates an intent filter to listen for actions with a specific package in the data field.
- */
- public static IntentFilter getPackageFilter(String pkg, String... actions) {
- IntentFilter packageFilter = new IntentFilter();
- for (String action : actions) {
- packageFilter.addAction(action);
- }
- packageFilter.addDataScheme("package");
- packageFilter.addDataSchemeSpecificPart(pkg, PatternMatcher.PATTERN_LITERAL);
- 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;
@@ -280,25 +277,6 @@
}
/**
- * Finds a system apk which had a broadcast receiver listening to a particular action.
- * @param action intent action used to find the apk
- * @return a pair of apk package name and the resources.
- */
- public static Pair<String, Resources> findSystemApk(String action, PackageManager pm) {
- final Intent intent = new Intent(action);
- for (ResolveInfo info : pm.queryBroadcastReceivers(intent, MATCH_SYSTEM_ONLY)) {
- final String packageName = info.activityInfo.packageName;
- try {
- final Resources res = pm.getResourcesForApplication(packageName);
- return Pair.create(packageName, res);
- } catch (NameNotFoundException e) {
- Log.w(TAG, "Failed to find resources for " + packageName);
- }
- }
- return null;
- }
-
- /**
* Returns true if the intent is a valid launch intent for a launcher activity of an app.
* This is used to identify shortcuts which are different from the ones exposed by the
* applications' manifest file.
diff --git a/src/com/android/launcher3/util/Partner.java b/src/com/android/launcher3/util/Partner.java
new file mode 100644
index 0000000..220ab56
--- /dev/null
+++ b/src/com/android/launcher3/util/Partner.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2014 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 android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
+
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.util.Log;
+import android.util.Pair;
+
+/**
+ * Utilities to discover and interact with partner customizations. There can
+ * only be one set of customizations on a device, and it must be bundled with
+ * the system.
+ */
+public class Partner {
+
+ static final String TAG = "Launcher.Partner";
+
+ /** Marker action used to discover partner */
+ private static final String
+ ACTION_PARTNER_CUSTOMIZATION = "com.android.launcher3.action.PARTNER_CUSTOMIZATION";
+
+ /**
+ * Find and return partner details, or {@code null} if none exists.
+ */
+ public static Partner get(PackageManager pm) {
+ return get(pm, ACTION_PARTNER_CUSTOMIZATION);
+ }
+
+ /**
+ * Find and return partner details, or {@code null} if none exists.
+ */
+ public static Partner get(PackageManager pm, String action) {
+ Pair<String, Resources> apkInfo = findSystemApk(action, pm);
+ return apkInfo != null ? new Partner(apkInfo.first, apkInfo.second) : null;
+ }
+
+ private final String mPackageName;
+ private final Resources mResources;
+
+ private Partner(String packageName, Resources res) {
+ mPackageName = packageName;
+ mResources = res;
+ }
+
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ public Resources getResources() {
+ return mResources;
+ }
+
+ /**
+ * Returns the xml resource Id for the provided name, or 0 is the resource is not found
+ */
+ public int getXmlResId(String layoutName) {
+ return getResources().getIdentifier(layoutName, "xml", getPackageName());
+ }
+
+ /**
+ * Returns the integer resource value for the provided resource name,
+ * or default value if the resource name is not present
+ */
+ public int getIntValue(String resName, int defaultValue) {
+ int resId = getResources().getIdentifier(resName, "integer", getPackageName());
+ return resId > 0 ? getResources().getInteger(resId) : defaultValue;
+ }
+
+ /**
+ * Returns the dimension value for the provided resource name,
+ * or default value if the resource name is not present
+ */
+ public float getDimenValue(String resName, int defaultValue) {
+ int resId = getResources().getIdentifier(resName, "dimen", getPackageName());
+ return resId > 0 ? getResources().getDimension(resId) : defaultValue;
+ }
+
+ /**
+ * Finds a system apk which had a broadcast receiver listening to a particular action.
+ * @param action intent action used to find the apk
+ * @return a pair of apk package name and the resources.
+ */
+ private static Pair<String, Resources> findSystemApk(String action, PackageManager pm) {
+ final Intent intent = new Intent(action);
+ for (ResolveInfo info : pm.queryBroadcastReceivers(intent, MATCH_SYSTEM_ONLY)) {
+ final String packageName = info.activityInfo.packageName;
+ try {
+ final Resources res = pm.getResourcesForApplication(packageName);
+ return Pair.create(packageName, res);
+ } catch (NameNotFoundException e) {
+ Log.w(TAG, "Failed to find resources for " + packageName);
+ }
+ }
+ return 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/ScreenOnTracker.java b/src/com/android/launcher3/util/ScreenOnTracker.java
new file mode 100644
index 0000000..67530a6
--- /dev/null
+++ b/src/com/android/launcher3/util/ScreenOnTracker.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.util;
+
+import static android.content.Intent.ACTION_SCREEN_OFF;
+import static android.content.Intent.ACTION_SCREEN_ON;
+import static android.content.Intent.ACTION_USER_PRESENT;
+
+import android.content.Context;
+import android.content.Intent;
+
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Utility class for tracking if the screen is currently on or off
+ */
+public class ScreenOnTracker {
+
+ public static final MainThreadInitializedObject<ScreenOnTracker> INSTANCE =
+ new MainThreadInitializedObject<>(ScreenOnTracker::new);
+
+ private final SimpleBroadcastReceiver mReceiver = new SimpleBroadcastReceiver(this::onReceive);
+ private final CopyOnWriteArrayList<ScreenOnListener> mListeners = new CopyOnWriteArrayList<>();
+
+ private boolean mIsScreenOn;
+
+ private ScreenOnTracker(Context context) {
+ // Assume that the screen is on to begin with
+ mIsScreenOn = true;
+ mReceiver.register(context, ACTION_SCREEN_ON, ACTION_SCREEN_OFF, ACTION_USER_PRESENT);
+ }
+
+ private void onReceive(Intent intent) {
+ String action = intent.getAction();
+ if (ACTION_SCREEN_ON.equals(action)) {
+ mIsScreenOn = true;
+ dispatchScreenOnChanged();
+ } else if (ACTION_SCREEN_OFF.equals(action)) {
+ mIsScreenOn = false;
+ dispatchScreenOnChanged();
+ } else if (ACTION_USER_PRESENT.equals(action)) {
+ mListeners.forEach(ScreenOnListener::onUserPresent);
+ }
+ }
+
+ private void dispatchScreenOnChanged() {
+ mListeners.forEach(l -> l.onScreenOnChanged(mIsScreenOn));
+ }
+
+ /** Returns if the screen is on or not */
+ public boolean isScreenOn() {
+ return mIsScreenOn;
+ }
+
+ /** Adds a listener for screen on changes */
+ public void addListener(ScreenOnListener listener) {
+ mListeners.add(listener);
+ }
+
+ /** Removes a previously added listener */
+ public void removeListener(ScreenOnListener listener) {
+ mListeners.remove(listener);
+ }
+
+ /**
+ * Interface to listen for screen on changes
+ */
+ public interface ScreenOnListener {
+
+ /**
+ * Called when the screen turns on/off
+ */
+ void onScreenOnChanged(boolean isOn);
+
+ /**
+ * Called when the keyguard goes away
+ */
+ default void onUserPresent() { }
+ }
+}
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..064bcd0 100644
--- a/src/com/android/launcher3/util/SimpleBroadcastReceiver.java
+++ b/src/com/android/launcher3/util/SimpleBroadcastReceiver.java
@@ -19,6 +19,10 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.os.PatternMatcher;
+import android.text.TextUtils;
+
+import androidx.annotation.Nullable;
import java.util.function.Consumer;
@@ -39,17 +43,44 @@
* Helper method to register multiple actions
*/
public void register(Context context, String... actions) {
- register(context, 0, actions);
+ context.registerReceiver(this, getFilter(actions));
}
/**
- * Helper method to register multiple actions with one or more {@code flags}.
+ * Helper method to register multiple actions associated with a paction
*/
- public void register(Context context, int flags, String... actions) {
+ public void registerPkgActions(Context context, @Nullable String pkg, String... actions) {
+ context.registerReceiver(this, getPackageFilter(pkg, actions));
+ }
+
+ /**
+ * Creates an intent filter to listen for actions with a specific package in the data field.
+ */
+ public static IntentFilter getPackageFilter(String pkg, String... actions) {
+ IntentFilter filter = getFilter(actions);
+ filter.addDataScheme("package");
+ if (!TextUtils.isEmpty(pkg)) {
+ filter.addDataSchemeSpecificPart(pkg, PatternMatcher.PATTERN_LITERAL);
+ }
+ return filter;
+ }
+
+ private static IntentFilter getFilter(String... actions) {
IntentFilter filter = new IntentFilter();
for (String action : actions) {
filter.addAction(action);
}
- context.registerReceiver(this, filter, flags);
+ return filter;
+ }
+
+ /**
+ * 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/SystemUiController.java b/src/com/android/launcher3/util/SystemUiController.java
index 6945983..df54fd7 100644
--- a/src/com/android/launcher3/util/SystemUiController.java
+++ b/src/com/android/launcher3/util/SystemUiController.java
@@ -35,6 +35,7 @@
public static final int UI_STATE_SCRIM_VIEW = 1;
public static final int UI_STATE_WIDGET_BOTTOM_SHEET = 2;
public static final int UI_STATE_FULLSCREEN_TASK = 3;
+ public static final int UI_STATE_ALL_APPS = 4;
public static final int FLAG_LIGHT_NAV = 1 << 0;
public static final int FLAG_DARK_NAV = 1 << 1;
@@ -54,7 +55,7 @@
public @interface SystemUiControllerFlags {}
private final Window mWindow;
- private final int[] mStates = new int[4];
+ private final int[] mStates = new int[5];
public SystemUiController(Window window) {
mWindow = window;
diff --git a/src/com/android/launcher3/util/Themes.java b/src/com/android/launcher3/util/Themes.java
index 53a584d..5526839 100644
--- a/src/com/android/launcher3/util/Themes.java
+++ b/src/com/android/launcher3/util/Themes.java
@@ -19,6 +19,8 @@
import static android.app.WallpaperColors.HINT_SUPPORTS_DARK_TEXT;
import static android.app.WallpaperColors.HINT_SUPPORTS_DARK_THEME;
+import static com.android.launcher3.LauncherPrefs.THEMED_ICONS;
+
import android.app.WallpaperColors;
import android.app.WallpaperManager;
import android.content.Context;
@@ -30,9 +32,9 @@
import android.util.SparseArray;
import android.util.TypedValue;
+import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.GraphicsUtils;
/**
@@ -74,8 +76,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 LauncherPrefs.get(context).get(THEMED_ICONS);
}
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/TouchUtil.java b/src/com/android/launcher3/util/TouchUtil.java
new file mode 100644
index 0000000..b18a2ef
--- /dev/null
+++ b/src/com/android/launcher3/util/TouchUtil.java
@@ -0,0 +1,37 @@
+/*
+ * 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.view.InputDevice;
+import android.view.MotionEvent;
+
+import androidx.annotation.NonNull;
+
+/** Util class for touch event. */
+public final class TouchUtil {
+
+ private TouchUtil() {}
+
+ /**
+ * Detect ACTION_DOWN or ACTION_MOVE from mouse right button. Note that we cannot detect
+ * ACTION_UP from mouse's right button because, in that case,
+ * {@link MotionEvent#getButtonState()} returns 0 for any mouse button (right, middle, right).
+ */
+ public static boolean isMouseRightClickDownOrMove(@NonNull MotionEvent event) {
+ return event.isFromSource(InputDevice.SOURCE_MOUSE)
+ && ((event.getButtonState() & MotionEvent.BUTTON_SECONDARY) != 0);
+ }
+}
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/quickstep/src/com/android/quickstep/util/VibratorWrapper.java b/src/com/android/launcher3/util/VibratorWrapper.java
similarity index 95%
rename from quickstep/src/com/android/quickstep/util/VibratorWrapper.java
rename to src/com/android/launcher3/util/VibratorWrapper.java
index 211bd08..932bcfc 100644
--- a/quickstep/src/com/android/quickstep/util/VibratorWrapper.java
+++ b/src/com/android/launcher3/util/VibratorWrapper.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.quickstep.util;
+package com.android.launcher3.util;
import static android.os.VibrationEffect.createPredefined;
import static android.provider.Settings.System.HAPTIC_FEEDBACK_ENABLED;
@@ -51,8 +51,6 @@
public static final VibrationEffect EFFECT_CLICK =
createPredefined(VibrationEffect.EFFECT_CLICK);
- public static final VibrationEffect EFFECT_TEXTURE_TICK =
- VibrationEffect.createPredefined(VibrationEffect.EFFECT_TEXTURE_TICK);
/**
* Haptic when entering overview.
diff --git a/src/com/android/launcher3/util/ViewCapture.java b/src/com/android/launcher3/util/ViewCapture.java
deleted file mode 100644
index 140971b..0000000
--- a/src/com/android/launcher3/util/ViewCapture.java
+++ /dev/null
@@ -1,212 +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.content.res.Resources;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.SystemClock;
-import android.os.Trace;
-import android.util.Base64;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewTreeObserver.OnDrawListener;
-
-import androidx.annotation.UiThread;
-
-import com.android.launcher3.view.ViewCaptureData.ExportedData;
-import com.android.launcher3.view.ViewCaptureData.FrameData;
-import com.android.launcher3.view.ViewCaptureData.ViewNode;
-
-import java.util.concurrent.FutureTask;
-
-/**
- * Utility class for capturing view data every frame
- */
-public class ViewCapture implements OnDrawListener {
-
- private static final String TAG = "ViewCapture";
-
- private static final int MEMORY_SIZE = 2000;
-
- private final View mRoot;
- private final long[] mFrameTimes = new long[MEMORY_SIZE];
- private final Node[] mNodes = new Node[MEMORY_SIZE];
-
- private int mFrameIndex = -1;
-
- /**
- * @param root the root view for the capture data
- */
- public ViewCapture(View root) {
- mRoot = root;
- }
-
- @Override
- public void onDraw() {
- Trace.beginSection("view_capture");
- long now = SystemClock.elapsedRealtimeNanos();
-
- mFrameIndex++;
- if (mFrameIndex >= MEMORY_SIZE) {
- mFrameIndex = 0;
- }
- mFrameTimes[mFrameIndex] = now;
- mNodes[mFrameIndex] = captureView(mRoot, mNodes[mFrameIndex]);
- Trace.endSection();
- }
-
- /**
- * Creates a proto of all the data captured so far.
- */
- public String dumpToString() {
- Handler handler = mRoot.getHandler();
- if (handler == null) {
- handler = Executors.MAIN_EXECUTOR.getHandler();
- }
- FutureTask<ExportedData> task = new FutureTask<>(this::dumpToProtoUI);
- if (Looper.myLooper() == handler.getLooper()) {
- task.run();
- } else {
- handler.post(task);
- }
- try {
- return Base64.encodeToString(task.get().toByteArray(),
- Base64.NO_CLOSE | Base64.NO_PADDING | Base64.NO_WRAP);
- } catch (Exception e) {
- Log.e(TAG, "Error capturing proto", e);
- return "--error--";
- }
- }
-
- @UiThread
- private ExportedData dumpToProtoUI() {
- ExportedData.Builder dataBuilder = ExportedData.newBuilder();
- Resources res = mRoot.getResources();
-
- int size = (mNodes[MEMORY_SIZE - 1] == null) ? mFrameIndex + 1 : MEMORY_SIZE;
- for (int i = size - 1; i >= 0; i--) {
- int index = (MEMORY_SIZE + mFrameIndex - i) % MEMORY_SIZE;
- dataBuilder.addFrameData(FrameData.newBuilder()
- .setNode(mNodes[index].toProto(res))
- .setTimestamp(mFrameTimes[index]));
- }
- return dataBuilder.build();
- }
-
- private Node captureView(View view, Node recycle) {
- Node result = recycle == null ? new Node() : recycle;
-
- result.clazz = view.getClass();
- result.hashCode = view.hashCode();
- result.id = view.getId();
- result.left = view.getLeft();
- result.top = view.getTop();
- result.right = view.getRight();
- result.bottom = view.getBottom();
- result.scrollX = view.getScrollX();
- result.scrollY = view.getScrollY();
-
- result.translateX = view.getTranslationX();
- result.translateY = view.getTranslationY();
- result.scaleX = view.getScaleX();
- result.scaleY = view.getScaleY();
- result.alpha = view.getAlpha();
-
- result.visibility = view.getVisibility();
- result.willNotDraw = view.willNotDraw();
-
- if (view instanceof ViewGroup) {
- ViewGroup parent = (ViewGroup) view;
- result.clipChildren = parent.getClipChildren();
- int childCount = parent.getChildCount();
- if (childCount == 0) {
- result.children = null;
- } else {
- result.children = captureView(parent.getChildAt(0), result.children);
- Node lastChild = result.children;
- for (int i = 1; i < childCount; i++) {
- lastChild.sibling = captureView(parent.getChildAt(i), lastChild.sibling);
- lastChild = lastChild.sibling;
- }
- lastChild.sibling = null;
- }
- } else {
- result.clipChildren = false;
- result.children = null;
- }
- return result;
- }
-
- private static class Node {
-
- // We store reference in memory to avoid generating and storing too many strings
- public Class clazz;
- public int hashCode;
-
- 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 Node sibling;
- public Node children;
-
- public ViewNode toProto(Resources res) {
- 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";
- }
-
- ViewNode.Builder result = ViewNode.newBuilder()
- .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);
- Node child = children;
- while (child != null) {
- result.addChildren(child.toProto(res));
- child = child.sibling;
- }
- return result.build();
- }
-
- }
-}
diff --git a/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java b/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java
index 43e9820..4ac6bc4 100644
--- a/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java
+++ b/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java
@@ -1,12 +1,11 @@
package com.android.launcher3.util;
+import static android.content.Intent.ACTION_WALLPAPER_CHANGED;
+
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import android.app.WallpaperManager;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
@@ -23,7 +22,7 @@
/**
* Utility class to handle wallpaper scrolling along with workspace.
*/
-public class WallpaperOffsetInterpolator extends BroadcastReceiver {
+public class WallpaperOffsetInterpolator {
private static final int[] sTempInt = new int[2];
private static final String TAG = "WPOffsetInterpolator";
@@ -32,6 +31,8 @@
// Don't use all the wallpaper for parallax until you have at least this many pages
private static final int MIN_PARALLAX_PAGE_SPAN = 4;
+ private final SimpleBroadcastReceiver mWallpaperChangeReceiver =
+ new SimpleBroadcastReceiver(i -> onWallpaperChanged());
private final Workspace<?> mWorkspace;
private final boolean mIsRtl;
private final Handler mHandler;
@@ -197,22 +198,20 @@
public void setWindowToken(IBinder token) {
mWindowToken = token;
if (mWindowToken == null && mRegistered) {
- mWorkspace.getContext().unregisterReceiver(this);
+ mWallpaperChangeReceiver.unregisterReceiverSafely(mWorkspace.getContext());
mRegistered = false;
} else if (mWindowToken != null && !mRegistered) {
- mWorkspace.getContext()
- .registerReceiver(this, new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED));
- onReceive(mWorkspace.getContext(), null);
+ mWallpaperChangeReceiver.register(mWorkspace.getContext(), ACTION_WALLPAPER_CHANGED);
+ onWallpaperChanged();
mRegistered = true;
}
}
- @Override
- public void onReceive(Context context, Intent intent) {
+ private void onWallpaperChanged() {
UI_HELPER_EXECUTOR.execute(() -> {
// Updating the boolean on a background thread is fine as the assignments are atomic
- mWallpaperIsLiveWallpaper =
- WallpaperManager.getInstance(context).getWallpaperInfo() != null;
+ mWallpaperIsLiveWallpaper = WallpaperManager.getInstance(mWorkspace.getContext())
+ .getWallpaperInfo() != null;
updateOffset();
});
}
diff --git a/src/com/android/launcher3/util/window/WindowManagerProxy.java b/src/com/android/launcher3/util/window/WindowManagerProxy.java
index 582ff8d..4093bc9 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 =
@@ -157,12 +161,10 @@
insetsBuilder.setInsetsIgnoringVisibility(WindowInsets.Type.navigationBars(), newNavInsets);
Insets statusBarInsets = oldInsets.getInsets(WindowInsets.Type.statusBars());
- int statusBarHeight = getDimenByName(systemRes,
- (isPortrait) ? STATUS_BAR_HEIGHT_PORTRAIT : STATUS_BAR_HEIGHT_LANDSCAPE,
- STATUS_BAR_HEIGHT);
+
Insets newStatusBarInsets = Insets.of(
statusBarInsets.left,
- Math.max(statusBarInsets.top, statusBarHeight),
+ getStatusBarHeight(context, isPortrait, statusBarInsets.top),
statusBarInsets.right,
statusBarInsets.bottom);
insetsBuilder.setInsets(WindowInsets.Type.statusBars(), newStatusBarInsets);
@@ -186,6 +188,15 @@
return result;
}
+ protected int getStatusBarHeight(Context context, boolean isPortrait, int statusBarInset) {
+ Resources systemRes = context.getResources();
+ int statusBarHeight = getDimenByName(systemRes,
+ isPortrait ? STATUS_BAR_HEIGHT_PORTRAIT : STATUS_BAR_HEIGHT_LANDSCAPE,
+ STATUS_BAR_HEIGHT);
+
+ return Math.max(statusBarInset, statusBarHeight);
+ }
+
/**
* Returns a list of possible WindowBounds for the display keyed on the 4 surface rotations
*/
@@ -208,6 +219,9 @@
boolean isTabletOrGesture = isTablet
|| (Utilities.ATLEAST_R && isGestureNav(context));
+ // Use the status bar height resources because current system API to get the status bar
+ // height doesn't allow to do this for an arbitrary display, it returns value only
+ // for the current active display (see com.android.internal.policy.StatusBarUtils)
int statusBarHeightPortrait = getDimenByName(systemRes,
STATUS_BAR_HEIGHT_PORTRAIT, STATUS_BAR_HEIGHT);
int statusBarHeightLandscape = getDimenByName(systemRes,
@@ -343,4 +357,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 e2dc34f..79b4cb4 100644
--- a/src/com/android/launcher3/views/ActivityContext.java
+++ b/src/com/android/launcher3/views/ActivityContext.java
@@ -15,35 +15,73 @@
*/
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.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
+import static com.android.launcher3.model.WidgetsModel.GO_DISABLE_WIDGETS;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+
+import android.app.ActivityOptions;
+import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.ContextWrapper;
+import android.content.Intent;
+import android.content.pm.LauncherApps;
import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Process;
+import android.os.StrictMode;
+import android.os.UserHandle;
+import android.util.Log;
+import android.view.Display;
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 android.widget.Toast;
import androidx.annotation.Nullable;
+import com.android.launcher3.BubbleTextView;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
+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.allapps.search.SearchAdapterProvider;
import com.android.launcher3.dot.DotInfo;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.logger.LauncherAtom;
+import com.android.launcher3.logging.InstanceId;
+import com.android.launcher3.logging.InstanceIdSequence;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.model.StringCache;
import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.popup.PopupDataProvider;
+import com.android.launcher3.util.ActivityOptionsWrapper;
import com.android.launcher3.util.OnboardingPrefs;
+import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.util.Preconditions;
+import com.android.launcher3.util.RunnableList;
import com.android.launcher3.util.ViewCache;
+import java.util.List;
+
/**
* An interface to be used along with a context for various activities in Launcher. This allows a
* generic class to depend on Context subclass instead of an Activity.
*/
public interface ActivityContext {
+ String TAG = "ActivityContext";
+
default boolean finishAutoCancelActionMode() {
return false;
}
@@ -106,6 +144,28 @@
DeviceProfile getDeviceProfile();
+ /** Registered {@link OnDeviceProfileChangeListener} instances. */
+ List<OnDeviceProfileChangeListener> getOnDeviceProfileChangeListeners();
+
+ /** Notifies listeners of a {@link DeviceProfile} change. */
+ default void dispatchDeviceProfileChanged() {
+ DeviceProfile deviceProfile = getDeviceProfile();
+ List<OnDeviceProfileChangeListener> listeners = getOnDeviceProfileChangeListeners();
+ for (int i = listeners.size() - 1; i >= 0; i--) {
+ listeners.get(i).onDeviceProfileChanged(deviceProfile);
+ }
+ }
+
+ /** Register listener for {@link DeviceProfile} changes. */
+ default void addOnDeviceProfileChangeListener(OnDeviceProfileChangeListener listener) {
+ getOnDeviceProfileChangeListeners().add(listener);
+ }
+
+ /** Unregister listener for {@link DeviceProfile} changes. */
+ default void removeOnDeviceProfileChangeListener(OnDeviceProfileChangeListener listener) {
+ getOnDeviceProfileChangeListeners().remove(listener);
+ }
+
default ViewCache getViewCache() {
return new ViewCache();
}
@@ -151,6 +211,222 @@
return false;
}
+ default View.OnClickListener getItemOnClickListener() {
+ return v -> {
+ // No op.
+ };
+ }
+
+ @Nullable
+ default PopupDataProvider getPopupDataProvider() {
+ return null;
+ }
+
+ @Nullable
+ default StringCache getStringCache() {
+ 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));
+ }
+ });
+ }
+ }
+
+ /**
+ * Safely starts an activity.
+ *
+ * @param v View starting the activity.
+ * @param intent Base intent being launched.
+ * @param item Item associated with the view.
+ * @return {@code true} if the activity starts successfully.
+ */
+ default boolean startActivitySafely(
+ View v, Intent intent, @Nullable ItemInfo item) {
+
+ Context context = (Context) this;
+ if (isAppBlockedForSafeMode() && !PackageManagerHelper.isSystemApp(context, intent)) {
+ Toast.makeText(context, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();
+ return false;
+ }
+
+ Bundle optsBundle = (v != null) ? getActivityLaunchOptions(v, item).toBundle() : null;
+ UserHandle user = item == null ? null : item.user;
+
+ // Prepare intent
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ if (v != null) {
+ intent.setSourceBounds(Utilities.getViewBounds(v));
+ }
+ try {
+ boolean isShortcut = (item instanceof WorkspaceItemInfo)
+ && (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT
+ || item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT)
+ && !((WorkspaceItemInfo) item).isPromise();
+ if (isShortcut) {
+ // Shortcuts need some special checks due to legacy reasons.
+ startShortcutIntentSafely(intent, optsBundle, item);
+ } else if (user == null || user.equals(Process.myUserHandle())) {
+ // Could be launching some bookkeeping activity
+ context.startActivity(intent, optsBundle);
+ } else {
+ context.getSystemService(LauncherApps.class).startMainActivity(
+ intent.getComponent(), user, intent.getSourceBounds(), optsBundle);
+ }
+ if (item != null) {
+ InstanceId instanceId = new InstanceIdSequence().newInstanceId();
+ logAppLaunch(getStatsLogManager(), item, instanceId);
+ }
+ return true;
+ } catch (NullPointerException | ActivityNotFoundException | SecurityException e) {
+ Toast.makeText(context, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
+ Log.e(TAG, "Unable to launch. tag=" + item + " intent=" + intent, e);
+ }
+ return false;
+ }
+
+ /** Returns {@code true} if an app launch is blocked due to safe mode. */
+ default boolean isAppBlockedForSafeMode() {
+ return false;
+ }
+
+ /**
+ * Creates and logs a new app launch event.
+ */
+ default void logAppLaunch(StatsLogManager statsLogManager, ItemInfo info,
+ InstanceId instanceId) {
+ statsLogManager.logger().withItemInfo(info).withInstanceId(instanceId)
+ .log(LAUNCHER_APP_LAUNCH_TAP);
+ }
+
+ /**
+ * Returns launch options for an Activity.
+ *
+ * @param v View initiating a launch.
+ * @param item Item associated with the view.
+ */
+ default ActivityOptionsWrapper getActivityLaunchOptions(View v, @Nullable ItemInfo item) {
+ int left = 0, top = 0;
+ int width = v.getMeasuredWidth(), height = v.getMeasuredHeight();
+ if (v instanceof BubbleTextView) {
+ // Launch from center of icon, not entire view
+ Drawable icon = ((BubbleTextView) v).getIcon();
+ if (icon != null) {
+ Rect bounds = icon.getBounds();
+ left = (width - bounds.width()) / 2;
+ top = v.getPaddingTop();
+ width = bounds.width();
+ height = bounds.height();
+ }
+ }
+ ActivityOptions options =
+ ActivityOptions.makeClipRevealAnimation(v, left, top, width, height);
+
+ options.setLaunchDisplayId(
+ (v != null && v.getDisplay() != null) ? v.getDisplay().getDisplayId()
+ : Display.DEFAULT_DISPLAY);
+ RunnableList callback = new RunnableList();
+ return new ActivityOptionsWrapper(options, callback);
+ }
+
+ /**
+ * Safely launches an intent for a shortcut.
+ *
+ * @param intent Intent to start.
+ * @param optsBundle Optional launch arguments.
+ * @param info Shortcut information.
+ */
+ default void startShortcutIntentSafely(Intent intent, Bundle optsBundle, ItemInfo info) {
+ try {
+ StrictMode.VmPolicy oldPolicy = StrictMode.getVmPolicy();
+ try {
+ // Temporarily disable deathPenalty on all default checks. For eg, shortcuts
+ // containing file Uri's would cause a crash as penaltyDeathOnFileUriExposure
+ // is enabled by default on NYC.
+ StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll()
+ .penaltyLog().build());
+
+ if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+ String id = ((WorkspaceItemInfo) info).getDeepShortcutId();
+ String packageName = intent.getPackage();
+ startShortcut(packageName, id, intent.getSourceBounds(), optsBundle, info.user);
+ } else {
+ // Could be launching some bookkeeping activity
+ ((Context) this).startActivity(intent, optsBundle);
+ }
+ } finally {
+ StrictMode.setVmPolicy(oldPolicy);
+ }
+ } catch (SecurityException e) {
+ if (!onErrorStartingShortcut(intent, info)) {
+ throw e;
+ }
+ }
+ }
+
+ /**
+ * A wrapper around the platform method with Launcher specific checks.
+ */
+ default void startShortcut(String packageName, String id, Rect sourceBounds,
+ Bundle startActivityOptions, UserHandle user) {
+ if (GO_DISABLE_WIDGETS) {
+ return;
+ }
+ try {
+ ((Context) this).getSystemService(LauncherApps.class).startShortcut(packageName, id,
+ sourceBounds, startActivityOptions, user);
+ } catch (SecurityException | IllegalStateException e) {
+ Log.e(TAG, "Failed to start shortcut", e);
+ }
+ }
+
+ /**
+ * Invoked when a shortcut fails to launch.
+ * @param intent Shortcut intent that failed to start.
+ * @param info Shortcut information.
+ * @return {@code true} if the error is handled by this callback.
+ */
+ default boolean onErrorStartingShortcut(Intent intent, ItemInfo info) {
+ return false;
+ }
+
/**
* Returns the ActivityContext associated with the given Context, or throws an exception if
* the Context is not associated with any ActivityContext.
@@ -176,30 +452,4 @@
return null;
}
}
-
- default View.OnClickListener getItemOnClickListener() {
- return v -> {
- // No op.
- };
- }
-
- @Nullable
- default PopupDataProvider getPopupDataProvider() {
- return null;
- }
-
- @Nullable
- default StringCache getStringCache() {
- return null;
- }
-
- /**
- * Creates and returns {@link SearchAdapterProvider} for build variant specific search result
- * views.
- */
- @Nullable
- default SearchAdapterProvider<?> createSearchAdapterProvider(
- ActivityAllAppsContainerView<?> appsView) {
- return null;
- }
}
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/AppLauncher.java b/src/com/android/launcher3/views/AppLauncher.java
deleted file mode 100644
index 19e66ab..0000000
--- a/src/com/android/launcher3/views/AppLauncher.java
+++ /dev/null
@@ -1,213 +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 static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
-import static com.android.launcher3.model.WidgetsModel.GO_DISABLE_WIDGETS;
-
-import android.app.ActivityOptions;
-import android.content.ActivityNotFoundException;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.LauncherApps;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.os.Process;
-import android.os.StrictMode;
-import android.os.UserHandle;
-import android.util.Log;
-import android.view.Display;
-import android.view.View;
-import android.widget.Toast;
-
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.logging.InstanceId;
-import com.android.launcher3.logging.InstanceIdSequence;
-import com.android.launcher3.logging.StatsLogManager;
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.model.data.WorkspaceItemInfo;
-import com.android.launcher3.util.ActivityOptionsWrapper;
-import com.android.launcher3.util.PackageManagerHelper;
-import com.android.launcher3.util.RunnableList;
-
-/** An {@link ActivityContext} that can also launch app activities and shortcuts safely. */
-public interface AppLauncher extends ActivityContext {
-
- String TAG = "AppLauncher";
-
- /**
- * Safely starts an activity.
- *
- * @param v View starting the activity.
- * @param intent Base intent being launched.
- * @param item Item associated with the view.
- * @return {@code true} if the activity starts successfully.
- */
- default boolean startActivitySafely(
- View v, Intent intent, @Nullable ItemInfo item) {
-
- Context context = (Context) this;
- if (isAppBlockedForSafeMode() && !PackageManagerHelper.isSystemApp(context, intent)) {
- Toast.makeText(context, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();
- return false;
- }
-
- Bundle optsBundle = (v != null) ? getActivityLaunchOptions(v, item).toBundle() : null;
- UserHandle user = item == null ? null : item.user;
-
- // Prepare intent
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- if (v != null) {
- intent.setSourceBounds(Utilities.getViewBounds(v));
- }
- try {
- boolean isShortcut = (item instanceof WorkspaceItemInfo)
- && (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT
- || item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT)
- && !((WorkspaceItemInfo) item).isPromise();
- if (isShortcut) {
- // Shortcuts need some special checks due to legacy reasons.
- startShortcutIntentSafely(intent, optsBundle, item);
- } else if (user == null || user.equals(Process.myUserHandle())) {
- // Could be launching some bookkeeping activity
- context.startActivity(intent, optsBundle);
- } else {
- context.getSystemService(LauncherApps.class).startMainActivity(
- intent.getComponent(), user, intent.getSourceBounds(), optsBundle);
- }
- if (item != null) {
- InstanceId instanceId = new InstanceIdSequence().newInstanceId();
- logAppLaunch(getStatsLogManager(), item, instanceId);
- }
- return true;
- } catch (NullPointerException | ActivityNotFoundException | SecurityException e) {
- Toast.makeText(context, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
- Log.e(TAG, "Unable to launch. tag=" + item + " intent=" + intent, e);
- }
- return false;
- }
-
- /** Returns {@code true} if an app launch is blocked due to safe mode. */
- default boolean isAppBlockedForSafeMode() {
- return false;
- }
-
- /**
- * Creates and logs a new app launch event.
- */
- default void logAppLaunch(StatsLogManager statsLogManager, ItemInfo info,
- InstanceId instanceId) {
- statsLogManager.logger().withItemInfo(info).withInstanceId(instanceId)
- .log(LAUNCHER_APP_LAUNCH_TAP);
- }
-
- /**
- * Returns launch options for an Activity.
- *
- * @param v View initiating a launch.
- * @param item Item associated with the view.
- */
- default ActivityOptionsWrapper getActivityLaunchOptions(View v, @Nullable ItemInfo item) {
- int left = 0, top = 0;
- int width = v.getMeasuredWidth(), height = v.getMeasuredHeight();
- if (v instanceof BubbleTextView) {
- // Launch from center of icon, not entire view
- Drawable icon = ((BubbleTextView) v).getIcon();
- if (icon != null) {
- Rect bounds = icon.getBounds();
- left = (width - bounds.width()) / 2;
- top = v.getPaddingTop();
- width = bounds.width();
- height = bounds.height();
- }
- }
- ActivityOptions options =
- ActivityOptions.makeClipRevealAnimation(v, left, top, width, height);
-
- options.setLaunchDisplayId(
- (v != null && v.getDisplay() != null) ? v.getDisplay().getDisplayId()
- : Display.DEFAULT_DISPLAY);
- RunnableList callback = new RunnableList();
- return new ActivityOptionsWrapper(options, callback);
- }
-
- /**
- * Safely launches an intent for a shortcut.
- *
- * @param intent Intent to start.
- * @param optsBundle Optional launch arguments.
- * @param info Shortcut information.
- */
- default void startShortcutIntentSafely(Intent intent, Bundle optsBundle, ItemInfo info) {
- try {
- StrictMode.VmPolicy oldPolicy = StrictMode.getVmPolicy();
- try {
- // Temporarily disable deathPenalty on all default checks. For eg, shortcuts
- // containing file Uri's would cause a crash as penaltyDeathOnFileUriExposure
- // is enabled by default on NYC.
- StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll()
- .penaltyLog().build());
-
- if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
- String id = ((WorkspaceItemInfo) info).getDeepShortcutId();
- String packageName = intent.getPackage();
- startShortcut(packageName, id, intent.getSourceBounds(), optsBundle, info.user);
- } else {
- // Could be launching some bookkeeping activity
- ((Context) this).startActivity(intent, optsBundle);
- }
- } finally {
- StrictMode.setVmPolicy(oldPolicy);
- }
- } catch (SecurityException e) {
- if (!onErrorStartingShortcut(intent, info)) {
- throw e;
- }
- }
- }
-
- /**
- * A wrapper around the platform method with Launcher specific checks.
- */
- default void startShortcut(String packageName, String id, Rect sourceBounds,
- Bundle startActivityOptions, UserHandle user) {
- if (GO_DISABLE_WIDGETS) {
- return;
- }
- try {
- ((Context) this).getSystemService(LauncherApps.class).startShortcut(packageName, id,
- sourceBounds, startActivityOptions, user);
- } catch (SecurityException | IllegalStateException e) {
- Log.e(TAG, "Failed to start shortcut", e);
- }
- }
-
- /**
- * Invoked when a shortcut fails to launch.
- * @param intent Shortcut intent that failed to start.
- * @param info Shortcut information.
- * @return {@code true} if the error is handled by this callback.
- */
- default boolean onErrorStartingShortcut(Intent intent, ItemInfo info) {
- return false;
- }
-}
diff --git a/src/com/android/launcher3/views/ArrowTipView.java b/src/com/android/launcher3/views/ArrowTipView.java
index 8d16a8d..73c5ad4 100644
--- a/src/com/android/launcher3/views/ArrowTipView.java
+++ b/src/com/android/launcher3/views/ArrowTipView.java
@@ -162,6 +162,7 @@
params.gravity = gravity;
params.leftMargin = mArrowMinOffset + grid.getInsets().left;
params.rightMargin = mArrowMinOffset + grid.getInsets().right;
+ params.width = LayoutParams.MATCH_PARENT;
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mArrowView.getLayoutParams();
lp.gravity = gravity;
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..5b57e22 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;
@@ -134,14 +139,13 @@
mTargetRect.roundOut(outPos);
}
- public static OptionsPopupView show(
- Launcher launcher, RectF targetRect, List<OptionItem> items, boolean shouldAddArrow) {
+ public static OptionsPopupView show(ActivityContext launcher, RectF targetRect,
+ List<OptionItem> items, boolean shouldAddArrow) {
return show(launcher, targetRect, items, shouldAddArrow, 0 /* width */);
}
- public static OptionsPopupView show(
- Launcher launcher, RectF targetRect, List<OptionItem> items, boolean shouldAddArrow,
- int width) {
+ public static OptionsPopupView show(ActivityContext launcher, RectF targetRect,
+ List<OptionItem> items, boolean shouldAddArrow, int width) {
OptionsPopupView popup = (OptionsPopupView) launcher.getLayoutInflater()
.inflate(R.layout.longpress_options_menu, launcher.getDragLayer(), false);
popup.mTargetRect = targetRect;
@@ -160,30 +164,20 @@
popup.mItemMap.put(view, item);
}
- popup.addPreDrawForColorExtraction(launcher);
popup.show();
return popup;
}
- @Override
- protected List<View> getChildrenForColorExtraction() {
- int childCount = getChildCount();
- ArrayList<View> children = new ArrayList<>(childCount);
- for (int i = 0; i < childCount; ++i) {
- children.add(getChildAt(i));
- }
- return children;
- }
-
/**
* Returns the list of supported actions
*/
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 +245,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 +293,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 11ca130..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;
@@ -174,6 +171,11 @@
ta.recycle();
}
+ /** @return whether there is a RecyclerView bound to this scroller. */
+ public boolean hasRecyclerView() {
+ return mRv != null;
+ }
+
public void setRecyclerView(FastScrollRecyclerView rv, TextView popupView) {
if (mRv != null && mOnScrollListener != null) {
mRv.removeOnScrollListener(mOnScrollListener);
@@ -204,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() {
@@ -308,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/views/ScrimView.java b/src/com/android/launcher3/views/ScrimView.java
index 4c0bfde..870ff12 100644
--- a/src/com/android/launcher3/views/ScrimView.java
+++ b/src/com/android/launcher3/views/ScrimView.java
@@ -46,6 +46,7 @@
private int mBackgroundColor;
private boolean mIsVisible = true;
private boolean mLastDispatchedOpaqueness;
+ private float mHeaderScale = 1f;
public ScrimView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -91,7 +92,16 @@
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mDrawingController != null) {
- mDrawingController.drawOnScrim(canvas);
+ mDrawingController.drawOnScrimWithScale(canvas, mHeaderScale);
+ }
+ }
+
+ /** Set scrim header's scale and bottom offset. */
+ public void setScrimHeaderScale(float scale) {
+ boolean hasChanged = mHeaderScale != scale;
+ mHeaderScale = scale;
+ if (hasChanged) {
+ invalidate();
}
}
@@ -176,6 +186,6 @@
/**
* Called inside ScrimView#OnDraw
*/
- void drawOnScrim(Canvas canvas);
+ void drawOnScrimWithScale(Canvas canvas, float scale);
}
}
diff --git a/src/com/android/launcher3/views/Snackbar.java b/src/com/android/launcher3/views/Snackbar.java
index e582114..86b3419 100644
--- a/src/com/android/launcher3/views/Snackbar.java
+++ b/src/com/android/launcher3/views/Snackbar.java
@@ -31,6 +31,7 @@
import androidx.annotation.Nullable;
import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.compat.AccessibilityManagerCompat;
@@ -97,7 +98,11 @@
dragLayer.getWidth() - maxMarginLeftRight * 2 - insets.left - insets.right,
absoluteMaxWidth);
params.width = minWidth;
- params.setMargins(0, 0, 0, marginBottom + insets.bottom);
+ DeviceProfile deviceProfile = activity.getDeviceProfile();
+ params.setMargins(0, 0, 0, marginBottom
+ + (deviceProfile.isTaskbarPresent
+ ? deviceProfile.taskbarSize + deviceProfile.getTaskbarOffsetY()
+ : insets.bottom));
TextView labelView = snackbar.findViewById(R.id.label);
String labelText = res.getString(labelStringResId);
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..d7235ad
--- /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.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.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.util.ResourceBasedOverride;
+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;
+
+ protected 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;
+ }
+
+ /**
+ * 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();
+ }
+ }
+
+ /**
+ * Returns the new LauncherWidgetHolder instance
+ */
+ public static LauncherWidgetHolder newInstance(Context context) {
+ return HolderFactory.newFactory(context).newInstance(context, null);
+ }
+
+ /**
+ * A factory class that generates new instances of {@code LauncherWidgetHolder}
+ */
+ public static class HolderFactory implements ResourceBasedOverride {
+
+ /**
+ * @param context The context of the caller
+ * @param appWidgetRemovedCallback The callback that is called when widgets are removed
+ * @return A new instance of {@code LauncherWidgetHolder}
+ */
+ public LauncherWidgetHolder newInstance(@NonNull Context context,
+ @Nullable IntConsumer appWidgetRemovedCallback) {
+ return new LauncherWidgetHolder(context, appWidgetRemovedCallback);
+ }
+
+ /**
+ * @param context The context of the caller
+ * @return A new instance of factory class for widget holders. If not specified, returning
+ * {@code HolderFactory} by default.
+ */
+ public static HolderFactory newFactory(Context context) {
+ return Overrides.getObject(
+ HolderFactory.class, context, R.string.widget_holder_factory_class);
+ }
+ }
+}
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..38cdb4b 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;
@@ -170,6 +169,7 @@
private View mSearchBarContainer;
private WidgetsSearchBar mSearchBar;
private TextView mHeaderTitle;
+ private @Nullable WidgetsRecyclerView mCurrentTouchEventRecyclerView;
public WidgetsFullSheet(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
@@ -322,7 +322,7 @@
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- mActivityContext.getAppWidgetHost().addProviderChangeListener(this);
+ mActivityContext.getAppWidgetHolder().addProviderChangeListener(this);
notifyWidgetProvidersChanged();
onRecommendedWidgetsBound();
}
@@ -330,7 +330,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 +582,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 +603,7 @@
@Override
protected void handleClose(boolean animate) {
- handleClose(animate, DEFAULT_OPEN_DURATION);
+ handleClose(animate, mActivityContext.getDeviceProfile().bottomSheetCloseDuration);
}
@Override
@@ -616,12 +616,13 @@
// Disable swipe down when recycler view is scrolling
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
mNoIntercept = false;
- RecyclerViewFastScroller scroller = getRecyclerView().getScrollbar();
+ WidgetsRecyclerView recyclerView = getRecyclerView();
+ RecyclerViewFastScroller scroller = recyclerView.getScrollbar();
if (scroller.getThumbOffsetY() >= 0
&& getPopupContainer().isEventOverView(scroller, ev)) {
mNoIntercept = true;
- } else if (getPopupContainer().isEventOverView(mContent, ev)) {
- mNoIntercept = !getRecyclerView().shouldContainerScroll(ev, getPopupContainer());
+ } else if (getPopupContainer().isEventOverView(recyclerView, ev)) {
+ mNoIntercept = !recyclerView.shouldContainerScroll(ev, getPopupContainer());
}
if (mSearchBar.isSearchBarFocused()
@@ -642,6 +643,51 @@
return sheet;
}
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ return isTouchOnScrollbar(ev) || super.onInterceptTouchEvent(ev);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ return maybeHandleTouchEvent(ev) || super.onTouchEvent(ev);
+ }
+
+ private boolean maybeHandleTouchEvent(MotionEvent ev) {
+ boolean isEventHandled = false;
+
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ mCurrentTouchEventRecyclerView = isTouchOnScrollbar(ev) ? getRecyclerView() : null;
+ }
+
+ if (mCurrentTouchEventRecyclerView != null) {
+ final float offsetX = mContent.getX();
+ final float offsetY = mContent.getY();
+ ev.offsetLocation(-offsetX, -offsetY);
+ isEventHandled = mCurrentTouchEventRecyclerView.dispatchTouchEvent(ev);
+ ev.offsetLocation(offsetX, offsetY);
+ }
+
+ if (ev.getAction() == MotionEvent.ACTION_UP
+ || ev.getAction() == MotionEvent.ACTION_CANCEL) {
+ mCurrentTouchEventRecyclerView = null;
+ }
+
+ return isEventHandled;
+ }
+
+ private boolean isTouchOnScrollbar(MotionEvent ev) {
+ final float offsetX = mContent.getX();
+ final float offsetY = mContent.getY();
+ WidgetsRecyclerView rv = getRecyclerView();
+
+ ev.offsetLocation(-offsetX, -offsetY);
+ boolean isOnScrollBar = rv != null && rv.getScrollbar() != null && rv.isHitOnScrollBar(ev);
+ ev.offsetLocation(offsetX, offsetY);
+
+ return isOnScrollBar;
+ }
+
/** Gets the {@link WidgetsRecyclerView} which shows all widgets in {@link WidgetsFullSheet}. */
@VisibleForTesting
public static WidgetsRecyclerView getWidgetsView(Launcher launcher) {
diff --git a/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
index 35fa7a4..698e764 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;
@@ -137,8 +127,7 @@
@Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
if (e.getAction() == MotionEvent.ACTION_DOWN) {
- mTouchDownOnScroller =
- mScrollbar.isHitInParent(e.getX(), e.getY(), mFastScrollerOffset);
+ mTouchDownOnScroller = isHitOnScrollBar(e);
}
if (mTouchDownOnScroller) {
final boolean result = mScrollbar.handleTouchEvent(e, mFastScrollerOffset);
@@ -154,6 +143,15 @@
}
}
+ /**
+ * Detects whether a {@code MotionEvent} is on the scroll bar
+ * @param e The {@code MotionEvent} on the screen
+ * @return {@code true} if the motion is on the scroll bar
+ */
+ boolean isHitOnScrollBar(MotionEvent e) {
+ return mScrollbar.isHitInParent(e.getX(), e.getY(), mFastScrollerOffset);
+ }
+
@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
@@ -164,39 +162,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/com/android/launcher3/workprofile/PersonalWorkSlidingTabStrip.java b/src/com/android/launcher3/workprofile/PersonalWorkSlidingTabStrip.java
index 49db2a0..e94f3a0 100644
--- a/src/com/android/launcher3/workprofile/PersonalWorkSlidingTabStrip.java
+++ b/src/com/android/launcher3/workprofile/PersonalWorkSlidingTabStrip.java
@@ -16,6 +16,7 @@
package com.android.launcher3.workprofile;
import android.content.Context;
+import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.widget.Button;
import android.widget.LinearLayout;
@@ -24,6 +25,7 @@
import androidx.annotation.Nullable;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.R;
import com.android.launcher3.pageindicators.PageIndicator;
import com.android.launcher3.views.ActivityContext;
@@ -31,11 +33,17 @@
* Supports two indicator colors, dedicated for personal and work tabs.
*/
public class PersonalWorkSlidingTabStrip extends LinearLayout implements PageIndicator {
+ private final boolean mIsAlignOnIcon;
private OnActivePageChangedListener mOnActivePageChangedListener;
private int mLastActivePage = 0;
public PersonalWorkSlidingTabStrip(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
+ TypedArray typedArray = context.obtainStyledAttributes(attrs,
+ R.styleable.PersonalWorkSlidingTabStrip);
+ mIsAlignOnIcon = typedArray.getBoolean(
+ R.styleable.PersonalWorkSlidingTabStrip_alignOnIcon, false);
+ typedArray.recycle();
}
/**
@@ -76,7 +84,7 @@
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- if (getPaddingLeft() == 0 && getPaddingRight() == 0) {
+ if (mIsAlignOnIcon) {
// If any padding is not specified, restrict the width to emulate padding
int size = MeasureSpec.getSize(widthMeasureSpec);
size = getTabWidth(getContext(), size);
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_shortcuts_overrides/com/android/launcher3/model/LoaderResults.java b/src_shortcuts_overrides/com/android/launcher3/model/LauncherBinder.java
similarity index 88%
rename from src_shortcuts_overrides/com/android/launcher3/model/LoaderResults.java
rename to src_shortcuts_overrides/com/android/launcher3/model/LauncherBinder.java
index abce2a2..e1a5f24 100644
--- a/src_shortcuts_overrides/com/android/launcher3/model/LoaderResults.java
+++ b/src_shortcuts_overrides/com/android/launcher3/model/LauncherBinder.java
@@ -27,11 +27,11 @@
import java.util.List;
/**
- * Helper class to handle results of {@link com.android.launcher3.model.LoaderTask}.
+ * Binds the results of {@link com.android.launcher3.model.LoaderTask} to the Callbacks objects.
*/
-public class LoaderResults extends BaseLoaderResults {
+public class LauncherBinder extends BaseLauncherBinder {
- public LoaderResults(LauncherAppState app, BgDataModel dataModel,
+ public LauncherBinder(LauncherAppState app, BgDataModel dataModel,
AllAppsList allAppsList, Callbacks[] callbacks) {
super(app, dataModel, allAppsList, callbacks, MAIN_EXECUTOR);
}
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java b/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java
index c8b5e2f..47bf135 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java
@@ -21,6 +21,9 @@
import com.android.launcher3.Utilities;
+/**
+ * A wrapper for the hidden API calls
+ */
public class ApiWrapper {
public static final boolean TASKBAR_DRAWN_IN_PROCESS = false;
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..772a995 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -20,11 +20,11 @@
import android.content.Context;
-import com.android.launcher3.DeviceProfile.DeviceProfileListenable;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
import com.android.launcher3.util.Themes;
+import com.android.launcher3.views.ActivityContext;
/**
* Definition for AllApps state
@@ -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;
@@ -41,11 +40,11 @@
}
@Override
- public <DEVICE_PROFILE_CONTEXT extends Context & DeviceProfileListenable>
+ public <DEVICE_PROFILE_CONTEXT extends Context & ActivityContext>
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 54cded0..39bd307 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -24,6 +24,18 @@
"src/**/*.java",
"src/**/*.kt"
],
+ exclude_srcs: [
+ ":launcher-non-quickstep-tests-src"
+ ],
+}
+
+// Source code used for non-quickstep tests
+filegroup {
+ name: "launcher-non-quickstep-tests-src",
+ srcs: [
+ "src/com/android/launcher3/nonquickstep/**/*.java",
+ "src/com/android/launcher3/nonquickstep/**/*.kt",
+ ],
}
// Source code used for oop test helpers
@@ -48,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",
],
}
@@ -84,6 +97,7 @@
name: "Launcher3Tests",
srcs: [
":launcher-tests-src",
+ ":launcher-non-quickstep-tests-src",
],
static_libs: ["Launcher3TestLib"],
libs: [
diff --git a/tests/AndroidManifest-common.xml b/tests/AndroidManifest-common.xml
index 9cc3aed..b170061 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">
@@ -268,7 +280,7 @@
</intent-filter>
</activity-alias>
<activity-alias android:name="Activity15" android:exported="true"
- android:label="ThemeIconTestActivity"
+ android:label="IconThemedActivity"
android:icon="@drawable/test_theme_icon"
android:targetActivity="com.android.launcher3.testcomponent.OtherBaseTestingActivity">
<intent-filter>
@@ -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/OWNERS b/tests/OWNERS
index 02e8ebc..6b8643c 100644
--- a/tests/OWNERS
+++ b/tests/OWNERS
@@ -1,3 +1,4 @@
+# Bug component: 164335
vadimt@google.com
sunnygoyal@google.com
winsonc@google.com
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/res/xml/shortcuts.xml b/tests/res/xml/shortcuts.xml
index bdc22f9..94e8edd 100644
--- a/tests/res/xml/shortcuts.xml
+++ b/tests/res/xml/shortcuts.xml
@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
<shortcut
- android:shortcutId="shortcut1"
+ android:shortcutId="shortcut1_themed"
+ android:icon="@drawable/test_theme_icon"
android:shortcutShortLabel="@string/shortcut1">
<intent android:action="com.android.launcher3.intent.action.test_shortcut"/>
</shortcut>
diff --git a/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt b/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt
index 7465db5..2da5eee 100644
--- a/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt
+++ b/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt
@@ -17,14 +17,17 @@
import android.content.Context
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.util.DisplayController.Info
import com.android.launcher3.util.WindowBounds
+import java.io.PrintWriter
+import java.io.StringWriter
import org.junit.Before
import org.mockito.ArgumentMatchers.any
import org.mockito.Mockito.mock
-import java.io.PrintWriter
-import java.io.StringWriter
import org.mockito.Mockito.`when` as whenever
abstract class DeviceProfileBaseTest {
@@ -46,126 +49,265 @@
isGestureMode = true
}
- protected fun newDP(): DeviceProfile = DeviceProfile(
- context,
- inv,
- info,
- windowBounds,
- isMultiWindowMode,
- transposeLayoutWithOrientation,
- useTwoPanels,
- isGestureMode
- )
+ protected fun newDP(): DeviceProfile =
+ DeviceProfile(
+ context,
+ inv,
+ info,
+ windowBounds,
+ SparseArray(),
+ isMultiWindowMode,
+ transposeLayoutWithOrientation,
+ useTwoPanels,
+ isGestureMode,
+ DEFAULT_PROVIDER
+ )
- protected fun initializeVarsForPhone(isGestureMode: Boolean = true,
- isVerticalBar: Boolean = false) {
- val (x, y) = if (isVerticalBar)
- Pair(3120, 1440)
- else
- Pair(1440, 3120)
+ protected fun initializeVarsForPhone(
+ isGestureMode: Boolean = true,
+ isVerticalBar: Boolean = false
+ ) {
+ val (x, y) = if (isVerticalBar) Pair(2400, 1080) else Pair(1080, 2400)
- windowBounds = WindowBounds(x, y, x, y - 100, 0)
+ windowBounds =
+ WindowBounds(
+ Rect(0, 0, x, y),
+ Rect(
+ if (isVerticalBar) 118 else 0,
+ if (isVerticalBar) 74 else 118,
+ if (!isGestureMode && isVerticalBar) 126 else 0,
+ if (isGestureMode) 63 else if (isVerticalBar) 0 else 126
+ )
+ )
whenever(info.isTablet(any())).thenReturn(false)
- whenever(info.getDensityDpi()).thenReturn(560)
+ whenever(info.getDensityDpi()).thenReturn(420)
whenever(info.smallestSizeDp(any())).thenReturn(411f)
this.isGestureMode = isGestureMode
+ transposeLayoutWithOrientation = true
- inv = newScalableInvariantDeviceProfile().apply {
- deviceType = InvariantDeviceProfile.TYPE_PHONE
- transposeLayoutWithOrientation = isVerticalBar
- }
+ inv =
+ InvariantDeviceProfile().apply {
+ numRows = 5
+ numColumns = 4
+ numSearchContainerColumns = 4
+
+ iconSize = floatArrayOf(60f, 54f, 60f, 60f)
+ iconTextSize = FloatArray(4) { 14f }
+ deviceType = InvariantDeviceProfile.TYPE_PHONE
+
+ minCellSize =
+ listOf(
+ PointF(80f, 104f),
+ PointF(80f, 104f),
+ PointF(80f, 104f),
+ PointF(80f, 104f)
+ )
+ .toTypedArray()
+
+ borderSpaces =
+ listOf(PointF(16f, 16f), PointF(16f, 16f), PointF(16f, 16f), PointF(16f, 16f))
+ .toTypedArray()
+
+ numFolderRows = 3
+ numFolderColumns = 3
+ folderStyle = R.style.FolderDefaultStyle
+
+ inlineNavButtonsEndSpacing = R.dimen.taskbar_button_margin_split
+
+ horizontalMargin = FloatArray(4) { 22f }
+
+ allAppsCellSize =
+ listOf(
+ PointF(80f, 104f),
+ PointF(80f, 104f),
+ PointF(80f, 104f),
+ PointF(80f, 104f)
+ )
+ .toTypedArray()
+ allAppsIconSize = floatArrayOf(60f, 60f, 60f, 60f)
+ allAppsIconTextSize = FloatArray(4) { 14f }
+ allAppsBorderSpaces =
+ listOf(PointF(16f, 16f), PointF(16f, 16f), PointF(16f, 16f), PointF(16f, 16f))
+ .toTypedArray()
+
+ numShownHotseatIcons = 4
+
+ numDatabaseHotseatIcons = 4
+
+ hotseatColumnSpan = IntArray(4) { 4 }
+ hotseatBarBottomSpace = FloatArray(4) { 48f }
+ hotseatQsbSpace = FloatArray(4) { 36f }
+
+ numAllAppsColumns = 4
+
+ isScalable = true
+
+ inlineQsb = BooleanArray(4) { false }
+
+ devicePaddingId = R.xml.paddings_handhelds
+ }
}
- protected fun initializeVarsForTablet(isLandscape: Boolean = false,
- isTwoPanel: Boolean = false,
- isGestureMode: Boolean = true) {
- val (x, y) = if (isLandscape)
- Pair(2560, 1600)
- else
- Pair(1600, 2560)
+ protected fun initializeVarsForTablet(
+ isLandscape: Boolean = false,
+ isGestureMode: Boolean = true
+ ) {
+ val (x, y) = if (isLandscape) Pair(2560, 1600) else Pair(1600, 2560)
- windowBounds = WindowBounds(x, y, x, y - 100, 0)
+ windowBounds = WindowBounds(Rect(0, 0, x, y), Rect(0, 104, 0, 0))
whenever(info.isTablet(any())).thenReturn(true)
whenever(info.getDensityDpi()).thenReturn(320)
whenever(info.smallestSizeDp(any())).thenReturn(800f)
this.isGestureMode = isGestureMode
- useTwoPanels = isTwoPanel
+ useTwoPanels = false
- inv = newScalableInvariantDeviceProfile().apply {
- deviceType = if (isTwoPanel)
- InvariantDeviceProfile.TYPE_MULTI_DISPLAY else InvariantDeviceProfile.TYPE_TABLET
- inlineQsb = booleanArrayOf(
- false,
- isLandscape,
- false,
- false
- )
- }
+ inv =
+ InvariantDeviceProfile().apply {
+ numRows = 5
+ numColumns = 6
+ numSearchContainerColumns = 3
+
+ iconSize = FloatArray(4) { 60f }
+ iconTextSize = FloatArray(4) { 14f }
+ deviceType = InvariantDeviceProfile.TYPE_TABLET
+
+ minCellSize =
+ listOf(
+ PointF(102f, 120f),
+ PointF(120f, 104f),
+ PointF(102f, 120f),
+ PointF(102f, 120f)
+ )
+ .toTypedArray()
+
+ borderSpaces =
+ listOf(PointF(16f, 64f), PointF(64f, 16f), 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)
+
+ allAppsCellSize =
+ listOf(
+ PointF(96f, 142f),
+ PointF(126f, 126f),
+ PointF(96f, 142f),
+ PointF(96f, 142f)
+ )
+ .toTypedArray()
+ allAppsIconSize = FloatArray(4) { 60f }
+ allAppsIconTextSize = FloatArray(4) { 14f }
+ allAppsBorderSpaces =
+ listOf(PointF(8f, 16f), PointF(16f, 16f), PointF(8f, 16f), PointF(8f, 16f))
+ .toTypedArray()
+
+ numShownHotseatIcons = 6
+
+ numDatabaseHotseatIcons = 6
+
+ hotseatColumnSpan = intArrayOf(6, 4, 6, 6)
+ hotseatBarBottomSpace = floatArrayOf(36f, 40f, 36f, 36f)
+ hotseatQsbSpace = FloatArray(4) { 32f }
+
+ numAllAppsColumns = 6
+
+ isScalable = true
+ devicePaddingId = R.xml.paddings_6x5
+
+ inlineQsb = booleanArrayOf(false, true, false, false)
+
+ devicePaddingId = R.xml.paddings_handhelds
+ }
}
- /**
- * A very generic grid, just to make qsb tests work. For real calculations, make sure to use
- * values that better represent a real grid.
- */
- protected fun newScalableInvariantDeviceProfile(): InvariantDeviceProfile =
- InvariantDeviceProfile().apply {
- isScalable = true
- numColumns = 4
- numRows = 4
- numShownHotseatIcons = 4
- numDatabaseHotseatIcons = 6
- numShrunkenHotseatIcons = 5
- horizontalMargin = FloatArray(4) { 22f }
- borderSpaces = listOf(
- PointF(16f, 16f),
- PointF(16f, 16f),
- PointF(16f, 16f),
- PointF(16f, 16f)
- ).toTypedArray()
- allAppsBorderSpaces = listOf(
- PointF(16f, 16f),
- PointF(16f, 16f),
- PointF(16f, 16f),
- PointF(16f, 16f)
- ).toTypedArray()
- hotseatBorderSpaces = FloatArray(4) { 16f }
- hotseatColumnSpan = IntArray(4) { 4 }
- hotseatBarBottomSpace = FloatArray(4) { 48f }
- inlineNavButtonsEndSpacing = R.dimen.taskbar_button_margin_4_4
- hotseatQsbSpace = FloatArray(4) { 36f }
- iconSize = FloatArray(4) { 56f }
- allAppsIconSize = FloatArray(4) { 56f }
- iconTextSize = FloatArray(4) { 14f }
- allAppsIconTextSize = FloatArray(4) { 14f }
- minCellSize = listOf(
- PointF(64f, 83f),
- PointF(64f, 83f),
- PointF(64f, 83f),
- PointF(64f, 83f)
- ).toTypedArray()
- allAppsCellSize = listOf(
- PointF(64f, 83f),
- PointF(64f, 83f),
- PointF(64f, 83f),
- PointF(64f, 83f)
- ).toTypedArray()
- inlineQsb = booleanArrayOf(
- false,
- false,
- false,
- false
- )
- }
+ protected fun initializeVarsForTwoPanel(
+ isLandscape: Boolean = false,
+ isGestureMode: Boolean = true
+ ) {
+ val (x, y) = if (isLandscape) Pair(2208, 1840) else Pair(1840, 2208)
- fun dump(dp: DeviceProfile): StringWriter {
+ windowBounds = WindowBounds(Rect(0, 0, x, y), Rect(0, 110, 0, 0))
+
+ whenever(info.isTablet(any())).thenReturn(true)
+ whenever(info.getDensityDpi()).thenReturn(420)
+ whenever(info.smallestSizeDp(any())).thenReturn(700f)
+
+ this.isGestureMode = isGestureMode
+ useTwoPanels = true
+
+ inv =
+ InvariantDeviceProfile().apply {
+ numRows = 4
+ numColumns = 4
+ numSearchContainerColumns = 4
+
+ iconSize = floatArrayOf(60f, 52f, 52f, 60f)
+ iconTextSize = floatArrayOf(14f, 14f, 12f, 14f)
+ deviceType = InvariantDeviceProfile.TYPE_MULTI_DISPLAY
+
+ minCellSize =
+ listOf(
+ PointF(80f, 104f),
+ PointF(80f, 104f),
+ PointF(68f, 116f),
+ PointF(80f, 102f)
+ )
+ .toTypedArray()
+
+ borderSpaces =
+ listOf(PointF(16f, 16f), PointF(16f, 16f), PointF(16f, 20f), PointF(20f, 20f))
+ .toTypedArray()
+
+ numFolderRows = 3
+ numFolderColumns = 3
+ folderStyle = R.style.FolderDefaultStyle
+
+ inlineNavButtonsEndSpacing = R.dimen.taskbar_button_margin_split
+
+ horizontalMargin = floatArrayOf(21.5f, 21.5f, 22.5f, 30.5f)
+
+ allAppsCellSize =
+ listOf(PointF(0f, 0f), PointF(0f, 0f), PointF(68f, 104f), PointF(80f, 104f))
+ .toTypedArray()
+ allAppsIconSize = floatArrayOf(60f, 60f, 52f, 60f)
+ allAppsIconTextSize = floatArrayOf(14f, 14f, 12f, 14f)
+ allAppsBorderSpaces =
+ listOf(PointF(16f, 16f), PointF(16f, 16f), PointF(16f, 28f), PointF(20f, 16f))
+ .toTypedArray()
+
+ numShownHotseatIcons = 6
+
+ numDatabaseHotseatIcons = 6
+
+ hotseatColumnSpan = IntArray(4) { 6 }
+ hotseatBarBottomSpace = floatArrayOf(48f, 48f, 36f, 20f)
+ hotseatQsbSpace = floatArrayOf(36f, 36f, 36f, 28f)
+
+ numAllAppsColumns = 6
+ numDatabaseAllAppsColumns = 6
+
+ isScalable = true
+
+ inlineQsb = booleanArrayOf(false, false, false, false)
+
+ devicePaddingId = R.xml.paddings_handhelds
+ }
+ }
+
+ fun dump(dp: DeviceProfile): String {
val stringWriter = StringWriter()
val printWriter = PrintWriter(stringWriter)
- dp.dump("", printWriter)
+ dp.dump(context, "", printWriter)
printWriter.flush()
- return stringWriter
+ return stringWriter.toString()
}
-}
\ No newline at end of file
+}
diff --git a/tests/src/com/android/launcher3/HotseatShownIconsTest.kt b/tests/src/com/android/launcher3/HotseatShownIconsTest.kt
deleted file mode 100644
index 5dabb33..0000000
--- a/tests/src/com/android/launcher3/HotseatShownIconsTest.kt
+++ /dev/null
@@ -1,185 +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
-
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.launcher3.InvariantDeviceProfile.TYPE_MULTI_DISPLAY
-import com.android.launcher3.InvariantDeviceProfile.TYPE_PHONE
-import com.android.launcher3.InvariantDeviceProfile.TYPE_TABLET
-import com.google.common.truth.Truth.assertThat
-import org.junit.Test
-import org.junit.runner.RunWith
-
-/**
- * Test for [DeviceProfile]
- */
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class HotseatShownIconsTest : DeviceProfileBaseTest() {
-
- @Test
- fun hotseat_size_is_shrunk_if_needed_when_large_screen() {
- initializeVarsForTablet(isLandscape = true)
- inv = newScalableInvariantDeviceProfile().apply {
- deviceType = TYPE_MULTI_DISPLAY
- inlineQsb = booleanArrayOf(
- false,
- false,
- false,
- true // two panels landscape
- )
- }
- useTwoPanels = true
-
- isGestureMode = false
- val dp = newDP()
-
- if (dp.hotseatQsbHeight > 0) {
- assertThat(dp.isQsbInline).isTrue()
- assertThat(dp.numShownHotseatIcons).isEqualTo(5)
- } else { // Launcher3 doesn't have QSB height
- assertThat(dp.isQsbInline).isFalse()
- assertThat(dp.numShownHotseatIcons).isEqualTo(6)
- }
- }
-
- /**
- * For consistency, the hotseat should shrink if any orientation on the device type has an
- * inline qsb
- */
- @Test
- fun hotseat_size_is_shrunk_even_in_portrait_when_large_screen() {
- initializeVarsForTablet()
- inv = newScalableInvariantDeviceProfile().apply {
- deviceType = TYPE_MULTI_DISPLAY
- inlineQsb = booleanArrayOf(
- false,
- false,
- false,
- true // two panels landscape
- )
- }
- useTwoPanels = true
-
- isGestureMode = false
- val dp = newDP()
-
- if (dp.hotseatQsbHeight > 0) {
- assertThat(dp.isQsbInline).isFalse()
- assertThat(dp.numShownHotseatIcons).isEqualTo(5)
- } else { // Launcher3 doesn't have QSB height
- assertThat(dp.isQsbInline).isFalse()
- assertThat(dp.numShownHotseatIcons).isEqualTo(6)
- }
- }
-
- @Test
- fun hotseat_size_is_default_when_small_screen() {
- initializeVarsForPhone()
- inv = newScalableInvariantDeviceProfile().apply {
- deviceType = TYPE_MULTI_DISPLAY
- }
- useTwoPanels = true
-
- val dp = newDP()
-
- assertThat(dp.numShownHotseatIcons).isEqualTo(4)
- }
-
- @Test
- fun hotseat_size_is_not_shrunk_on_gesture_tablet() {
- initializeVarsForTablet(isLandscape = true)
- inv = newScalableInvariantDeviceProfile().apply {
- deviceType = TYPE_TABLET
- inlineQsb = booleanArrayOf(
- false,
- true, // landscape
- false,
- false
- )
- numShownHotseatIcons = 6
- }
-
- isGestureMode = true
- val dp = newDP()
-
- if (dp.hotseatQsbHeight > 0) {
- assertThat(dp.isQsbInline).isTrue()
- assertThat(dp.numShownHotseatIcons).isEqualTo(6)
- } else { // Launcher3 doesn't have QSB height
- assertThat(dp.isQsbInline).isFalse()
- assertThat(dp.numShownHotseatIcons).isEqualTo(6)
- }
- }
-
- @Test
- fun hotseat_size_is_shrunk_if_needed_on_tablet() {
- initializeVarsForTablet(isLandscape = true)
- inv = newScalableInvariantDeviceProfile().apply {
- deviceType = TYPE_TABLET
- inlineQsb = booleanArrayOf(
- false,
- true, // landscape
- false,
- false
- )
- numShownHotseatIcons = 6
- }
-
- isGestureMode = false
- val dp = newDP()
-
- if (dp.hotseatQsbHeight > 0) {
- assertThat(dp.isQsbInline).isTrue()
- assertThat(dp.numShownHotseatIcons).isEqualTo(5)
- } else { // Launcher3 doesn't have QSB height
- assertThat(dp.isQsbInline).isFalse()
- assertThat(dp.numShownHotseatIcons).isEqualTo(6)
- }
- }
-
- /**
- * For consistency, the hotseat should shrink if any orientation on the device type has an
- * inline qsb
- */
- @Test
- fun hotseat_size_is_shrunk_even_in_portrait_on_tablet() {
- initializeVarsForTablet()
- inv = newScalableInvariantDeviceProfile().apply {
- deviceType = TYPE_TABLET
- inlineQsb = booleanArrayOf(
- false,
- true, // landscape
- false,
- false
- )
- numShownHotseatIcons = 6
- }
-
- isGestureMode = false
- val dp = newDP()
-
- if (dp.hotseatQsbHeight > 0) {
- assertThat(dp.isQsbInline).isFalse()
- assertThat(dp.numShownHotseatIcons).isEqualTo(5)
- } else { // Launcher3 doesn't have QSB height
- assertThat(dp.isQsbInline).isFalse()
- assertThat(dp.numShownHotseatIcons).isEqualTo(6)
- }
- }
-
-}
\ No newline at end of file
diff --git a/tests/src/com/android/launcher3/LauncherPrefsTest.kt b/tests/src/com/android/launcher3/LauncherPrefsTest.kt
new file mode 100644
index 0000000..151abf1
--- /dev/null
+++ b/tests/src/com/android/launcher3/LauncherPrefsTest.kt
@@ -0,0 +1,148 @@
+package com.android.launcher3
+
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+import org.junit.Test
+import org.junit.runner.RunWith
+
+private val TEST_BOOLEAN_ITEM = LauncherPrefs.nonRestorableItem("1", false)
+private val TEST_STRING_ITEM = LauncherPrefs.nonRestorableItem("2", "( ͡❛ ͜ʖ ͡❛)")
+private val TEST_INT_ITEM = LauncherPrefs.nonRestorableItem("3", -1)
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class LauncherPrefsTest {
+
+ private val launcherPrefs by lazy {
+ LauncherPrefs.get(InstrumentationRegistry.getInstrumentation().targetContext).apply {
+ remove(TEST_BOOLEAN_ITEM, TEST_STRING_ITEM, TEST_INT_ITEM)
+ }
+ }
+
+ @Test
+ fun has_keyMissingFromLauncherPrefs_returnsFalse() {
+ assertThat(launcherPrefs.has(TEST_BOOLEAN_ITEM)).isFalse()
+ }
+
+ @Test
+ fun has_keyPresentInLauncherPrefs_returnsTrue() {
+ with(launcherPrefs) {
+ putSync(TEST_BOOLEAN_ITEM.to(TEST_BOOLEAN_ITEM.defaultValue))
+ assertThat(has(TEST_BOOLEAN_ITEM)).isTrue()
+ remove(TEST_BOOLEAN_ITEM)
+ }
+ }
+
+ @Test
+ fun addListener_listeningForStringItemUpdates_isCorrectlyNotifiedOfUpdates() {
+ val latch = CountDownLatch(1)
+ val listener = OnSharedPreferenceChangeListener { _, _ -> latch.countDown() }
+
+ with(launcherPrefs) {
+ putSync(TEST_STRING_ITEM.to(TEST_STRING_ITEM.defaultValue))
+ addListener(listener, TEST_STRING_ITEM)
+ putSync(TEST_STRING_ITEM.to(TEST_STRING_ITEM.defaultValue + "abc"))
+
+ assertThat(latch.await(2, TimeUnit.SECONDS)).isTrue()
+ remove(TEST_STRING_ITEM)
+ }
+ }
+
+ @Test
+ fun removeListener_previouslyListeningForStringItemUpdates_isNoLongerNotifiedOfUpdates() {
+ val latch = CountDownLatch(1)
+ val listener = OnSharedPreferenceChangeListener { _, _ -> latch.countDown() }
+
+ with(launcherPrefs) {
+ addListener(listener, TEST_STRING_ITEM)
+ removeListener(listener, TEST_STRING_ITEM)
+ putSync(TEST_STRING_ITEM.to(TEST_STRING_ITEM.defaultValue + "hello."))
+
+ // latch will be still be 1 (and await will return false) if the listener was not called
+ assertThat(latch.await(2, TimeUnit.SECONDS)).isFalse()
+ remove(TEST_STRING_ITEM)
+ }
+ }
+
+ @Test
+ fun addListenerAndRemoveListener_forMultipleItems_bothWorkProperly() {
+ var latch = CountDownLatch(3)
+ val listener = OnSharedPreferenceChangeListener { _, _ -> latch.countDown() }
+
+ with(launcherPrefs) {
+ addListener(listener, TEST_INT_ITEM, TEST_STRING_ITEM, TEST_BOOLEAN_ITEM)
+ putSync(
+ TEST_INT_ITEM.to(TEST_INT_ITEM.defaultValue + 123),
+ TEST_STRING_ITEM.to(TEST_STRING_ITEM.defaultValue + "abc"),
+ TEST_BOOLEAN_ITEM.to(!TEST_BOOLEAN_ITEM.defaultValue)
+ )
+ assertThat(latch.await(2, TimeUnit.SECONDS)).isTrue()
+
+ removeListener(listener, TEST_INT_ITEM, TEST_STRING_ITEM, TEST_BOOLEAN_ITEM)
+ latch = CountDownLatch(1)
+ putSync(
+ TEST_INT_ITEM.to(TEST_INT_ITEM.defaultValue),
+ TEST_STRING_ITEM.to(TEST_STRING_ITEM.defaultValue),
+ TEST_BOOLEAN_ITEM.to(TEST_BOOLEAN_ITEM.defaultValue)
+ )
+ remove(TEST_INT_ITEM, TEST_STRING_ITEM, TEST_BOOLEAN_ITEM)
+
+ assertThat(latch.await(2, TimeUnit.SECONDS)).isFalse()
+ }
+ }
+
+ @Test
+ fun get_booleanItemNotInLauncherprefs_returnsDefaultValue() {
+ assertThat(launcherPrefs.get(TEST_BOOLEAN_ITEM)).isEqualTo(TEST_BOOLEAN_ITEM.defaultValue)
+ }
+
+ @Test
+ fun get_stringItemNotInLauncherPrefs_returnsDefaultValue() {
+ assertThat(launcherPrefs.get(TEST_STRING_ITEM)).isEqualTo(TEST_STRING_ITEM.defaultValue)
+ }
+
+ @Test
+ fun get_intItemNotInLauncherprefs_returnsDefaultValue() {
+ assertThat(launcherPrefs.get(TEST_INT_ITEM)).isEqualTo(TEST_INT_ITEM.defaultValue)
+ }
+
+ @Test
+ fun put_storesItemInLauncherPrefs_successfully() {
+ val notDefaultValue = !TEST_BOOLEAN_ITEM.defaultValue
+
+ with(launcherPrefs) {
+ putSync(TEST_BOOLEAN_ITEM.to(notDefaultValue))
+ assertThat(get(TEST_BOOLEAN_ITEM)).isEqualTo(notDefaultValue)
+ remove(TEST_BOOLEAN_ITEM)
+ }
+ }
+
+ @Test
+ fun put_storesListOfItemsInLauncherPrefs_successfully() {
+ with(launcherPrefs) {
+ putSync(
+ TEST_STRING_ITEM.to(TEST_STRING_ITEM.defaultValue),
+ TEST_INT_ITEM.to(TEST_INT_ITEM.defaultValue),
+ TEST_BOOLEAN_ITEM.to(TEST_BOOLEAN_ITEM.defaultValue)
+ )
+ assertThat(has(TEST_BOOLEAN_ITEM, TEST_INT_ITEM, TEST_STRING_ITEM)).isTrue()
+ remove(TEST_STRING_ITEM, TEST_INT_ITEM, TEST_BOOLEAN_ITEM)
+ }
+ }
+
+ @Test
+ fun remove_deletesItemFromLauncherPrefs_successfully() {
+ val notDefaultValue = !TEST_BOOLEAN_ITEM.defaultValue
+
+ with(launcherPrefs) {
+ putSync(TEST_BOOLEAN_ITEM.to(notDefaultValue))
+ remove(TEST_BOOLEAN_ITEM)
+ assertThat(get(TEST_BOOLEAN_ITEM)).isEqualTo(TEST_BOOLEAN_ITEM.defaultValue)
+ }
+ }
+}
diff --git a/tests/src/com/android/launcher3/celllayout/CellLayoutBoard.java b/tests/src/com/android/launcher3/celllayout/CellLayoutBoard.java
index 3ca05bc..6d75180 100644
--- a/tests/src/com/android/launcher3/celllayout/CellLayoutBoard.java
+++ b/tests/src/com/android/launcher3/celllayout/CellLayoutBoard.java
@@ -26,43 +26,255 @@
import java.util.Map;
import java.util.Queue;
import java.util.Set;
+import java.util.stream.Collectors;
-public class CellLayoutBoard {
+public class CellLayoutBoard implements Comparable<CellLayoutBoard> {
+
+ private boolean intersects(Rect r1, Rect r2) {
+ // If one rectangle is on left side of other
+ if (r1.left > r2.right || r2.left > r1.right) {
+ return false;
+ }
+
+ // If one rectangle is above other
+ if (r1.bottom > r2.top || r2.bottom > r1.top) {
+ return false;
+ }
+
+ return true;
+ }
+
+ private boolean overlapsWithIgnored(Set<Rect> ignoredRectangles, Rect rect) {
+ for (Rect ignoredRect : ignoredRectangles) {
+ // Using the built in intersects doesn't work because it doesn't account for area 0
+ if (intersects(ignoredRect, rect)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public int compareTo(CellLayoutBoard cellLayoutBoard) {
+ // to be equal they need to have the same number of widgets and the same dimensions
+ // their order can be different
+ Set<Rect> widgetsSet = new HashSet<>();
+ Set<Rect> ignoredRectangles = new HashSet<>();
+ for (WidgetRect rect : mWidgetsRects) {
+ if (rect.shouldIgnore()) {
+ ignoredRectangles.add(rect.mBounds);
+ } else {
+ widgetsSet.add(rect.mBounds);
+ }
+ }
+ for (WidgetRect rect : cellLayoutBoard.mWidgetsRects) {
+ // ignore rectangles overlapping with the area marked by x
+ if (overlapsWithIgnored(ignoredRectangles, rect.mBounds)) {
+ continue;
+ }
+ if (!widgetsSet.contains(rect.mBounds)) {
+ return -1;
+ }
+ widgetsSet.remove(rect.mBounds);
+ }
+ if (!widgetsSet.isEmpty()) {
+ return 1;
+ }
+
+ // to be equal they need to have the same number of icons their order can be different
+ Set<Point> iconsSet = new HashSet<>();
+ mIconPoints.forEach(icon -> iconsSet.add(icon.getCoord()));
+ for (IconPoint icon : cellLayoutBoard.mIconPoints) {
+ if (!iconsSet.contains(icon.getCoord())) {
+ return -1;
+ }
+ iconsSet.remove(icon.getCoord());
+ }
+ if (!iconsSet.isEmpty()) {
+ return 1;
+ }
+ return 0;
+ }
+
+ 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();
+ WidgetRect mMain = null;
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 Point getMain() {
+ public List<IconPoint> getIcons() {
+ return mIconPoints;
+ }
+
+ public WidgetRect 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) {
+ private void removeWidgetFromBoard(WidgetRect widget) {
+ for (int xi = widget.mBounds.left; xi < widget.mBounds.right; xi++) {
+ for (int yi = widget.mBounds.top; yi < widget.mBounds.bottom; yi++) {
+ mWidget[xi][yi] = '-';
+ }
+ }
+ }
+
+ private void removeOverlappingItems(Rect rect) {
+ // Remove overlapping widgets and remove them from the board
+ mWidgetsRects = mWidgetsRects.stream().filter(widget -> {
+ if (rect.intersect(widget.mBounds)) {
+ removeWidgetFromBoard(widget);
+ return false;
+ }
+ return true;
+ }).collect(Collectors.toList());
+ // Remove overlapping icons and remove them from the board
+ mIconPoints = mIconPoints.stream().filter(iconPoint -> {
+ int x = iconPoint.coord.x;
+ int y = iconPoint.coord.y;
+ if (rect.contains(x, y)) {
+ mWidget[x][y] = '-';
+ return false;
+ }
+ return true;
+ }).collect(Collectors.toList());
+ }
+
+ private void removeOverlappingItems(Point p) {
+ // Remove overlapping widgets and remove them from the board
+ mWidgetsRects = mWidgetsRects.stream().filter(widget -> {
+ if (widget.mBounds.contains(p.x, p.y)) {
+ removeWidgetFromBoard(widget);
+ return false;
+ }
+ return true;
+ }).collect(Collectors.toList());
+ // Remove overlapping icons and remove them from the board
+ mIconPoints = mIconPoints.stream().filter(iconPoint -> {
+ int x = iconPoint.coord.x;
+ int y = iconPoint.coord.y;
+ if (p.x == x && p.y == y) {
+ mWidget[x][y] = '-';
+ return false;
+ }
+ return true;
+ }).collect(Collectors.toList());
+ }
+
+ public void addWidget(int x, int y, int spanX, int spanY, char type) {
+ Rect rect = new Rect(x, y + spanY - 1, x + spanX - 1, y);
+ removeOverlappingItems(rect);
+ WidgetRect widgetRect = new WidgetRect(type, rect);
+ mWidgetsRects.add(widgetRect);
+ for (int xi = rect.left; xi < rect.right + 1; xi++) {
+ for (int yi = rect.bottom; yi < rect.top + 1; yi++) {
+ mWidget[xi][yi] = type;
+ }
+ }
+ }
+
+ public void addIcon(int x, int y) {
+ removeOverlappingItems(new Point(x, y));
+ mWidget[x][y] = 'i';
+ }
+
+ 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 +303,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,18 +327,28 @@
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]));
}
}
}
return iconPoints;
}
+ public static WidgetRect getMainFromList(List<CellLayoutBoard> boards) {
+ for (CellLayoutBoard board : boards) {
+ WidgetRect main = board.getMain();
+ if (main != null) {
+ return main;
+ }
+ }
+ return null;
+ }
+
public static CellLayoutBoard boardFromString(String boardStr) {
String[] lines = boardStr.split("\n");
CellLayoutBoard board = new CellLayoutBoard();
@@ -135,18 +357,52 @@
String line = lines[y];
for (int x = 0; x < line.length(); x++) {
char c = line.charAt(x);
- if (c == 'm') {
- 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.forEach(
- widgetRect -> board.mWidgetsMap.put(widgetRect.mType, widgetRect));
- board.mIconPoints = getIconPoints(board.mBoard);
+ board.mWidgetsRects = getRects(board.mWidget);
+ board.mWidgetsRects.forEach(widgetRect -> {
+ if (widgetRect.mType == CellType.MAIN_WIDGET) {
+ board.mMain = widgetRect;
+ }
+ board.mWidgetsMap.put(widgetRect.mType, widgetRect);
+ });
+ board.mIconPoints = getIconPoints(board.mWidget);
return board;
}
+
+ public String toString(int maxX, int maxY) {
+ StringBuilder s = new StringBuilder();
+ maxX = Math.min(maxX, mWidget.length);
+ maxY = Math.min(maxY, mWidget[0].length);
+ for (int y = 0; y < maxY; y++) {
+ for (int x = 0; x < maxX; x++) {
+ s.append(mWidget[x][y]);
+ }
+ s.append('\n');
+ }
+ return s.toString();
+ }
+
+ public static List<CellLayoutBoard> boardListFromString(String boardsStr) {
+ String[] lines = boardsStr.split("\n");
+ ArrayList<String> individualBoards = new ArrayList<>();
+ ArrayList<CellLayoutBoard> boards = new ArrayList<>();
+ for (String line : lines) {
+ String[] boardSegment = line.split("\\|");
+ for (int i = 0; i < boardSegment.length; i++) {
+ if (i >= individualBoards.size()) {
+ individualBoards.add(boardSegment[i]);
+ } else {
+ individualBoards.set(i, individualBoards.get(i) + "\n" + boardSegment[i]);
+ }
+ }
+ }
+ for (String board : individualBoards) {
+ boards.add(CellLayoutBoard.boardFromString(board));
+ }
+ return boards;
+ }
}
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/MultipleCellLayoutsSimpleReorder.java b/tests/src/com/android/launcher3/celllayout/MultipleCellLayoutsSimpleReorder.java
new file mode 100644
index 0000000..9b52fe8
--- /dev/null
+++ b/tests/src/com/android/launcher3/celllayout/MultipleCellLayoutsSimpleReorder.java
@@ -0,0 +1,88 @@
+/*
+ * 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.testcases;
+
+import android.graphics.Point;
+
+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 MultipleCellLayoutsSimpleReorder {
+
+ /** 5x5 Test
+ **/
+ private static final String START_BOARD_STR_5x5 = ""
+ + "xxxxx|-----\n"
+ + "--mm-|-----\n"
+ + "--mm-|-----\n"
+ + "-----|-----\n"
+ + "-----|-----";
+ private static final Point MOVE_TO_5x5 = new Point(8, 3);
+ private static final String END_BOARD_STR_5x5 = ""
+ + "xxxxx|-----\n"
+ + "-----|-----\n"
+ + "-----|-----\n"
+ + "-----|---mm\n"
+ + "-----|---mm";
+ private static final ReorderTestCase TEST_CASE_5x5 = new ReorderTestCase(START_BOARD_STR_5x5,
+ MOVE_TO_5x5,
+ END_BOARD_STR_5x5);
+
+ /** 4x4 Test
+ **/
+ private static final String START_BOARD_STR_4x4 = ""
+ + "xxxx|----\n"
+ + "--mm|----\n"
+ + "--mm|----\n"
+ + "----|----";
+ private static final Point MOVE_TO_4x4 = new Point(5, 3);
+ private static final String END_BOARD_STR_4x4 = ""
+ + "xxxx|----\n"
+ + "----|----\n"
+ + "----|-mm-\n"
+ + "----|-mm-";
+ private static final ReorderTestCase TEST_CASE_4x4 = new ReorderTestCase(START_BOARD_STR_4x4,
+ MOVE_TO_4x4,
+ END_BOARD_STR_4x4);
+
+
+ /** 6x5 Test
+ **/
+ private static final String START_BOARD_STR_6x5 = ""
+ + "xxxxxx|------\n"
+ + "--m---|------\n"
+ + "------|------\n"
+ + "------|------\n"
+ + "------|------";
+ private static final Point MOVE_TO_6x5 = new Point(10, 4);
+ private static final String END_BOARD_STR_6x5 = ""
+ + "xxxxxx|------\n"
+ + "------|------\n"
+ + "------|------\n"
+ + "------|------\n"
+ + "------|----m-";
+ private static final ReorderTestCase TEST_CASE_6x5 = new ReorderTestCase(START_BOARD_STR_6x5,
+ MOVE_TO_6x5,
+ END_BOARD_STR_6x5);
+
+ public static final Map<Point, ReorderTestCase> TEST_BY_GRID_SIZE =
+ Map.of(new Point(5, 5), TEST_CASE_5x5,
+ new Point(4, 4), TEST_CASE_4x4,
+ new Point(6, 5), TEST_CASE_6x5);
+}
diff --git a/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java b/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java
index ed0b71d..35f6cbc 100644
--- a/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java
+++ b/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java
@@ -15,40 +15,42 @@
*/
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;
import com.android.launcher3.CellLayout;
+import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.celllayout.testcases.FullReorderCase;
import com.android.launcher3.celllayout.testcases.MoveOutReorderCase;
+import com.android.launcher3.celllayout.testcases.MultipleCellLayoutsSimpleReorder;
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.ShellCommandRule;
-import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.views.DoubleShadowBubbleTextView;
import org.junit.Assume;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Map;
-
+import java.util.concurrent.ExecutionException;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -59,22 +61,11 @@
private static final String TAG = ReorderWidgets.class.getSimpleName();
- private View getViewAt(int cellX, int cellY) {
- return getFromLauncher(l -> l.getWorkspace().getScreenWithId(
- l.getWorkspace().getScreenIdForPageIndex(0)).getChildAt(cellX, cellY));
- }
-
- private Point getCellDimensions() {
- return getFromLauncher(l -> {
- CellLayout cellLayout = l.getWorkspace().getScreenWithId(
- l.getWorkspace().getScreenIdForPageIndex(0));
- return new Point(cellLayout.getWidth() / cellLayout.getCountX(),
- cellLayout.getHeight() / cellLayout.getCountY());
- });
- }
+ TestWorkspaceBuilder mWorkspaceBuilder;
@Before
public void setup() throws Throwable {
+ mWorkspaceBuilder = new TestWorkspaceBuilder(this, mTargetContext);
TaplTestsLauncher3.initialize(this);
clearHomescreen();
}
@@ -82,81 +73,88 @@
/**
* Validate if the given board represent the current CellLayout
**/
- private boolean validateBoard(CellLayoutBoard board) {
- boolean match = true;
- Point cellDimensions = getCellDimensions();
- for (TestBoardWidget widgetRect: board.getWidgets()) {
- if (widgetRect.shouldIgnore()) {
- continue;
- }
- View widget = getViewAt(widgetRect.getCellX(), widgetRect.getCellY());
- match &= widgetRect.getSpanX()
- == Math.round(widget.getWidth() / (float) cellDimensions.x);
- match &= widgetRect.getSpanY()
- == Math.round(widget.getHeight() / (float) cellDimensions.y);
- if (!match) return match;
+ private boolean validateBoard(List<CellLayoutBoard> testBoards) {
+ ArrayList<CellLayoutBoard> workspaceBoards = workspaceToBoards();
+ if (workspaceBoards.size() < testBoards.size()) {
+ return false;
}
- return match;
+ for (int i = 0; i < testBoards.size(); i++) {
+ if (testBoards.get(i).compareTo(workspaceBoards.get(i)) != 0) {
+ return false;
+ }
+ }
+ return true;
}
- /**
- * 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 FavoriteItemsTransaction buildWorkspaceFromBoards(List<CellLayoutBoard> boards,
+ FavoriteItemsTransaction transaction) {
+ for (int i = 0; i < boards.size(); i++) {
+ CellLayoutBoard board = boards.get(i);
+ mWorkspaceBuilder.buildFromBoard(board, transaction, i);
+ }
+ return transaction;
+ }
+
+ private void printCurrentWorkspace() {
+ InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(mTargetContext);
+ ArrayList<CellLayoutBoard> boards = workspaceToBoards();
+ for (int i = 0; i < boards.size(); i++) {
+ Log.d(TAG, "Screen number " + i);
+ Log.d(TAG, ".\n" + boards.get(i).toString(idp.numColumns, idp.numRows));
+ }
+ }
+
+ private ArrayList<CellLayoutBoard> workspaceToBoards() {
+ return getFromLauncher(l -> {
+ ArrayList<CellLayoutBoard> boards = new ArrayList<>();
+ int widgetCount = 0;
+ for (CellLayout cellLayout : l.getWorkspace().mWorkspaceScreens) {
+ CellLayoutBoard board = new CellLayoutBoard();
+ int count = cellLayout.getShortcutsAndWidgets().getChildCount();
+ for (int i = 0; i < count; i++) {
+ View callView = cellLayout.getShortcutsAndWidgets().getChildAt(i);
+ CellLayoutLayoutParams params =
+ (CellLayoutLayoutParams) callView.getLayoutParams();
+ // is icon
+ if (callView instanceof DoubleShadowBubbleTextView) {
+ board.addIcon(params.getCellX(), params.getCellY());
+ } else {
+ // is widget
+ board.addWidget(params.getCellX(), params.getCellY(), params.cellHSpan,
+ params.cellVSpan, (char) ('A' + widgetCount));
+ widgetCount++;
+ }
}
+ boards.add(board);
}
- }
+ return boards;
+ });
}
- 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();
+ private void runTestCase(ReorderTestCase testCase)
+ throws ExecutionException, InterruptedException {
+ CellLayoutBoard.WidgetRect mainWidgetCellPos = CellLayoutBoard.getMainFromList(
+ testCase.mStart);
- 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) {
- 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);
+ transaction = buildWorkspaceFromBoards(testCase.mStart, transaction);
+ transaction.commit();
+ waitForLauncherCondition("Workspace didn't finish loading", l -> !l.isWorkspaceLoading());
+ Widget widget = mLauncher.getWorkspace().getWidgetAtCell(mainWidgetCellPos.getCellX(),
+ mainWidgetCellPos.getCellY());
+ assertNotNull(widget);
+ WidgetResizeFrame resizeFrame = widget.dragWidgetToWorkspace(testCase.moveMainTo.x,
+ testCase.moveMainTo.y, mainWidgetCellPos.getSpanX(), mainWidgetCellPos.getSpanY());
+ resizeFrame.dismiss();
boolean isValid = false;
- for (CellLayoutBoard board : testCase.mEnd) {
- isValid |= validateBoard(board);
+ for (List<CellLayoutBoard> boards : testCase.mEnd) {
+ isValid |= validateBoard(boards);
+ if (isValid) break;
}
- assertTrue("None of the valid boards match with the current state", isValid);
+ printCurrentWorkspace();
+ assertTrue("Non of the valid boards match with the current state", isValid);
}
/**
@@ -164,7 +162,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(
@@ -174,24 +173,33 @@
}
@Test
- public void simpleReorder() {
+ public void simpleReorder() throws ExecutionException, InterruptedException {
runTestCaseMap(SimpleReorderCase.TEST_BY_GRID_SIZE,
SimpleReorderCase.class.getSimpleName());
}
+ @Ignore
@Test
- public void pushTest() {
+ public void pushTest() throws ExecutionException, InterruptedException {
runTestCaseMap(PushReorderCase.TEST_BY_GRID_SIZE, PushReorderCase.class.getSimpleName());
}
+ @Ignore
@Test
- public void fullReorder() {
+ public void fullReorder() throws ExecutionException, InterruptedException {
runTestCaseMap(FullReorderCase.TEST_BY_GRID_SIZE, FullReorderCase.class.getSimpleName());
}
@Test
- public void moveOutReorder() {
+ public void moveOutReorder() throws ExecutionException, InterruptedException {
runTestCaseMap(MoveOutReorderCase.TEST_BY_GRID_SIZE,
MoveOutReorderCase.class.getSimpleName());
}
+
+ @Test
+ public void multipleCellLayoutsSimpleReorder() throws ExecutionException, InterruptedException {
+ Assume.assumeTrue("Test doesn't support foldables", !mLauncher.isTwoPanels());
+ runTestCaseMap(MultipleCellLayoutsSimpleReorder.TEST_BY_GRID_SIZE,
+ MultipleCellLayoutsSimpleReorder.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..5945605
--- /dev/null
+++ b/tests/src/com/android/launcher3/celllayout/TestWorkspaceBuilder.java
@@ -0,0 +1,163 @@
+/*
+ * 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.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 screenId) {
+ 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)), screenId);
+ 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, int screenId) {
+ if (widgetRect.mType == 'x') {
+ fillWithWidgets(widgetRect, transaction, screenId);
+ } else {
+ transaction.addItem(createWidgetInCell(widgetRect, screenId));
+ }
+ }
+
+ /**
+ * Builds the given board into the transaction
+ */
+ public FavoriteItemsTransaction buildFromBoard(CellLayoutBoard board,
+ FavoriteItemsTransaction transaction, final int screenId) {
+ board.getWidgets().forEach(
+ (widgetRect) -> addCorrespondingWidgetRect(widgetRect, transaction, screenId));
+ board.getIcons().forEach((iconPoint) ->
+ transaction.addItem(createIconInCell(iconPoint, screenId))
+ );
+ 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).numDatabaseHotseatIcons;
+ for (int i = 0; i < hotseatCount; i++) {
+ transaction.addItem(getHotseatValues(i));
+ }
+ return transaction;
+ }
+
+ private ItemInfo createWidgetInCell(CellLayoutBoard.WidgetRect widgetRect, int screenId) {
+ LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(mTest, false);
+ LauncherAppWidgetInfo item = createWidgetInfo(info,
+ ApplicationProvider.getApplicationContext(), true);
+ item.id = getID();
+ item.cellX = widgetRect.getCellX();
+ item.cellY = widgetRect.getCellY();
+ item.spanX = widgetRect.getSpanX();
+ item.spanY = widgetRect.getSpanY();
+ item.screenId = screenId;
+ return item;
+ }
+
+ private ItemInfo createIconInCell(CellLayoutBoard.IconPoint iconPoint, int screenId) {
+ WorkspaceItemInfo item = new WorkspaceItemInfo(getApp());
+ item.id = getID();
+ item.screenId = screenId;
+ item.cellX = iconPoint.getCoord().x;
+ item.cellY = iconPoint.getCoord().y;
+ 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 ff03a50..d68d2ed 100644
--- a/tests/src/com/android/launcher3/celllayout/testcases/FullReorderCase.java
+++ b/tests/src/com/android/launcher3/celllayout/testcases/FullReorderCase.java
@@ -19,47 +19,91 @@
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
+ **/
private static final String START_BOARD_STR_5x5 = ""
+ "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,
MOVE_TO_5x5,
END_BOARD_STR_5x5);
+ /** 6x5 Test
+ **/
private static final String START_BOARD_STR_6x5 = ""
+ "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,
MOVE_TO_6x5,
END_BOARD_STR_6x5);
+ /** 4x4 Test
+ **/
+ private static final String START_BOARD_STR_4x4 = ""
+ + "xxxx\n"
+ + "22mm\n"
+ + "iimm\n"
+ + "ii11";
+ private static final Point MOVE_TO_4x4 = new Point(0, 3);
+ private static final String END_BOARD_STR_4x4 = ""
+ + "xxxx\n"
+ + "22ii\n"
+ + "mmii\n"
+ + "mm11";
+
+ private static final ReorderTestCase TEST_CASE_4x4 = new ReorderTestCase(START_BOARD_STR_4x4,
+ MOVE_TO_4x4,
+ END_BOARD_STR_4x4);
+
+ /** 4x4 Test
+ **/
+ private static final String START_BOARD_STR_4x5 = ""
+ + "xxxx\n"
+ + "22mm\n"
+ + "iimm\n"
+ + "ii11\n"
+ + "ii11";
+ private static final Point MOVE_TO_4x5 = new Point(0, 3);
+ private static final String END_BOARD_STR_4x5 = ""
+ + "xxxx\n"
+ + "22ii\n"
+ + "mmii\n"
+ + "mm11\n"
+ + "ii11";
+
+ private static final ReorderTestCase TEST_CASE_4x5 = new ReorderTestCase(START_BOARD_STR_4x5,
+ MOVE_TO_4x5,
+ END_BOARD_STR_4x5);
+
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);
+ 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(4, 5), TEST_CASE_4x5);
}
diff --git a/tests/src/com/android/launcher3/celllayout/testcases/MoveOutReorderCase.java b/tests/src/com/android/launcher3/celllayout/testcases/MoveOutReorderCase.java
index 32bf05a..047d342 100644
--- a/tests/src/com/android/launcher3/celllayout/testcases/MoveOutReorderCase.java
+++ b/tests/src/com/android/launcher3/celllayout/testcases/MoveOutReorderCase.java
@@ -19,48 +19,51 @@
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
+ **/
private static final String START_BOARD_STR_5x5 = ""
+ "xxxxx\n"
+ "34-m-\n"
+ "35111\n"
+ "32111\n"
+ "32111";
-
private static final Point MOVE_TO_5x5 = new Point(1, 2);
-
private static final String END_BOARD_STR_5x5 = ""
+ "xxxxx\n"
+ "345--\n"
+ "3m111\n"
+ "32111\n"
+ "32111";
-
private static final ReorderTestCase TEST_CASE_5x5 = new ReorderTestCase(START_BOARD_STR_5x5,
MOVE_TO_5x5,
END_BOARD_STR_5x5);
-
+ /** 6x5 Test
+ **/
private static final String START_BOARD_STR_6x5 = ""
+ "xxxxxx\n"
+ "34-m--\n"
+ "351111\n"
+ "321111\n"
+ "321111";
-
private static final Point MOVE_TO_6x5 = new Point(1, 2);
-
private static final String END_BOARD_STR_6x5 = ""
+ "xxxxxx\n"
+ "345---\n"
+ "3m1111\n"
+ "321111\n"
+ "321111";
-
private static final ReorderTestCase TEST_CASE_6x5 = new ReorderTestCase(START_BOARD_STR_6x5,
MOVE_TO_6x5,
END_BOARD_STR_6x5);
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);
+ Map.of(new Point(5, 5), TEST_CASE_5x5,
+ 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 b386946..38c9aee 100644
--- a/tests/src/com/android/launcher3/celllayout/testcases/PushReorderCase.java
+++ b/tests/src/com/android/launcher3/celllayout/testcases/PushReorderCase.java
@@ -17,60 +17,54 @@
import android.graphics.Point;
-import com.android.launcher3.celllayout.CellLayoutBoard;
-
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
+ **/
private static final String START_BOARD_STR_5x5 = ""
+ "xxxxx\n"
+ "222m-\n"
+ "--111\n"
+ "--333\n"
+ "-----";
- private static final CellLayoutBoard START_BOARD_5x5 = CellLayoutBoard.boardFromString(
- START_BOARD_STR_5x5);
-
private static final Point MOVE_TO_5x5 = new Point(2, 1);
-
private static final String END_BOARD_STR_5x5 = ""
+ "xxxxx\n"
+ "--m--\n"
+ "222--\n"
+ "--111\n"
+ "--333";
- private static final CellLayoutBoard END_BOARD_5x5 = CellLayoutBoard.boardFromString(
+ private static final ReorderTestCase TEST_CASE_5x5 = new ReorderTestCase(START_BOARD_STR_5x5,
+ MOVE_TO_5x5,
END_BOARD_STR_5x5);
- private static final ReorderTestCase TEST_CASE_5x5 = new ReorderTestCase(START_BOARD_5x5,
- MOVE_TO_5x5,
- END_BOARD_5x5);
-
+ /** 6x5 Test
+ **/
private static final String START_BOARD_STR_6x5 = ""
+ "xxxxxx\n"
+ "2222m-\n"
+ "--111-\n"
+ "--333-\n"
+ "------";
- private static final CellLayoutBoard START_BOARD_6x5 = CellLayoutBoard.boardFromString(
- START_BOARD_STR_6x5);
-
private static final Point MOVE_TO_6x5 = new Point(2, 1);
-
private static final String END_BOARD_STR_6x5 = ""
+ "xxxxxx\n"
+ "--m---\n"
+ "2222--\n"
+ "--111-\n"
+ "--333-";
- private static final CellLayoutBoard END_BOARD_6x5 = CellLayoutBoard.boardFromString(
+ private static final ReorderTestCase TEST_CASE_6x5 = new ReorderTestCase(START_BOARD_STR_6x5,
+ MOVE_TO_6x5,
END_BOARD_STR_6x5);
- private static final ReorderTestCase TEST_CASE_6x5 = new ReorderTestCase(START_BOARD_6x5,
- MOVE_TO_6x5,
- END_BOARD_6x5);
-
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);
+ Map.of(new Point(5, 5), TEST_CASE_5x5,
+ new Point(6, 5), TEST_CASE_6x5);
}
diff --git a/tests/src/com/android/launcher3/celllayout/testcases/ReorderTestCase.java b/tests/src/com/android/launcher3/celllayout/testcases/ReorderTestCase.java
index 0a28668..1689e47 100644
--- a/tests/src/com/android/launcher3/celllayout/testcases/ReorderTestCase.java
+++ b/tests/src/com/android/launcher3/celllayout/testcases/ReorderTestCase.java
@@ -24,23 +24,23 @@
import java.util.stream.Collectors;
public class ReorderTestCase {
- public CellLayoutBoard mStart;
+ public List<CellLayoutBoard> mStart;
public Point moveMainTo;
- public List<CellLayoutBoard> mEnd;
+ public List<List<CellLayoutBoard>> mEnd;
- ReorderTestCase(CellLayoutBoard start, Point moveMainTo, CellLayoutBoard ... end) {
+ ReorderTestCase(List<CellLayoutBoard> start, Point moveMainTo, List<CellLayoutBoard> ... end) {
mStart = start;
this.moveMainTo = moveMainTo;
mEnd = Arrays.asList(end);
}
ReorderTestCase(String start, Point moveMainTo, String ... end) {
- mStart = CellLayoutBoard.boardFromString(start);
+ mStart = CellLayoutBoard.boardListFromString(start);
this.moveMainTo = moveMainTo;
mEnd = Arrays
.asList(end)
.stream()
- .map(CellLayoutBoard::boardFromString)
+ .map(CellLayoutBoard::boardListFromString)
.collect(Collectors.toList());
}
}
diff --git a/tests/src/com/android/launcher3/celllayout/testcases/SimpleReorderCase.java b/tests/src/com/android/launcher3/celllayout/testcases/SimpleReorderCase.java
index 57e1398..c9f6849 100644
--- a/tests/src/com/android/launcher3/celllayout/testcases/SimpleReorderCase.java
+++ b/tests/src/com/android/launcher3/celllayout/testcases/SimpleReorderCase.java
@@ -17,35 +17,71 @@
import android.graphics.Point;
-import com.android.launcher3.celllayout.CellLayoutBoard;
-
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 {
- private static final String START_BOARD_STR = ""
+
+ /** 5x5 Test
+ **/
+ private static final String START_BOARD_STR_5x5 = ""
+ "xxxxx\n"
+ "--mm-\n"
+ "--mm-\n"
+ "-----\n"
+ "-----";
- private static final CellLayoutBoard START_BOARD_5x5 = CellLayoutBoard.boardFromString(
- START_BOARD_STR);
-
- private static final Point MOVE_TO_5x5 = new Point(4, 4);
-
+ private static final Point MOVE_TO_5x5 = new Point(0, 4);
private static final String END_BOARD_STR_5x5 = ""
+ "xxxxx\n"
+ "-----\n"
+ "-----\n"
- + "---mm\n"
- + "---mm";
- private static final CellLayoutBoard END_BOARD_5x5 = CellLayoutBoard.boardFromString(
+ + "mm---\n"
+ + "mm---";
+ private static final ReorderTestCase TEST_CASE_5x5 = new ReorderTestCase(START_BOARD_STR_5x5,
+ MOVE_TO_5x5,
END_BOARD_STR_5x5);
- private static final ReorderTestCase TEST_CASE_5x5 = new ReorderTestCase(START_BOARD_5x5,
- MOVE_TO_5x5,
- END_BOARD_5x5);
+ /** 4x4 Test
+ **/
+ private static final String START_BOARD_STR_4x4 = ""
+ + "xxxx\n"
+ + "--mm\n"
+ + "--mm\n"
+ + "----";
+ private static final Point MOVE_TO_4x4 = new Point(3, 3);
+ private static final String END_BOARD_STR_4x4 = ""
+ + "xxxx\n"
+ + "----\n"
+ + "--mm\n"
+ + "--mm";
+ private static final ReorderTestCase TEST_CASE_4x4 = new ReorderTestCase(START_BOARD_STR_4x4,
+ MOVE_TO_4x4,
+ END_BOARD_STR_4x4);
+
+ /** 6x5 Test
+ **/
+ private static final String START_BOARD_STR_6x5 = ""
+ + "xxxxxx\n"
+ + "-mm---\n"
+ + "-mm---\n"
+ + "------\n"
+ + "------";
+ private static final Point MOVE_TO_6x5 = new Point(4, 3);
+ private static final String END_BOARD_STR_6x5 = ""
+ + "xxxxxx\n"
+ + "------\n"
+ + "------\n"
+ + "----mm\n"
+ + "----mm";
+ private static final ReorderTestCase TEST_CASE_6x5 = new ReorderTestCase(START_BOARD_STR_6x5,
+ MOVE_TO_6x5,
+ END_BOARD_STR_6x5);
public static final Map<Point, ReorderTestCase> TEST_BY_GRID_SIZE =
- Map.of(new Point(5, 5), TEST_CASE_5x5);
+ Map.of(new Point(5, 5), TEST_CASE_5x5,
+ new Point(4, 4), TEST_CASE_4x4,
+ new Point(6, 5), TEST_CASE_6x5);
}
diff --git a/tests/src/com/android/launcher3/model/AbstractWorkspaceModelTest.kt b/tests/src/com/android/launcher3/model/AbstractWorkspaceModelTest.kt
index d26381d..03352fe 100644
--- a/tests/src/com/android/launcher3/model/AbstractWorkspaceModelTest.kt
+++ b/tests/src/com/android/launcher3/model/AbstractWorkspaceModelTest.kt
@@ -30,9 +30,7 @@
import com.android.launcher3.util.LauncherModelHelper
import java.util.UUID
-/**
- * Base class for workspace related tests.
- */
+/** Base class for workspace related tests. */
abstract class AbstractWorkspaceModelTest {
companion object {
val emptyScreenSpaces = listOf(Rect(0, 0, 5, 5))
@@ -64,10 +62,7 @@
mModelHelper.destroy()
}
-
- /**
- * Sets up workspaces with the given screen IDs with some items and a 2x2 space.
- */
+ /** Sets up workspaces with the given screen IDs with some items and a 2x2 space. */
fun setupWorkspaces(screenIdsWithItems: List<Int>) {
var nextItemId = 1
screenIdsWithItems.forEach { screenId ->
@@ -83,8 +78,7 @@
screen1: List<Rect>? = null,
screen2: List<Rect>? = null,
screen3: List<Rect>? = null,
- ) = listOf(screen0, screen1, screen2, screen3)
- .let(this::setupWithSpaces)
+ ) = listOf(screen0, screen1, screen2, screen3).let(this::setupWithSpaces)
private fun setupWithSpaces(workspaceSpaces: List<List<Rect>?>) {
var nextItemId = 1
@@ -110,9 +104,7 @@
var itemId = itemStartId
val occupancy = GridOccupancy(mIdp.numColumns, mIdp.numRows)
occupancy.markCells(0, 0, mIdp.numColumns, mIdp.numRows, true)
- spaces.forEach { spaceRect ->
- occupancy.markCells(spaceRect, false)
- }
+ spaces.forEach { spaceRect -> occupancy.markCells(spaceRect, false) }
mExistingScreens.add(screenId)
mScreenOccupancy.append(screenId, occupancy)
for (x in 0 until mIdp.numColumns) {
@@ -139,24 +131,21 @@
return itemId
}
- fun getExistingItem() = WorkspaceItemInfo()
- .apply { intent = Intent().setComponent(ComponentName("a", "b")) }
+ fun getExistingItem() =
+ WorkspaceItemInfo().apply { intent = Intent().setComponent(ComponentName("a", "b")) }
fun getNewItem(): WorkspaceItemInfo {
val itemPackage = UUID.randomUUID().toString()
- return WorkspaceItemInfo()
- .apply { intent = Intent().setComponent(ComponentName(itemPackage, itemPackage)) }
+ return WorkspaceItemInfo().apply {
+ intent = Intent().setComponent(ComponentName(itemPackage, itemPackage))
+ }
}
}
-data class NewItemSpace(
- val screenId: Int,
- val cellX: Int,
- val cellY: Int
-) {
+data class NewItemSpace(val screenId: Int, val cellX: Int, val cellY: Int) {
fun toIntArray() = intArrayOf(screenId, cellX, cellY)
companion object {
fun fromIntArray(array: kotlin.IntArray) = NewItemSpace(array[0], array[1], array[2])
}
-}
\ No newline at end of file
+}
diff --git a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt
index 65d938b..6636b8a 100644
--- a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt
+++ b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt
@@ -23,9 +23,10 @@
import com.android.launcher3.model.data.WorkspaceItemInfo
import com.android.launcher3.util.Executors
import com.android.launcher3.util.IntArray
-import com.android.launcher3.util.same
-import com.android.launcher3.util.eq
+import com.android.launcher3.util.IntSet
import com.android.launcher3.util.any
+import com.android.launcher3.util.eq
+import com.android.launcher3.util.same
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
@@ -34,36 +35,30 @@
import org.mockito.ArgumentCaptor
import org.mockito.Captor
import org.mockito.Mock
-import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyZeroInteractions
-import org.mockito.Mockito.times
import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
-/**
- * Tests for [AddWorkspaceItemsTask]
- */
+/** Tests for [AddWorkspaceItemsTask] */
@SmallTest
@RunWith(AndroidJUnit4::class)
class AddWorkspaceItemsTaskTest : AbstractWorkspaceModelTest() {
- @Captor
- private lateinit var mAnimatedItemArgumentCaptor: ArgumentCaptor<ArrayList<ItemInfo>>
+ @Captor private lateinit var mAnimatedItemArgumentCaptor: ArgumentCaptor<ArrayList<ItemInfo>>
- @Captor
- private lateinit var mNotAnimatedItemArgumentCaptor: ArgumentCaptor<ArrayList<ItemInfo>>
+ @Captor private lateinit var mNotAnimatedItemArgumentCaptor: ArgumentCaptor<ArrayList<ItemInfo>>
- @Mock
- private lateinit var mDataModelCallbacks: BgDataModel.Callbacks
+ @Mock private lateinit var mDataModelCallbacks: BgDataModel.Callbacks
- @Mock
- private lateinit var mWorkspaceItemSpaceFinder: WorkspaceItemSpaceFinder
-
+ @Mock private lateinit var mWorkspaceItemSpaceFinder: WorkspaceItemSpaceFinder
@Before
override fun setup() {
super.setup()
MockitoAnnotations.initMocks(this)
+ whenever(mDataModelCallbacks.getPagesToBindSynchronously(any())).thenReturn(IntSet())
Executors.MAIN_EXECUTOR.submit { mModelHelper.model.addCallbacks(mDataModelCallbacks) }
.get()
}
@@ -89,10 +84,7 @@
@Test
fun givenNewAndExistingItems_whenExecuteTask_thenOnlyAddNewItem() {
- val itemsToAdd = arrayOf(
- getNewItem(),
- getExistingItem()
- )
+ val itemsToAdd = arrayOf(getNewItem(), getExistingItem())
givenNewItemSpaces(NewItemSpace(1, 0, 0))
val nonEmptyScreenIds = listOf(0)
@@ -132,13 +124,14 @@
@Test
fun givenMultipleItems_whenExecuteTask_thenAddThem() {
- val itemsToAdd = arrayOf(
- getNewItem(),
- getExistingItem(),
- getNewItem(),
- getNewItem(),
- getExistingItem(),
- )
+ val itemsToAdd =
+ arrayOf(
+ getNewItem(),
+ getExistingItem(),
+ getNewItem(),
+ getNewItem(),
+ getExistingItem(),
+ )
givenNewItemSpaces(
NewItemSpace(1, 3, 3),
NewItemSpace(2, 0, 0),
@@ -159,27 +152,16 @@
// Items that are added to the second screen should be animated
val itemsAddedToSecondScreen = addedItems.filter { it.itemInfo.screenId == 2 }
assertThat(itemsAddedToSecondScreen.size).isEqualTo(2)
- itemsAddedToSecondScreen.forEach {
- assertThat(it.isAnimated).isTrue()
- }
+ itemsAddedToSecondScreen.forEach { assertThat(it.isAnimated).isTrue() }
verifyItemSpaceFinderCall(nonEmptyScreenIds, numberOfExpectedCall = 3)
}
- /**
- * Sets up the item space data that will be returned from WorkspaceItemSpaceFinder.
- */
+ /** Sets up the item space data that will be returned from WorkspaceItemSpaceFinder. */
private fun givenNewItemSpaces(vararg newItemSpaces: NewItemSpace) {
val spaceStack = newItemSpaces.toMutableList()
whenever(
- mWorkspaceItemSpaceFinder.findSpaceForItem(
- any(),
- any(),
- any(),
- any(),
- any(),
- any()
+ mWorkspaceItemSpaceFinder.findSpaceForItem(any(), any(), any(), any(), any(), any())
)
- )
.then { spaceStack.removeFirst().toIntArray() }
}
@@ -187,20 +169,21 @@
* Verifies if WorkspaceItemSpaceFinder was called with proper arguments and how many times was
* it called.
*/
- private fun verifyItemSpaceFinderCall(
- nonEmptyScreenIds: List<Int>,
- numberOfExpectedCall: Int
- ) {
+ private fun verifyItemSpaceFinderCall(nonEmptyScreenIds: List<Int>, numberOfExpectedCall: Int) {
verify(mWorkspaceItemSpaceFinder, times(numberOfExpectedCall))
.findSpaceForItem(
- same(mAppState), same(mModelHelper.bgDataModel),
- eq(IntArray.wrap(*nonEmptyScreenIds.toIntArray())), eq(IntArray()), eq(1), eq(1)
+ same(mAppState),
+ same(mModelHelper.bgDataModel),
+ eq(IntArray.wrap(*nonEmptyScreenIds.toIntArray())),
+ eq(IntArray()),
+ eq(1),
+ eq(1)
)
}
/**
- * Sets up the workspaces with items, executes the task, collects the added items from the
- * model callback then returns it.
+ * Sets up the workspaces with items, executes the task, collects the added items from the model
+ * callback then returns it.
*/
private fun testAddItems(
nonEmptyScreenIds: List<Int>,
@@ -209,21 +192,21 @@
setupWorkspaces(nonEmptyScreenIds)
val task = newTask(*itemsToAdd)
var updateCount = 0
- mModelHelper.executeTaskForTest(task)
- .forEach {
- updateCount++
- it.run()
- }
+ mModelHelper.executeTaskForTest(task).forEach {
+ updateCount++
+ it.run()
+ }
val addedItems = mutableListOf<AddedItem>()
if (updateCount > 0) {
- verify(mDataModelCallbacks).bindAppsAdded(
- any(),
- mNotAnimatedItemArgumentCaptor.capture(), mAnimatedItemArgumentCaptor.capture()
- )
+ verify(mDataModelCallbacks)
+ .bindAppsAdded(
+ any(),
+ mNotAnimatedItemArgumentCaptor.capture(),
+ mAnimatedItemArgumentCaptor.capture()
+ )
addedItems.addAll(mAnimatedItemArgumentCaptor.value.map { AddedItem(it, true) })
addedItems.addAll(mNotAnimatedItemArgumentCaptor.value.map { AddedItem(it, false) })
-
}
return addedItems
@@ -234,12 +217,10 @@
* with a mock.
*/
private fun newTask(vararg items: ItemInfo): AddWorkspaceItemsTask =
- items.map { Pair.create(it, Any()) }
+ items
+ .map { Pair.create(it, Any()) }
.toMutableList()
.let { AddWorkspaceItemsTask(it, mWorkspaceItemSpaceFinder) }
}
-private data class AddedItem(
- val itemInfo: ItemInfo,
- val isAnimated: Boolean
-)
+private data class AddedItem(val itemInfo: ItemInfo, val isAnimated: Boolean)
diff --git a/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java b/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
index dba0a40..f55b244 100644
--- a/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
+++ b/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
@@ -57,23 +57,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/model/GridSizeMigrationTaskV2Test.kt b/tests/src/com/android/launcher3/model/GridSizeMigrationTaskV2Test.kt
deleted file mode 100644
index 90d7b43..0000000
--- a/tests/src/com/android/launcher3/model/GridSizeMigrationTaskV2Test.kt
+++ /dev/null
@@ -1,510 +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.model
-
-import android.content.Context
-import android.content.Intent
-import android.database.sqlite.SQLiteDatabase
-import android.graphics.Point
-import android.os.Process
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.launcher3.InvariantDeviceProfile
-import com.android.launcher3.LauncherFiles
-import com.android.launcher3.LauncherSettings.Favorites.*
-import com.android.launcher3.config.FeatureFlags
-import com.android.launcher3.model.GridSizeMigrationTaskV2.DbReader
-import com.android.launcher3.pm.UserCache
-import com.android.launcher3.provider.LauncherDbUtils
-import com.android.launcher3.util.LauncherModelHelper
-import com.android.launcher3.util.LauncherModelHelper.*
-import com.google.common.truth.Truth.assertThat
-import org.junit.After
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-
-/** Unit tests for [GridSizeMigrationTaskV2] */
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class GridSizeMigrationTaskV2Test {
- private lateinit var modelHelper: LauncherModelHelper
- private lateinit var context: Context
- private lateinit var db: SQLiteDatabase
- private lateinit var validPackages: Set<String>
- private lateinit var idp: InvariantDeviceProfile
- private val testPackage1 = "com.android.launcher3.validpackage1"
- private val testPackage2 = "com.android.launcher3.validpackage2"
- private val testPackage3 = "com.android.launcher3.validpackage3"
- private val testPackage4 = "com.android.launcher3.validpackage4"
- private val testPackage5 = "com.android.launcher3.validpackage5"
- private val testPackage6 = "com.android.launcher3.validpackage6"
- private val testPackage7 = "com.android.launcher3.validpackage7"
- private val testPackage8 = "com.android.launcher3.validpackage8"
- private val testPackage9 = "com.android.launcher3.validpackage9"
- private val testPackage10 = "com.android.launcher3.validpackage10"
-
- @Before
- fun setUp() {
- modelHelper = LauncherModelHelper()
- context = modelHelper.sandboxContext
- db = modelHelper.provider.db
-
- validPackages = setOf(
- TEST_PACKAGE,
- testPackage1,
- testPackage2,
- testPackage3,
- testPackage4,
- testPackage5,
- testPackage6,
- testPackage7,
- testPackage8,
- testPackage9,
- testPackage10
- )
-
- idp = InvariantDeviceProfile.INSTANCE[context]
- val userSerial = UserCache.INSTANCE[context].getSerialNumberForUser(Process.myUserHandle())
- LauncherDbUtils.dropTable(db, TMP_TABLE)
- addTableToDb(db, userSerial, false, TMP_TABLE)
- }
-
- @After
- fun tearDown() {
- modelHelper.destroy()
- }
-
- /**
- * Old migration logic, should be modified once [FeatureFlags.ENABLE_NEW_MIGRATION_LOGIC] is
- * not needed anymore
- */
- @Test
- @Throws(Exception::class)
- fun testMigration() {
- // Src Hotseat icons
- modelHelper.addItem(APP_ICON, 0, HOTSEAT, 0, 0, testPackage1, 1, TMP_CONTENT_URI)
- modelHelper.addItem(SHORTCUT, 1, HOTSEAT, 0, 0, testPackage2, 2, TMP_CONTENT_URI)
- modelHelper.addItem(SHORTCUT, 3, HOTSEAT, 0, 0, testPackage3, 3, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 4, HOTSEAT, 0, 0, testPackage4, 4, TMP_CONTENT_URI)
- // Src grid icons
- // _ _ _ _ _
- // _ _ _ _ 5
- // _ _ 6 _ 7
- // _ _ 8 _ 9
- // _ _ _ _ _
- modelHelper.addItem(APP_ICON, 0, DESKTOP, 4, 1, testPackage5, 5, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 2, testPackage6, 6, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 0, DESKTOP, 4, 2, testPackage7, 7, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 3, testPackage8, 8, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 0, DESKTOP, 4, 3, testPackage9, 9, TMP_CONTENT_URI)
-
- // Dest hotseat icons
- modelHelper.addItem(SHORTCUT, 1, HOTSEAT, 0, 0, testPackage2)
- // Dest grid icons
- modelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 2, testPackage10)
-
- idp.numDatabaseHotseatIcons = 4
- idp.numColumns = 4
- idp.numRows = 4
- val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
- val destReader = DbReader(db, TABLE_NAME, context, validPackages)
- val task = GridSizeMigrationTaskV2(
- context,
- db,
- srcReader,
- destReader,
- idp.numDatabaseHotseatIcons,
- Point(idp.numColumns, idp.numRows)
- )
- task.migrate(DeviceGridState(context), DeviceGridState(idp))
-
- // Check hotseat items
- var c = context.contentResolver.query(
- CONTENT_URI,
- arrayOf(SCREEN, INTENT),
- "container=$CONTAINER_HOTSEAT",
- null,
- SCREEN,
- null
- ) ?: throw IllegalStateException()
-
- assertThat(c.count).isEqualTo(idp.numDatabaseHotseatIcons)
-
- val screenIndex = c.getColumnIndex(SCREEN)
- var intentIndex = c.getColumnIndex(INTENT)
- c.moveToNext()
- assertThat(c.getInt(screenIndex).toLong()).isEqualTo(0)
- assertThat(c.getString(intentIndex)).contains(testPackage1)
- c.moveToNext()
- assertThat(c.getInt(screenIndex).toLong()).isEqualTo(1)
- assertThat(c.getString(intentIndex)).contains(testPackage2)
- c.moveToNext()
- assertThat(c.getInt(screenIndex).toLong()).isEqualTo(2)
- assertThat(c.getString(intentIndex)).contains(testPackage3)
- c.moveToNext()
- assertThat(c.getInt(screenIndex).toLong()).isEqualTo(3)
- assertThat(c.getString(intentIndex)).contains(testPackage4)
- c.close()
-
- // Check workspace items
- c = context.contentResolver.query(
- CONTENT_URI,
- arrayOf(CELLX, CELLY, INTENT),
- "container=$CONTAINER_DESKTOP",
- null,
- null,
- null
- ) ?: throw IllegalStateException()
-
- intentIndex = c.getColumnIndex(INTENT)
- val cellXIndex = c.getColumnIndex(CELLX)
- val cellYIndex = c.getColumnIndex(CELLY)
- val locMap = HashMap<String, Point>()
- while (c.moveToNext()) {
- locMap[Intent.parseUri(c.getString(intentIndex), 0).getPackage()] =
- Point(c.getInt(cellXIndex), c.getInt(cellYIndex))
- }
- c.close()
- // Expected dest grid icons
- // _ _ _ _
- // 5 6 7 8
- // 9 _ 10_
- // _ _ _ _
- assertThat(locMap.size.toLong()).isEqualTo(6)
- assertThat(locMap[testPackage5]).isEqualTo(Point(0, 1))
- assertThat(locMap[testPackage6]).isEqualTo(Point(1, 1))
- assertThat(locMap[testPackage7]).isEqualTo(Point(2, 1))
- assertThat(locMap[testPackage8]).isEqualTo(Point(3, 1))
- assertThat(locMap[testPackage9]).isEqualTo(Point(0, 2))
- assertThat(locMap[testPackage10]).isEqualTo(Point(2, 2))
- }
-
- @Test
- fun migrateToLargerHotseat() {
- val srcHotseatItems = intArrayOf(
- modelHelper.addItem(APP_ICON, 0, HOTSEAT, 0, 0, testPackage1, 1, TMP_CONTENT_URI),
- modelHelper.addItem(SHORTCUT, 1, HOTSEAT, 0, 0, testPackage2, 2, TMP_CONTENT_URI),
- modelHelper.addItem(APP_ICON, 2, HOTSEAT, 0, 0, testPackage3, 3, TMP_CONTENT_URI),
- modelHelper.addItem(SHORTCUT, 3, HOTSEAT, 0, 0, testPackage4, 4, TMP_CONTENT_URI)
- )
- val numSrcDatabaseHotseatIcons = srcHotseatItems.size
- idp.numDatabaseHotseatIcons = 6
- idp.numColumns = 4
- idp.numRows = 4
- val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
- val destReader = DbReader(db, TABLE_NAME, context, validPackages)
- val task = GridSizeMigrationTaskV2(
- context,
- db,
- srcReader,
- destReader,
- idp.numDatabaseHotseatIcons,
- Point(idp.numColumns, idp.numRows)
- )
- task.migrate(DeviceGridState(context), DeviceGridState(idp))
-
- // Check hotseat items
- val c = context.contentResolver.query(
- CONTENT_URI,
- arrayOf(SCREEN, INTENT),
- "container=$CONTAINER_HOTSEAT",
- null,
- SCREEN,
- null
- ) ?: throw IllegalStateException()
-
- assertThat(c.count.toLong()).isEqualTo(numSrcDatabaseHotseatIcons.toLong())
- val screenIndex = c.getColumnIndex(SCREEN)
- val intentIndex = c.getColumnIndex(INTENT)
- c.moveToNext()
- assertThat(c.getInt(screenIndex)).isEqualTo(0)
- assertThat(c.getString(intentIndex)).contains(testPackage1)
-
- c.moveToNext()
- assertThat(c.getInt(screenIndex)).isEqualTo(1)
- assertThat(c.getString(intentIndex)).contains(testPackage2)
-
- c.moveToNext()
- assertThat(c.getInt(screenIndex)).isEqualTo(2)
- assertThat(c.getString(intentIndex)).contains(testPackage3)
-
- c.moveToNext()
- assertThat(c.getInt(screenIndex)).isEqualTo(3)
- assertThat(c.getString(intentIndex)).contains(testPackage4)
-
- c.close()
- }
-
- @Test
- fun migrateFromLargerHotseat() {
- modelHelper.addItem(APP_ICON, 0, HOTSEAT, 0, 0, testPackage1, 1, TMP_CONTENT_URI)
- modelHelper.addItem(SHORTCUT, 2, HOTSEAT, 0, 0, testPackage2, 2, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 3, HOTSEAT, 0, 0, testPackage3, 3, TMP_CONTENT_URI)
- modelHelper.addItem(SHORTCUT, 4, HOTSEAT, 0, 0, testPackage4, 4, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 5, HOTSEAT, 0, 0, testPackage5, 5, TMP_CONTENT_URI)
-
- idp.numDatabaseHotseatIcons = 4
- idp.numColumns = 4
- idp.numRows = 4
- val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
- val destReader = DbReader(db, TABLE_NAME, context, validPackages)
- val task = GridSizeMigrationTaskV2(
- context,
- db,
- srcReader,
- destReader,
- idp.numDatabaseHotseatIcons,
- Point(idp.numColumns, idp.numRows)
- )
- task.migrate(DeviceGridState(context), DeviceGridState(idp))
-
- // Check hotseat items
- val c = context.contentResolver.query(
- CONTENT_URI,
- arrayOf(SCREEN, INTENT),
- "container=$CONTAINER_HOTSEAT",
- null,
- SCREEN,
- null
- ) ?: throw IllegalStateException()
-
- assertThat(c.count.toLong()).isEqualTo(idp.numDatabaseHotseatIcons.toLong())
- val screenIndex = c.getColumnIndex(SCREEN)
- val intentIndex = c.getColumnIndex(INTENT)
-
- c.moveToNext()
- assertThat(c.getInt(screenIndex)).isEqualTo(0)
- assertThat(c.getString(intentIndex)).contains(testPackage1)
-
- c.moveToNext()
- assertThat(c.getInt(screenIndex)).isEqualTo(1)
- assertThat(c.getString(intentIndex)).contains(testPackage2)
-
- c.moveToNext()
- assertThat(c.getInt(screenIndex)).isEqualTo(2)
- assertThat(c.getString(intentIndex)).contains(testPackage3)
-
- c.moveToNext()
- assertThat(c.getInt(screenIndex)).isEqualTo(3)
- assertThat(c.getString(intentIndex)).contains(testPackage4)
-
- c.close()
- }
-
- /**
- * Migrating from a smaller grid to a large one should keep the pages
- * if the column difference is less than 2
- */
- @Test
- @Throws(Exception::class)
- fun migrateFromSmallerGridSmallDifference() {
- enableNewMigrationLogic("4,4")
-
- // Setup src grid
- modelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 2, testPackage1, 5, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 3, testPackage2, 6, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 1, DESKTOP, 3, 1, testPackage3, 7, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 1, DESKTOP, 3, 2, testPackage4, 8, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 2, DESKTOP, 3, 3, testPackage5, 9, TMP_CONTENT_URI)
-
- idp.numDatabaseHotseatIcons = 4
- idp.numColumns = 6
- idp.numRows = 5
-
- val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
- val destReader = DbReader(db, TABLE_NAME, context, validPackages)
- val task = GridSizeMigrationTaskV2(
- context,
- db,
- srcReader,
- destReader,
- idp.numDatabaseHotseatIcons,
- Point(idp.numColumns, idp.numRows)
- )
- task.migrate(DeviceGridState(context), DeviceGridState(idp))
-
- // Get workspace items
- val c = context.contentResolver.query(
- CONTENT_URI,
- arrayOf(INTENT, SCREEN),
- "container=$CONTAINER_DESKTOP",
- null,
- null,
- null
- ) ?: throw IllegalStateException()
- val intentIndex = c.getColumnIndex(INTENT)
- val screenIndex = c.getColumnIndex(SCREEN)
-
- // Get in which screen the icon is
- val locMap = HashMap<String, Int>()
- while (c.moveToNext()) {
- locMap[Intent.parseUri(c.getString(intentIndex), 0).getPackage()] =
- c.getInt(screenIndex)
- }
- c.close()
- assertThat(locMap.size).isEqualTo(5)
- assertThat(locMap[testPackage1]).isEqualTo(0)
- assertThat(locMap[testPackage2]).isEqualTo(0)
- assertThat(locMap[testPackage3]).isEqualTo(1)
- assertThat(locMap[testPackage4]).isEqualTo(1)
- assertThat(locMap[testPackage5]).isEqualTo(2)
-
- disableNewMigrationLogic()
- }
-
- /**
- * Migrating from a smaller grid to a large one should reflow the pages
- * if the column difference is more than 2
- */
- @Test
- @Throws(Exception::class)
- fun migrateFromSmallerGridBigDifference() {
- enableNewMigrationLogic("2,2")
-
- // Setup src grid
- modelHelper.addItem(APP_ICON, 0, DESKTOP, 0, 1, testPackage1, 5, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 0, DESKTOP, 1, 1, testPackage2, 6, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 1, DESKTOP, 0, 0, testPackage3, 7, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 1, DESKTOP, 1, 0, testPackage4, 8, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 2, DESKTOP, 0, 0, testPackage5, 9, TMP_CONTENT_URI)
-
- idp.numDatabaseHotseatIcons = 4
- idp.numColumns = 5
- idp.numRows = 5
- val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
- val destReader = DbReader(db, TABLE_NAME, context, validPackages)
- val task = GridSizeMigrationTaskV2(
- context,
- db,
- srcReader,
- destReader,
- idp.numDatabaseHotseatIcons,
- Point(idp.numColumns, idp.numRows)
- )
- task.migrate(DeviceGridState(context), DeviceGridState(idp))
-
- // Get workspace items
- val c = context.contentResolver.query(
- CONTENT_URI,
- arrayOf(INTENT, SCREEN),
- "container=$CONTAINER_DESKTOP",
- null,
- null,
- null
- ) ?: throw IllegalStateException()
-
- val intentIndex = c.getColumnIndex(INTENT)
- val screenIndex = c.getColumnIndex(SCREEN)
-
- // Get in which screen the icon is
- val locMap = HashMap<String, Int>()
- while (c.moveToNext()) {
- locMap[Intent.parseUri(c.getString(intentIndex), 0).getPackage()] =
- c.getInt(screenIndex)
- }
- c.close()
-
- // All icons fit the first screen
- assertThat(locMap.size).isEqualTo(5)
- assertThat(locMap[testPackage1]).isEqualTo(0)
- assertThat(locMap[testPackage2]).isEqualTo(0)
- assertThat(locMap[testPackage3]).isEqualTo(0)
- assertThat(locMap[testPackage4]).isEqualTo(0)
- assertThat(locMap[testPackage5]).isEqualTo(0)
- disableNewMigrationLogic()
- }
-
- /**
- * Migrating from a larger grid to a smaller, we reflow from page 0
- */
- @Test
- @Throws(Exception::class)
- fun migrateFromLargerGrid() {
- enableNewMigrationLogic("5,5")
-
- // Setup src grid
- modelHelper.addItem(APP_ICON, 0, DESKTOP, 0, 1, testPackage1, 5, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 0, DESKTOP, 1, 1, testPackage2, 6, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 1, DESKTOP, 0, 0, testPackage3, 7, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 1, DESKTOP, 1, 0, testPackage4, 8, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 2, DESKTOP, 0, 0, testPackage5, 9, TMP_CONTENT_URI)
-
- idp.numDatabaseHotseatIcons = 4
- idp.numColumns = 4
- idp.numRows = 4
- val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
- val destReader = DbReader(db, TABLE_NAME, context, validPackages)
- val task = GridSizeMigrationTaskV2(
- context,
- db,
- srcReader,
- destReader,
- idp.numDatabaseHotseatIcons,
- Point(idp.numColumns, idp.numRows)
- )
- task.migrate(DeviceGridState(context), DeviceGridState(idp))
-
- // Get workspace items
- val c = context.contentResolver.query(
- CONTENT_URI,
- arrayOf(INTENT, SCREEN),
- "container=$CONTAINER_DESKTOP",
- null,
- null,
- null
- ) ?: throw IllegalStateException()
- val intentIndex = c.getColumnIndex(INTENT)
- val screenIndex = c.getColumnIndex(SCREEN)
-
- // Get in which screen the icon is
- val locMap = HashMap<String, Int>()
- while (c.moveToNext()) {
- locMap[Intent.parseUri(c.getString(intentIndex), 0).getPackage()] =
- c.getInt(screenIndex)
- }
- c.close()
-
- // All icons fit the first screen
- assertThat(locMap.size).isEqualTo(5)
- assertThat(locMap[testPackage1]).isEqualTo(0)
- assertThat(locMap[testPackage2]).isEqualTo(0)
- assertThat(locMap[testPackage3]).isEqualTo(0)
- assertThat(locMap[testPackage4]).isEqualTo(0)
- assertThat(locMap[testPackage5]).isEqualTo(0)
-
- disableNewMigrationLogic()
- }
-
- private fun enableNewMigrationLogic(srcGridSize: String) {
- context.getSharedPreferences(FeatureFlags.FLAGS_PREF_NAME, Context.MODE_PRIVATE)
- .edit()
- .putBoolean(FeatureFlags.ENABLE_NEW_MIGRATION_LOGIC.key, true)
- .commit()
- context.getSharedPreferences(LauncherFiles.SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE)
- .edit()
- .putString(DeviceGridState.KEY_WORKSPACE_SIZE, srcGridSize)
- .commit()
- FeatureFlags.initialize(context)
- }
-
- private fun disableNewMigrationLogic() {
- context.getSharedPreferences(FeatureFlags.FLAGS_PREF_NAME, Context.MODE_PRIVATE)
- .edit()
- .putBoolean(FeatureFlags.ENABLE_NEW_MIGRATION_LOGIC.key, false)
- .commit()
- }
-}
\ No newline at end of file
diff --git a/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt b/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt
new file mode 100644
index 0000000..a85fa3a
--- /dev/null
+++ b/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt
@@ -0,0 +1,769 @@
+/*
+ * 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.model
+
+import android.content.Context
+import android.content.Intent
+import android.database.Cursor
+import android.database.sqlite.SQLiteDatabase
+import android.graphics.Point
+import android.os.Process
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.InvariantDeviceProfile
+import com.android.launcher3.LauncherPrefs
+import com.android.launcher3.LauncherPrefs.Companion.WORKSPACE_SIZE
+import com.android.launcher3.LauncherSettings.Favorites.*
+import com.android.launcher3.config.FeatureFlags
+import com.android.launcher3.model.GridSizeMigrationUtil.DbReader
+import com.android.launcher3.pm.UserCache
+import com.android.launcher3.provider.LauncherDbUtils
+import com.android.launcher3.util.LauncherModelHelper
+import com.android.launcher3.util.LauncherModelHelper.*
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** Unit tests for [GridSizeMigrationUtil] */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class GridSizeMigrationUtilTest {
+ private lateinit var modelHelper: LauncherModelHelper
+ private lateinit var context: Context
+ private lateinit var db: SQLiteDatabase
+ private lateinit var validPackages: Set<String>
+ private lateinit var idp: InvariantDeviceProfile
+ private val testPackage1 = "com.android.launcher3.validpackage1"
+ private val testPackage2 = "com.android.launcher3.validpackage2"
+ private val testPackage3 = "com.android.launcher3.validpackage3"
+ private val testPackage4 = "com.android.launcher3.validpackage4"
+ private val testPackage5 = "com.android.launcher3.validpackage5"
+ private val testPackage6 = "com.android.launcher3.validpackage6"
+ private val testPackage7 = "com.android.launcher3.validpackage7"
+ private val testPackage8 = "com.android.launcher3.validpackage8"
+ private val testPackage9 = "com.android.launcher3.validpackage9"
+ private val testPackage10 = "com.android.launcher3.validpackage10"
+
+ @Before
+ fun setUp() {
+ modelHelper = LauncherModelHelper()
+ context = modelHelper.sandboxContext
+ db = modelHelper.provider.db
+
+ validPackages =
+ setOf(
+ TEST_PACKAGE,
+ testPackage1,
+ testPackage2,
+ testPackage3,
+ testPackage4,
+ testPackage5,
+ testPackage6,
+ testPackage7,
+ testPackage8,
+ testPackage9,
+ testPackage10
+ )
+
+ idp = InvariantDeviceProfile.INSTANCE[context]
+ val userSerial = UserCache.INSTANCE[context].getSerialNumberForUser(Process.myUserHandle())
+ LauncherDbUtils.dropTable(db, TMP_TABLE)
+ addTableToDb(db, userSerial, false, TMP_TABLE)
+ }
+
+ @After
+ fun tearDown() {
+ modelHelper.destroy()
+ }
+
+ /**
+ * Old migration logic, should be modified once [FeatureFlags.ENABLE_NEW_MIGRATION_LOGIC] is not
+ * needed anymore
+ */
+ @Test
+ @Throws(Exception::class)
+ fun testMigration() {
+ // Src Hotseat icons
+ modelHelper.addItem(APP_ICON, 0, HOTSEAT, 0, 0, testPackage1, 1, TMP_CONTENT_URI)
+ modelHelper.addItem(SHORTCUT, 1, HOTSEAT, 0, 0, testPackage2, 2, TMP_CONTENT_URI)
+ modelHelper.addItem(SHORTCUT, 3, HOTSEAT, 0, 0, testPackage3, 3, TMP_CONTENT_URI)
+ modelHelper.addItem(APP_ICON, 4, HOTSEAT, 0, 0, testPackage4, 4, TMP_CONTENT_URI)
+ // Src grid icons
+ // _ _ _ _ _
+ // _ _ _ _ 5
+ // _ _ 6 _ 7
+ // _ _ 8 _ 9
+ // _ _ _ _ _
+ modelHelper.addItem(APP_ICON, 0, DESKTOP, 4, 1, testPackage5, 5, TMP_CONTENT_URI)
+ modelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 2, testPackage6, 6, TMP_CONTENT_URI)
+ modelHelper.addItem(APP_ICON, 0, DESKTOP, 4, 2, testPackage7, 7, TMP_CONTENT_URI)
+ modelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 3, testPackage8, 8, TMP_CONTENT_URI)
+ modelHelper.addItem(APP_ICON, 0, DESKTOP, 4, 3, testPackage9, 9, TMP_CONTENT_URI)
+
+ // Dest hotseat icons
+ modelHelper.addItem(SHORTCUT, 1, HOTSEAT, 0, 0, testPackage2)
+ // Dest grid icons
+ modelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 2, testPackage10)
+
+ idp.numDatabaseHotseatIcons = 4
+ idp.numColumns = 4
+ idp.numRows = 4
+ val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
+ val destReader = DbReader(db, TABLE_NAME, context, validPackages)
+ GridSizeMigrationUtil.migrate(
+ context,
+ db,
+ srcReader,
+ destReader,
+ idp.numDatabaseHotseatIcons,
+ Point(idp.numColumns, idp.numRows),
+ DeviceGridState(context),
+ DeviceGridState(idp)
+ )
+
+ // Check hotseat items
+ var c =
+ context.contentResolver.query(
+ CONTENT_URI,
+ arrayOf(SCREEN, INTENT),
+ "container=$CONTAINER_HOTSEAT",
+ null,
+ SCREEN,
+ null
+ )
+ ?: throw IllegalStateException()
+
+ assertThat(c.count).isEqualTo(idp.numDatabaseHotseatIcons)
+
+ val screenIndex = c.getColumnIndex(SCREEN)
+ var intentIndex = c.getColumnIndex(INTENT)
+ c.moveToNext()
+ assertThat(c.getInt(screenIndex).toLong()).isEqualTo(0)
+ assertThat(c.getString(intentIndex)).contains(testPackage1)
+ c.moveToNext()
+ assertThat(c.getInt(screenIndex).toLong()).isEqualTo(1)
+ assertThat(c.getString(intentIndex)).contains(testPackage2)
+ c.moveToNext()
+ assertThat(c.getInt(screenIndex).toLong()).isEqualTo(2)
+ assertThat(c.getString(intentIndex)).contains(testPackage3)
+ c.moveToNext()
+ assertThat(c.getInt(screenIndex).toLong()).isEqualTo(3)
+ assertThat(c.getString(intentIndex)).contains(testPackage4)
+ c.close()
+
+ // Check workspace items
+ c =
+ context.contentResolver.query(
+ CONTENT_URI,
+ arrayOf(CELLX, CELLY, INTENT),
+ "container=$CONTAINER_DESKTOP",
+ null,
+ null,
+ null
+ )
+ ?: throw IllegalStateException()
+
+ intentIndex = c.getColumnIndex(INTENT)
+ val cellXIndex = c.getColumnIndex(CELLX)
+ val cellYIndex = c.getColumnIndex(CELLY)
+ val locMap = HashMap<String, Point>()
+ while (c.moveToNext()) {
+ locMap[Intent.parseUri(c.getString(intentIndex), 0).getPackage()] =
+ Point(c.getInt(cellXIndex), c.getInt(cellYIndex))
+ }
+ c.close()
+ // Expected dest grid icons
+ // _ _ _ _
+ // 5 6 7 8
+ // 9 _ _ _
+ // _ _ _ _
+ assertThat(locMap.size.toLong()).isEqualTo(5)
+ assertThat(locMap[testPackage5]).isEqualTo(Point(0, 1))
+ assertThat(locMap[testPackage6]).isEqualTo(Point(1, 1))
+ assertThat(locMap[testPackage7]).isEqualTo(Point(2, 1))
+ assertThat(locMap[testPackage8]).isEqualTo(Point(3, 1))
+ assertThat(locMap[testPackage9]).isEqualTo(Point(0, 2))
+ }
+
+ /**
+ * Old migration logic, should be modified once [FeatureFlags.ENABLE_NEW_MIGRATION_LOGIC] is not
+ * needed anymore
+ */
+ @Test
+ @Throws(Exception::class)
+ fun testMigrationBackAndForth() {
+ // Hotseat items in grid A
+ // 1 2 _ 3 4
+ modelHelper.addItem(APP_ICON, 0, HOTSEAT, 0, 0, testPackage1, 1, TMP_CONTENT_URI)
+ modelHelper.addItem(SHORTCUT, 1, HOTSEAT, 0, 0, testPackage2, 2, TMP_CONTENT_URI)
+ modelHelper.addItem(SHORTCUT, 3, HOTSEAT, 0, 0, testPackage3, 3, TMP_CONTENT_URI)
+ modelHelper.addItem(APP_ICON, 4, HOTSEAT, 0, 0, testPackage4, 4, TMP_CONTENT_URI)
+ // Workspace items in grid A
+ // _ _ _ _ _
+ // _ _ _ _ 5
+ // _ _ 6 _ 7
+ // _ _ 8 _ _
+ // _ _ _ _ _
+ modelHelper.addItem(APP_ICON, 0, DESKTOP, 4, 1, testPackage5, 5, TMP_CONTENT_URI)
+ modelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 2, testPackage6, 6, TMP_CONTENT_URI)
+ modelHelper.addItem(APP_ICON, 0, DESKTOP, 4, 2, testPackage7, 7, TMP_CONTENT_URI)
+ modelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 3, testPackage8, 8, TMP_CONTENT_URI)
+
+ // Hotseat items in grid B
+ // 2 _ _ _
+ modelHelper.addItem(SHORTCUT, 0, HOTSEAT, 0, 0, testPackage2)
+ // Workspace items in grid B
+ // _ _ _ _
+ // _ _ _ 10
+ // _ _ _ _
+ // _ _ _ _
+ modelHelper.addItem(APP_ICON, 0, DESKTOP, 1, 3, testPackage10)
+
+ idp.numDatabaseHotseatIcons = 4
+ idp.numColumns = 4
+ idp.numRows = 4
+ val readerGridA = DbReader(db, TMP_TABLE, context, validPackages)
+ val readerGridB = DbReader(db, TABLE_NAME, context, validPackages)
+ // migrate from A -> B
+ GridSizeMigrationUtil.migrate(
+ context,
+ db,
+ readerGridA,
+ readerGridB,
+ idp.numDatabaseHotseatIcons,
+ Point(idp.numColumns, idp.numRows),
+ DeviceGridState(context),
+ DeviceGridState(idp)
+ )
+
+ // Check hotseat items in grid B
+ var c =
+ context.contentResolver.query(
+ CONTENT_URI,
+ arrayOf(SCREEN, INTENT),
+ "container=$CONTAINER_HOTSEAT",
+ null,
+ SCREEN,
+ null
+ )
+ ?: throw IllegalStateException()
+ // Expected hotseat items in grid B
+ // 2 1 3 4
+ verifyHotseat(
+ c,
+ idp,
+ mutableListOf(testPackage2, testPackage1, testPackage3, testPackage4).toList()
+ )
+
+ // Check workspace items in grid B
+ c =
+ context.contentResolver.query(
+ CONTENT_URI,
+ arrayOf(SCREEN, CELLX, CELLY, INTENT),
+ "container=$CONTAINER_DESKTOP",
+ null,
+ null,
+ null
+ )
+ ?: throw IllegalStateException()
+ var locMap = parseLocMap(context, c)
+ // Expected items in grid B
+ // _ _ _ _
+ // 5 6 7 8
+ // _ _ _ _
+ // _ _ _ _
+ assertThat(locMap.size.toLong()).isEqualTo(4)
+ assertThat(locMap[testPackage5]).isEqualTo(Triple(0, 0, 1))
+ assertThat(locMap[testPackage6]).isEqualTo(Triple(0, 1, 1))
+ assertThat(locMap[testPackage7]).isEqualTo(Triple(0, 2, 1))
+ assertThat(locMap[testPackage8]).isEqualTo(Triple(0, 3, 1))
+
+ // add item in B
+ modelHelper.addItem(APP_ICON, 0, DESKTOP, 0, 2, testPackage9)
+
+ // migrate from B -> A
+ GridSizeMigrationUtil.migrate(
+ context,
+ db,
+ readerGridB,
+ readerGridA,
+ 5,
+ Point(5, 5),
+ DeviceGridState(idp),
+ DeviceGridState(context)
+ )
+ // Check hotseat items in grid A
+ c =
+ context.contentResolver.query(
+ TMP_CONTENT_URI,
+ arrayOf(SCREEN, INTENT),
+ "container=$CONTAINER_HOTSEAT",
+ null,
+ SCREEN,
+ null
+ )
+ ?: throw IllegalStateException()
+ // Expected hotseat items in grid A
+ // 1 2 _ 3 4
+ verifyHotseat(
+ c,
+ idp,
+ mutableListOf(testPackage1, testPackage2, null, testPackage3, testPackage4).toList()
+ )
+
+ // Check workspace items in grid A
+ c =
+ context.contentResolver.query(
+ TMP_CONTENT_URI,
+ arrayOf(SCREEN, CELLX, CELLY, INTENT),
+ "container=$CONTAINER_DESKTOP",
+ null,
+ null,
+ null
+ )
+ ?: throw IllegalStateException()
+ locMap = parseLocMap(context, c)
+ // Expected workspace items in grid A
+ // _ _ _ _ _
+ // _ _ _ _ 5
+ // 9 _ 6 _ 7
+ // _ _ 8 _ _
+ // _ _ _ _ _
+ assertThat(locMap.size.toLong()).isEqualTo(5)
+ // Verify items that existed in grid A remains in same position
+ assertThat(locMap[testPackage5]).isEqualTo(Triple(0, 4, 1))
+ assertThat(locMap[testPackage6]).isEqualTo(Triple(0, 2, 2))
+ assertThat(locMap[testPackage7]).isEqualTo(Triple(0, 4, 2))
+ assertThat(locMap[testPackage8]).isEqualTo(Triple(0, 2, 3))
+ // Verify items that didn't exist in grid A are added in new screen
+ assertThat(locMap[testPackage9]).isEqualTo(Triple(0, 0, 2))
+
+ // remove item from B
+ modelHelper.deleteItem(7, TMP_TABLE)
+
+ // migrate from A -> B
+ GridSizeMigrationUtil.migrate(
+ context,
+ db,
+ readerGridA,
+ readerGridB,
+ idp.numDatabaseHotseatIcons,
+ Point(idp.numColumns, idp.numRows),
+ DeviceGridState(context),
+ DeviceGridState(idp)
+ )
+
+ // Check hotseat items in grid B
+ c =
+ context.contentResolver.query(
+ CONTENT_URI,
+ arrayOf(SCREEN, INTENT),
+ "container=$CONTAINER_HOTSEAT",
+ null,
+ SCREEN,
+ null
+ )
+ ?: throw IllegalStateException()
+ // Expected hotseat items in grid B
+ // 2 1 3 4
+ verifyHotseat(
+ c,
+ idp,
+ mutableListOf(testPackage2, testPackage1, testPackage3, testPackage4).toList()
+ )
+
+ // Check workspace items in grid B
+ c =
+ context.contentResolver.query(
+ CONTENT_URI,
+ arrayOf(SCREEN, CELLX, CELLY, INTENT),
+ "container=$CONTAINER_DESKTOP",
+ null,
+ null,
+ null
+ )
+ ?: throw IllegalStateException()
+ locMap = parseLocMap(context, c)
+ // Expected workspace items in grid B
+ // _ _ _ _
+ // 5 6 _ 8
+ // 9 _ _ _
+ // _ _ _ _
+ assertThat(locMap.size.toLong()).isEqualTo(4)
+ assertThat(locMap[testPackage5]).isEqualTo(Triple(0, 0, 1))
+ assertThat(locMap[testPackage6]).isEqualTo(Triple(0, 1, 1))
+ assertThat(locMap[testPackage8]).isEqualTo(Triple(0, 3, 1))
+ assertThat(locMap[testPackage9]).isEqualTo(Triple(0, 0, 2))
+ }
+
+ private fun verifyHotseat(c: Cursor, idp: InvariantDeviceProfile, expected: List<String?>) {
+ assertThat(c.count).isEqualTo(idp.numDatabaseHotseatIcons)
+ val screenIndex = c.getColumnIndex(SCREEN)
+ val intentIndex = c.getColumnIndex(INTENT)
+ expected.forEachIndexed { idx, pkg ->
+ if (pkg == null) return@forEachIndexed
+ c.moveToNext()
+ assertThat(c.getInt(screenIndex).toLong()).isEqualTo(idx)
+ assertThat(c.getString(intentIndex)).contains(pkg)
+ }
+ c.close()
+ }
+
+ private fun parseLocMap(context: Context, c: Cursor): Map<String, Triple<Int, Int, Int>> {
+ // Check workspace items
+ val intentIndex = c.getColumnIndex(INTENT)
+ val screenIndex = c.getColumnIndex(SCREEN)
+ val cellXIndex = c.getColumnIndex(CELLX)
+ val cellYIndex = c.getColumnIndex(CELLY)
+ val locMap = mutableMapOf<String, Triple<Int, Int, Int>>()
+ while (c.moveToNext()) {
+ locMap[Intent.parseUri(c.getString(intentIndex), 0).getPackage()] =
+ Triple(c.getInt(screenIndex), c.getInt(cellXIndex), c.getInt(cellYIndex))
+ }
+ c.close()
+ return locMap.toMap()
+ }
+
+ @Test
+ fun migrateToLargerHotseat() {
+ val srcHotseatItems =
+ intArrayOf(
+ modelHelper.addItem(APP_ICON, 0, HOTSEAT, 0, 0, testPackage1, 1, TMP_CONTENT_URI),
+ modelHelper.addItem(SHORTCUT, 1, HOTSEAT, 0, 0, testPackage2, 2, TMP_CONTENT_URI),
+ modelHelper.addItem(APP_ICON, 2, HOTSEAT, 0, 0, testPackage3, 3, TMP_CONTENT_URI),
+ modelHelper.addItem(SHORTCUT, 3, HOTSEAT, 0, 0, testPackage4, 4, TMP_CONTENT_URI)
+ )
+ val numSrcDatabaseHotseatIcons = srcHotseatItems.size
+ idp.numDatabaseHotseatIcons = 6
+ idp.numColumns = 4
+ idp.numRows = 4
+ val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
+ val destReader = DbReader(db, TABLE_NAME, context, validPackages)
+ GridSizeMigrationUtil.migrate(
+ context,
+ db,
+ srcReader,
+ destReader,
+ idp.numDatabaseHotseatIcons,
+ Point(idp.numColumns, idp.numRows),
+ DeviceGridState(context),
+ DeviceGridState(idp)
+ )
+
+ // Check hotseat items
+ val c =
+ context.contentResolver.query(
+ CONTENT_URI,
+ arrayOf(SCREEN, INTENT),
+ "container=$CONTAINER_HOTSEAT",
+ null,
+ SCREEN,
+ null
+ )
+ ?: throw IllegalStateException()
+
+ assertThat(c.count.toLong()).isEqualTo(numSrcDatabaseHotseatIcons.toLong())
+ val screenIndex = c.getColumnIndex(SCREEN)
+ val intentIndex = c.getColumnIndex(INTENT)
+ c.moveToNext()
+ assertThat(c.getInt(screenIndex)).isEqualTo(0)
+ assertThat(c.getString(intentIndex)).contains(testPackage1)
+
+ c.moveToNext()
+ assertThat(c.getInt(screenIndex)).isEqualTo(1)
+ assertThat(c.getString(intentIndex)).contains(testPackage2)
+
+ c.moveToNext()
+ assertThat(c.getInt(screenIndex)).isEqualTo(2)
+ assertThat(c.getString(intentIndex)).contains(testPackage3)
+
+ c.moveToNext()
+ assertThat(c.getInt(screenIndex)).isEqualTo(3)
+ assertThat(c.getString(intentIndex)).contains(testPackage4)
+
+ c.close()
+ }
+
+ @Test
+ fun migrateFromLargerHotseat() {
+ modelHelper.addItem(APP_ICON, 0, HOTSEAT, 0, 0, testPackage1, 1, TMP_CONTENT_URI)
+ modelHelper.addItem(SHORTCUT, 2, HOTSEAT, 0, 0, testPackage2, 2, TMP_CONTENT_URI)
+ modelHelper.addItem(APP_ICON, 3, HOTSEAT, 0, 0, testPackage3, 3, TMP_CONTENT_URI)
+ modelHelper.addItem(SHORTCUT, 4, HOTSEAT, 0, 0, testPackage4, 4, TMP_CONTENT_URI)
+ modelHelper.addItem(APP_ICON, 5, HOTSEAT, 0, 0, testPackage5, 5, TMP_CONTENT_URI)
+
+ idp.numDatabaseHotseatIcons = 4
+ idp.numColumns = 4
+ idp.numRows = 4
+ val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
+ val destReader = DbReader(db, TABLE_NAME, context, validPackages)
+ GridSizeMigrationUtil.migrate(
+ context,
+ db,
+ srcReader,
+ destReader,
+ idp.numDatabaseHotseatIcons,
+ Point(idp.numColumns, idp.numRows),
+ DeviceGridState(context),
+ DeviceGridState(idp)
+ )
+
+ // Check hotseat items
+ val c =
+ context.contentResolver.query(
+ CONTENT_URI,
+ arrayOf(SCREEN, INTENT),
+ "container=$CONTAINER_HOTSEAT",
+ null,
+ SCREEN,
+ null
+ )
+ ?: throw IllegalStateException()
+
+ assertThat(c.count.toLong()).isEqualTo(idp.numDatabaseHotseatIcons.toLong())
+ val screenIndex = c.getColumnIndex(SCREEN)
+ val intentIndex = c.getColumnIndex(INTENT)
+
+ c.moveToNext()
+ assertThat(c.getInt(screenIndex)).isEqualTo(0)
+ assertThat(c.getString(intentIndex)).contains(testPackage1)
+
+ c.moveToNext()
+ assertThat(c.getInt(screenIndex)).isEqualTo(1)
+ assertThat(c.getString(intentIndex)).contains(testPackage2)
+
+ c.moveToNext()
+ assertThat(c.getInt(screenIndex)).isEqualTo(2)
+ assertThat(c.getString(intentIndex)).contains(testPackage3)
+
+ c.moveToNext()
+ assertThat(c.getInt(screenIndex)).isEqualTo(3)
+ assertThat(c.getString(intentIndex)).contains(testPackage4)
+
+ c.close()
+ }
+
+ /**
+ * Migrating from a smaller grid to a large one should keep the pages if the column difference
+ * is less than 2
+ */
+ @Test
+ @Throws(Exception::class)
+ fun migrateFromSmallerGridSmallDifference() {
+ enableNewMigrationLogic("4,4")
+
+ // Setup src grid
+ modelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 2, testPackage1, 5, TMP_CONTENT_URI)
+ modelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 3, testPackage2, 6, TMP_CONTENT_URI)
+ modelHelper.addItem(APP_ICON, 1, DESKTOP, 3, 1, testPackage3, 7, TMP_CONTENT_URI)
+ modelHelper.addItem(APP_ICON, 1, DESKTOP, 3, 2, testPackage4, 8, TMP_CONTENT_URI)
+ modelHelper.addItem(APP_ICON, 2, DESKTOP, 3, 3, testPackage5, 9, TMP_CONTENT_URI)
+
+ idp.numDatabaseHotseatIcons = 4
+ idp.numColumns = 6
+ idp.numRows = 5
+
+ val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
+ val destReader = DbReader(db, TABLE_NAME, context, validPackages)
+ GridSizeMigrationUtil.migrate(
+ context,
+ db,
+ srcReader,
+ destReader,
+ idp.numDatabaseHotseatIcons,
+ Point(idp.numColumns, idp.numRows),
+ DeviceGridState(context),
+ DeviceGridState(idp)
+ )
+
+ // Get workspace items
+ val c =
+ context.contentResolver.query(
+ CONTENT_URI,
+ arrayOf(INTENT, SCREEN),
+ "container=$CONTAINER_DESKTOP",
+ null,
+ null,
+ null
+ )
+ ?: throw IllegalStateException()
+ val intentIndex = c.getColumnIndex(INTENT)
+ val screenIndex = c.getColumnIndex(SCREEN)
+
+ // Get in which screen the icon is
+ val locMap = HashMap<String, Int>()
+ while (c.moveToNext()) {
+ locMap[Intent.parseUri(c.getString(intentIndex), 0).getPackage()] =
+ c.getInt(screenIndex)
+ }
+ c.close()
+ assertThat(locMap.size).isEqualTo(5)
+ assertThat(locMap[testPackage1]).isEqualTo(0)
+ assertThat(locMap[testPackage2]).isEqualTo(0)
+ assertThat(locMap[testPackage3]).isEqualTo(1)
+ assertThat(locMap[testPackage4]).isEqualTo(1)
+ assertThat(locMap[testPackage5]).isEqualTo(2)
+
+ disableNewMigrationLogic()
+ }
+
+ /**
+ * Migrating from a smaller grid to a large one should reflow the pages if the column difference
+ * is more than 2
+ */
+ @Test
+ @Throws(Exception::class)
+ fun migrateFromSmallerGridBigDifference() {
+ enableNewMigrationLogic("2,2")
+
+ // Setup src grid
+ modelHelper.addItem(APP_ICON, 0, DESKTOP, 0, 1, testPackage1, 5, TMP_CONTENT_URI)
+ modelHelper.addItem(APP_ICON, 0, DESKTOP, 1, 1, testPackage2, 6, TMP_CONTENT_URI)
+ modelHelper.addItem(APP_ICON, 1, DESKTOP, 0, 0, testPackage3, 7, TMP_CONTENT_URI)
+ modelHelper.addItem(APP_ICON, 1, DESKTOP, 1, 0, testPackage4, 8, TMP_CONTENT_URI)
+ modelHelper.addItem(APP_ICON, 2, DESKTOP, 0, 0, testPackage5, 9, TMP_CONTENT_URI)
+
+ idp.numDatabaseHotseatIcons = 4
+ idp.numColumns = 5
+ idp.numRows = 5
+ val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
+ val destReader = DbReader(db, TABLE_NAME, context, validPackages)
+ GridSizeMigrationUtil.migrate(
+ context,
+ db,
+ srcReader,
+ destReader,
+ idp.numDatabaseHotseatIcons,
+ Point(idp.numColumns, idp.numRows),
+ DeviceGridState(context),
+ DeviceGridState(idp)
+ )
+
+ // Get workspace items
+ val c =
+ context.contentResolver.query(
+ CONTENT_URI,
+ arrayOf(INTENT, SCREEN),
+ "container=$CONTAINER_DESKTOP",
+ null,
+ null,
+ null
+ )
+ ?: throw IllegalStateException()
+
+ val intentIndex = c.getColumnIndex(INTENT)
+ val screenIndex = c.getColumnIndex(SCREEN)
+
+ // Get in which screen the icon is
+ val locMap = HashMap<String, Int>()
+ while (c.moveToNext()) {
+ locMap[Intent.parseUri(c.getString(intentIndex), 0).getPackage()] =
+ c.getInt(screenIndex)
+ }
+ c.close()
+
+ // All icons fit the first screen
+ assertThat(locMap.size).isEqualTo(5)
+ assertThat(locMap[testPackage1]).isEqualTo(0)
+ assertThat(locMap[testPackage2]).isEqualTo(0)
+ assertThat(locMap[testPackage3]).isEqualTo(0)
+ assertThat(locMap[testPackage4]).isEqualTo(0)
+ assertThat(locMap[testPackage5]).isEqualTo(0)
+ disableNewMigrationLogic()
+ }
+
+ /** Migrating from a larger grid to a smaller, we reflow from page 0 */
+ @Test
+ @Throws(Exception::class)
+ fun migrateFromLargerGrid() {
+ enableNewMigrationLogic("5,5")
+
+ // Setup src grid
+ modelHelper.addItem(APP_ICON, 0, DESKTOP, 0, 1, testPackage1, 5, TMP_CONTENT_URI)
+ modelHelper.addItem(APP_ICON, 0, DESKTOP, 1, 1, testPackage2, 6, TMP_CONTENT_URI)
+ modelHelper.addItem(APP_ICON, 1, DESKTOP, 0, 0, testPackage3, 7, TMP_CONTENT_URI)
+ modelHelper.addItem(APP_ICON, 1, DESKTOP, 1, 0, testPackage4, 8, TMP_CONTENT_URI)
+ modelHelper.addItem(APP_ICON, 2, DESKTOP, 0, 0, testPackage5, 9, TMP_CONTENT_URI)
+
+ idp.numDatabaseHotseatIcons = 4
+ idp.numColumns = 4
+ idp.numRows = 4
+ val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
+ val destReader = DbReader(db, TABLE_NAME, context, validPackages)
+ GridSizeMigrationUtil.migrate(
+ context,
+ db,
+ srcReader,
+ destReader,
+ idp.numDatabaseHotseatIcons,
+ Point(idp.numColumns, idp.numRows),
+ DeviceGridState(context),
+ DeviceGridState(idp)
+ )
+
+ // Get workspace items
+ val c =
+ context.contentResolver.query(
+ CONTENT_URI,
+ arrayOf(INTENT, SCREEN),
+ "container=$CONTAINER_DESKTOP",
+ null,
+ null,
+ null
+ )
+ ?: throw IllegalStateException()
+ val intentIndex = c.getColumnIndex(INTENT)
+ val screenIndex = c.getColumnIndex(SCREEN)
+
+ // Get in which screen the icon is
+ val locMap = HashMap<String, Int>()
+ while (c.moveToNext()) {
+ locMap[Intent.parseUri(c.getString(intentIndex), 0).getPackage()] =
+ c.getInt(screenIndex)
+ }
+ c.close()
+
+ // All icons fit the first screen
+ assertThat(locMap.size).isEqualTo(5)
+ assertThat(locMap[testPackage1]).isEqualTo(0)
+ assertThat(locMap[testPackage2]).isEqualTo(0)
+ assertThat(locMap[testPackage3]).isEqualTo(0)
+ assertThat(locMap[testPackage4]).isEqualTo(0)
+ assertThat(locMap[testPackage5]).isEqualTo(0)
+
+ disableNewMigrationLogic()
+ }
+
+ private fun enableNewMigrationLogic(srcGridSize: String) {
+ context
+ .getSharedPreferences(FeatureFlags.FLAGS_PREF_NAME, Context.MODE_PRIVATE)
+ .edit()
+ .putBoolean(FeatureFlags.ENABLE_NEW_MIGRATION_LOGIC.key, true)
+ .commit()
+ LauncherPrefs.get(context).putSync(WORKSPACE_SIZE.to(srcGridSize))
+ FeatureFlags.initialize(context)
+ }
+
+ private fun disableNewMigrationLogic() {
+ context
+ .getSharedPreferences(FeatureFlags.FLAGS_PREF_NAME, Context.MODE_PRIVATE)
+ .edit()
+ .putBoolean(FeatureFlags.ENABLE_NEW_MIGRATION_LOGIC.key, false)
+ .commit()
+ }
+}
diff --git a/tests/src/com/android/launcher3/model/WorkspaceItemSpaceFinderTest.kt b/tests/src/com/android/launcher3/model/WorkspaceItemSpaceFinderTest.kt
index bfb1ac6..b3d02be 100644
--- a/tests/src/com/android/launcher3/model/WorkspaceItemSpaceFinderTest.kt
+++ b/tests/src/com/android/launcher3/model/WorkspaceItemSpaceFinderTest.kt
@@ -24,9 +24,7 @@
import org.junit.Test
import org.junit.runner.RunWith
-/**
- * Tests for [WorkspaceItemSpaceFinder]
- */
+/** Tests for [WorkspaceItemSpaceFinder] */
@SmallTest
@RunWith(AndroidJUnit4::class)
class WorkspaceItemSpaceFinderTest : AbstractWorkspaceModelTest() {
@@ -44,17 +42,27 @@
}
private fun findSpace(spanX: Int, spanY: Int): NewItemSpace =
- mItemSpaceFinder.findSpaceForItem(
- mAppState, mModelHelper.bgDataModel,
- mExistingScreens, mNewScreens, spanX, spanY
- )
+ mItemSpaceFinder
+ .findSpaceForItem(
+ mAppState,
+ mModelHelper.bgDataModel,
+ mExistingScreens,
+ mNewScreens,
+ spanX,
+ spanY
+ )
.let { NewItemSpace.fromIntArray(it) }
private fun assertRegionVacant(newItemSpace: NewItemSpace, spanX: Int, spanY: Int) {
assertThat(
- mScreenOccupancy[newItemSpace.screenId]
- .isRegionVacant(newItemSpace.cellX, newItemSpace.cellY, spanX, spanY)
- ).isTrue()
+ mScreenOccupancy[newItemSpace.screenId].isRegionVacant(
+ newItemSpace.cellX,
+ newItemSpace.cellY,
+ spanX,
+ spanY
+ )
+ )
+ .isTrue()
}
@Test
diff --git a/tests/src/com/android/launcher3/nonquickstep/HotseatWidthCalculationTest.kt b/tests/src/com/android/launcher3/nonquickstep/HotseatWidthCalculationTest.kt
new file mode 100644
index 0000000..951f5f8
--- /dev/null
+++ b/tests/src/com/android/launcher3/nonquickstep/HotseatWidthCalculationTest.kt
@@ -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.launcher3.nonquickstep
+
+import android.graphics.Rect
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.DeviceProfileBaseTest
+import com.android.launcher3.util.WindowBounds
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class HotseatWidthCalculationTest : DeviceProfileBaseTest() {
+
+ /**
+ * This is a case when after setting the hotseat, the space needs to be recalculated but it
+ * doesn't need to change QSB width or remove icons
+ */
+ @Test
+ fun distribute_border_space_when_space_is_enough_portrait() {
+ initializeVarsForTablet(isGestureMode = false)
+ windowBounds = WindowBounds(Rect(0, 0, 1800, 2560), Rect(0, 104, 0, 0))
+ val dp = newDP()
+ dp.isTaskbarPresentInApps = true
+
+ assertThat(dp.hotseatBarEndOffset).isEqualTo(0)
+ assertThat(dp.numShownHotseatIcons).isEqualTo(6)
+ assertThat(dp.hotseatBorderSpace).isEqualTo(145)
+
+ assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(177)
+ assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(177)
+
+ assertThat(dp.isQsbInline).isFalse()
+ assertThat(dp.hotseatQsbWidth).isEqualTo(1445)
+ }
+
+ /**
+ * This is a case when after setting the hotseat, and recalculating spaces it still needs to
+ * remove icons for everything to fit
+ */
+ @Test
+ fun decrease_num_of_icons_when_not_enough_space_portrait() {
+ initializeVarsForTablet(isGestureMode = false)
+ windowBounds = WindowBounds(Rect(0, 0, 1300, 2560), Rect(0, 104, 0, 0))
+ val dp = newDP()
+ dp.isTaskbarPresentInApps = true
+
+ assertThat(dp.hotseatBarEndOffset).isEqualTo(0)
+ assertThat(dp.numShownHotseatIcons).isEqualTo(6)
+ assertThat(dp.hotseatBorderSpace).isEqualTo(72)
+
+ assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(110)
+ assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(110)
+
+ assertThat(dp.isQsbInline).isFalse()
+ assertThat(dp.hotseatQsbWidth).isEqualTo(1080)
+ }
+
+ /**
+ * This is a case when after setting the hotseat, the space needs to be recalculated but it
+ * doesn't need to change QSB width or remove icons
+ */
+ @Test
+ fun distribute_border_space_when_space_is_enough_landscape() {
+ initializeVarsForTwoPanel(isGestureMode = false, isLandscape = true)
+ val dp = newDP()
+ dp.isTaskbarPresentInApps = true
+
+ assertThat(dp.hotseatBarEndOffset).isEqualTo(0)
+ assertThat(dp.numShownHotseatIcons).isEqualTo(6)
+ assertThat(dp.hotseatBorderSpace).isEqualTo(104)
+
+ assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(370)
+ assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(370)
+
+ assertThat(dp.isQsbInline).isFalse()
+ assertThat(dp.hotseatQsbWidth).isEqualTo(1468)
+ }
+
+ /**
+ * This is a case when the hotseat spans a certain amount of columns and the nav buttons push
+ * the hotseat to the side, but not enough to change the border space.
+ */
+ @Test
+ fun nav_buttons_dont_interfere_with_required_hotseat_width() {
+ initializeVarsForTablet(isGestureMode = false, isLandscape = true)
+ inv?.apply {
+ hotseatColumnSpan = IntArray(4) { 4 }
+ inlineQsb = BooleanArray(4) { false }
+ }
+ val dp = newDP()
+ dp.isTaskbarPresentInApps = true
+
+ assertThat(dp.hotseatBarEndOffset).isEqualTo(0)
+ assertThat(dp.numShownHotseatIcons).isEqualTo(6)
+ assertThat(dp.hotseatBorderSpace).isEqualTo(100)
+
+ assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(668)
+ assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(668)
+
+ assertThat(dp.isQsbInline).isFalse()
+ assertThat(dp.hotseatQsbWidth).isEqualTo(1224)
+ }
+
+ /** This is a case when after setting the hotseat, the QSB width needs to be changed to fit */
+ @Test
+ fun decrease_qsb_when_not_enough_space_landscape() {
+ initializeVarsForTablet(isGestureMode = false, isLandscape = true)
+ windowBounds = WindowBounds(Rect(0, 0, 2460, 1600), Rect(0, 104, 0, 0))
+ val dp = newDP()
+ dp.isTaskbarPresentInApps = true
+
+ assertThat(dp.hotseatBarEndOffset).isEqualTo(0)
+ assertThat(dp.numShownHotseatIcons).isEqualTo(6)
+ assertThat(dp.hotseatBorderSpace).isEqualTo(91)
+
+ assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(640)
+ assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(640)
+
+ assertThat(dp.isQsbInline).isFalse()
+ assertThat(dp.hotseatQsbWidth).isEqualTo(1179)
+ }
+
+ /**
+ * This is a case when after setting the hotseat, changing QSB width, and recalculating spaces
+ * it still needs to remove icons for everything to fit
+ */
+ @Test
+ fun decrease_num_of_icons_when_not_enough_space_landscape() {
+ initializeVarsForTablet(isGestureMode = false, isLandscape = true)
+ windowBounds = WindowBounds(Rect(0, 0, 2260, 1600), Rect(0, 104, 0, 0))
+ val dp = newDP()
+ dp.isTaskbarPresentInApps = true
+
+ assertThat(dp.hotseatBarEndOffset).isEqualTo(0)
+ assertThat(dp.numShownHotseatIcons).isEqualTo(6)
+ assertThat(dp.hotseatBorderSpace).isEqualTo(75)
+
+ assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(582)
+ assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(582)
+
+ assertThat(dp.isQsbInline).isFalse()
+ assertThat(dp.hotseatQsbWidth).isEqualTo(1095)
+ }
+}
diff --git a/tests/src/com/android/launcher3/search/StringMatcherUtilityTest.java b/tests/src/com/android/launcher3/search/StringMatcherUtilityTest.java
index 413f404..3b53255 100644
--- a/tests/src/com/android/launcher3/search/StringMatcherUtilityTest.java
+++ b/tests/src/com/android/launcher3/search/StringMatcherUtilityTest.java
@@ -24,6 +24,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.launcher3.search.StringMatcherUtility.StringMatcher;
+import com.android.launcher3.search.StringMatcherUtility.StringMatcherSpace;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -34,11 +35,12 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
public class StringMatcherUtilityTest {
- private static final StringMatcher MATCHER =
- StringMatcher.getInstance();
+ private static final StringMatcher MATCHER = StringMatcher.getInstance();
+ private static final StringMatcherSpace MATCHER_SPACE = StringMatcherSpace.getInstance();
@Test
public void testMatches() {
+ assertTrue(matches("white", "white cow", MATCHER));
assertTrue(matches("white ", "white cow", MATCHER));
assertTrue(matches("white c", "white cow", MATCHER));
assertTrue(matches("cow", "white cow", MATCHER));
@@ -93,4 +95,47 @@
assertFalse(matches("ㄷ", "로드라이브", MATCHER));
assertFalse(matches("åç", "abc", MATCHER));
}
+
+ @Test
+ public void testMatchesWithSpaceBreakOnly() {
+ assertTrue(matches("white", "white cow", MATCHER_SPACE));
+ assertTrue(matches("white ", "white cow", MATCHER_SPACE));
+ assertTrue(matches("white c", "white cow", MATCHER_SPACE));
+ assertTrue(matches("cow", "white cow", MATCHER_SPACE));
+ assertTrue(matches("cow", "whitecow cow", MATCHER_SPACE));
+
+ assertFalse(matches("cow", "whiteCow", MATCHER_SPACE));
+ assertFalse(matches("cow", "whiteCOW", MATCHER_SPACE));
+ assertFalse(matches("cow", "whitecowCOW", MATCHER_SPACE));
+ assertFalse(matches("cow", "white2cow", MATCHER_SPACE));
+ assertFalse(matches("cow", "whitecow", MATCHER_SPACE));
+ assertFalse(matches("cow", "whitEcow", MATCHER_SPACE));
+ assertFalse(matches("cow", "whitecowCow", MATCHER_SPACE));
+ assertFalse(matches("cow", "whitecowcow", MATCHER_SPACE));
+ assertFalse(matches("cow", "whit ecowcow", MATCHER_SPACE));
+
+ assertFalse(matches("dog", "cats&dogs", MATCHER_SPACE));
+ assertFalse(matches("dog", "cats&Dogs", MATCHER_SPACE));
+ assertFalse(matches("&", "cats&Dogs", MATCHER_SPACE));
+
+ assertFalse(matches("43", "2+43", MATCHER_SPACE));
+ assertFalse(matches("3", "2+43", MATCHER_SPACE));
+
+ assertTrue(matches("q", "Q", MATCHER_SPACE));
+ assertTrue(matches("q", " Q", MATCHER_SPACE));
+
+ // match lower case words
+ assertTrue(matches("e", "elephant", MATCHER_SPACE));
+ assertTrue(matches("eL", "Elephant", MATCHER_SPACE));
+
+ assertTrue(matches("电", "电子邮件", MATCHER_SPACE));
+ assertTrue(matches("电子", "电子邮件", MATCHER_SPACE));
+ assertTrue(matches("子", "电子邮件", MATCHER_SPACE));
+ assertTrue(matches("邮件", "电子邮件", MATCHER_SPACE));
+
+ assertFalse(matches("ba", "Bot", MATCHER_SPACE));
+ assertFalse(matches("ba", "bot", MATCHER_SPACE));
+ assertFalse(matches("phant", "elephant", MATCHER_SPACE));
+ assertFalse(matches("elephants", "elephant", MATCHER_SPACE));
+ }
}
diff --git a/tests/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncherTest.java b/tests/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncherTest.java
index 93fa705..082e243 100644
--- a/tests/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncherTest.java
+++ b/tests/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncherTest.java
@@ -15,39 +15,273 @@
*/
package com.android.launcher3.secondarydisplay;
-import static androidx.test.core.app.ActivityScenario.launch;
+import static android.content.Context.MODE_PRIVATE;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.view.MotionEvent.ACTION_DOWN;
-import androidx.test.core.app.ActivityScenario;
-import androidx.test.espresso.intent.Intents;
+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.MediumTest;
+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.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
/**
- * Tests for {@link SecondaryDisplayLauncher}
+ * Tests for {@link SecondaryDisplayLauncher}.
+ * TODO (b/242776943): Remove anti-patterns & migrate prediction row tests to Quickstep directory
*/
-@MediumTest
+@LargeTest
@RunWith(AndroidJUnit4.class)
-public class SecondaryDisplayLauncherTest {
+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;
- @Before
- public void setUp() {
- Intents.init();
+ 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() {
- Intents.release();
+ mTargetContext.getSharedPreferences(PINNED_APPS_KEY, MODE_PRIVATE)
+ .edit().clear().commit();
}
@Test
- public void testAllAppsListOpens() {
- ActivityScenario<SecondaryDisplayLauncher> launcher =
- launch(SecondaryDisplayLauncher.class);
- launcher.onActivity(l -> l.showAppDrawer(true));
+ @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/quickstep/res/values-sw720dp-land/dimens.xml b/tests/src/com/android/launcher3/testcomponent/DialogTestActivity.java
similarity index 63%
rename from quickstep/res/values-sw720dp-land/dimens.xml
rename to tests/src/com/android/launcher3/testcomponent/DialogTestActivity.java
index 02d1189..9e5a274 100644
--- a/quickstep/res/values-sw720dp-land/dimens.xml
+++ b/tests/src/com/android/launcher3/testcomponent/DialogTestActivity.java
@@ -1,20 +1,23 @@
-<?xml version="1.0" encoding="utf-8"?><!--
- * Copyright (c) 2022, 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.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
-*/
--->
-<resources>
- <!-- Overview actions -->
- <dimen name="overview_actions_top_margin">20dp</dimen>
-</resources>
+ */
+
+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..4f6cc64 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -63,7 +63,7 @@
import com.android.launcher3.testcomponent.TestCommandReceiver;
import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.util.LooperExecutor;
-import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.util.SimpleBroadcastReceiver;
import com.android.launcher3.util.Wait;
import com.android.launcher3.util.WidgetUtils;
import com.android.launcher3.util.rule.FailureWatcher;
@@ -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) {
@@ -194,15 +196,10 @@
protected void clearPackageData(String pkg) throws IOException, InterruptedException {
final CountDownLatch count = new CountDownLatch(2);
- final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- count.countDown();
- }
- };
- mTargetContext.registerReceiver(broadcastReceiver,
- PackageManagerHelper.getPackageFilter(pkg,
- Intent.ACTION_PACKAGE_RESTARTED, Intent.ACTION_PACKAGE_DATA_CLEARED));
+ final SimpleBroadcastReceiver broadcastReceiver =
+ new SimpleBroadcastReceiver(i -> count.countDown());
+ broadcastReceiver.registerPkgActions(mTargetContext, pkg,
+ Intent.ACTION_PACKAGE_RESTARTED, Intent.ACTION_PACKAGE_DATA_CLEARED);
mDevice.executeShellCommand("pm clear " + pkg);
assertTrue(pkg + " didn't restart", count.await(10, TimeUnit.SECONDS));
@@ -297,7 +294,7 @@
/**
* Removes all icons from homescreen and hotseat.
*/
- public void clearHomescreen() throws Throwable {
+ public void clearHomescreen() {
LauncherSettings.Settings.call(mTargetContext.getContentResolver(),
LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
LauncherSettings.Settings.call(mTargetContext.getContentResolver(),
@@ -319,7 +316,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();
@@ -437,7 +434,8 @@
private Intent mIntent;
public BlockingBroadcastReceiver(String action) {
- mTargetContext.registerReceiver(this, new IntentFilter(action));
+ mTargetContext.registerReceiver(this, new IntentFilter(action),
+ Context.RECEIVER_EXPORTED/*UNAUDITED*/);
}
@Override
@@ -477,6 +475,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 +533,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 c383997..fb8ac4f 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -29,7 +29,9 @@
import android.content.Intent;
import android.graphics.Point;
+import android.platform.test.annotations.IwTest;
+import androidx.test.filters.FlakyTest;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
@@ -194,6 +196,15 @@
@Test
@PortraitLandscape
+ public void testAllAppsSwitchToWorkspace() {
+ assertNotNull("switchToWorkspace() returned null",
+ mLauncher.getWorkspace().switchToAllApps().switchToWorkspace());
+ assertTrue("Launcher internal state is not Workspace",
+ isInState(() -> LauncherState.NORMAL));
+ }
+
+ @Test
+ @PortraitLandscape
public void testAllAppsDeadzoneForTablet() throws Exception {
assumeTrue(mLauncher.isTablet());
@@ -203,6 +214,7 @@
false /* tapRight */);
}
+ @IwTest(focusArea="launcher")
@Test
@ScreenRecord // b/202433017
public void testWorkspace() throws Exception {
@@ -211,9 +223,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 +312,7 @@
}
private int getWidgetsScroll(Launcher launcher) {
- return getWidgetsView(launcher).getCurrentScrollY();
+ return getWidgetsView(launcher).computeVerticalScrollOffset();
}
private boolean isOptionsPopupVisible(Launcher launcher) {
@@ -330,8 +342,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.
@@ -417,6 +431,7 @@
MAPS_APP_NAME);
}
+ @FlakyTest(bugId = 256615483)
@Test
@PortraitLandscape
public void testPressBack() throws Exception {
@@ -432,19 +447,20 @@
}
@Test
- public void testDisableRotationCheck() throws Exception {
- try {
- mLauncher.setExpectedRotationCheckEnabled(false);
- mLauncher.setEnableRotation(false);
- final AllApps allApps = mLauncher.getWorkspace().switchToAllApps();
- final AppIcon app = allApps.getAppIcon("TestActivity7");
- mLauncher.getDevice().setOrientationLeft();
- mLauncher.goHome();
- } finally {
- mLauncher.setExpectedRotationCheckEnabled(true);
- mLauncher.setEnableRotation(true);
- mLauncher.getDevice().setOrientationNatural();
- }
+ @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
@@ -471,10 +487,11 @@
}
}
+ @Ignore("b/256615483")
@Test
@PortraitLandscape
public void testUninstallFromWorkspace() throws Exception {
- TestUtil.installDummyApp();
+ installDummyAppAndWaitForUIUpdate();
try {
verifyAppUninstalledFromAllApps(
createShortcutInCenterIfNotExist(DUMMY_APP_NAME).uninstall(), DUMMY_APP_NAME);
@@ -485,8 +502,9 @@
@Test
@PortraitLandscape
+ @ScreenRecord // (b/256659409)
public void testUninstallFromAllApps() throws Exception {
- TestUtil.installDummyApp();
+ installDummyAppAndWaitForUIUpdate();
try {
Workspace workspace = mLauncher.getWorkspace();
final HomeAllApps allApps = workspace.switchToAllApps();
@@ -527,11 +545,12 @@
}
@Test
+ @ScreenRecord // b/241821721
public void getIconsPosition_afterIconRemoved_notContained() throws IOException {
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 =
@@ -582,6 +601,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()}
@@ -600,4 +630,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..c7628cc 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;
@@ -37,6 +38,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import java.util.Objects;
@@ -47,6 +49,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 +59,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 +108,8 @@
@Test
public void workTabExists() {
+ assumeTrue(mWorkProfileSetupSuccessful);
+ waitForWorkTabSetup();
waitForLauncherCondition("Personal tab is missing",
launcher -> launcher.getAppsView().isPersonalTabVisible(),
LauncherInstrumentation.WAIT_TIME_MS);
@@ -108,9 +119,10 @@
}
@Test
+ @Ignore("b/243855320")
public void toggleWorks() {
+ assumeTrue(mWorkProfileSetupSuccessful);
waitForWorkTabSetup();
-
executeOnLauncher(launcher -> {
AllAppsPagedView pagedView = (AllAppsPagedView) launcher.getAppsView().getContentView();
pagedView.setCurrentPage(WORK_PAGE);
@@ -126,7 +138,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 +168,7 @@
@Test
public void testEdu() {
+ assumeTrue(mWorkProfileSetupSuccessful);
waitForWorkTabSetup();
executeOnLauncher(l -> {
l.getSharedPrefs().edit().putInt(WorkProfileManager.KEY_WORK_EDU_STEP, 0).commit();
@@ -174,4 +191,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/AddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
index 2c9785c..655a77e 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
@@ -33,6 +33,7 @@
import com.android.launcher3.testcomponent.WidgetConfigActivity;
import com.android.launcher3.ui.AbstractLauncherUiTest;
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;
@@ -72,6 +73,7 @@
@Test
@PortraitLandscape
+ @ScreenRecord // b/206481237
public void testConfigCancelled() throws Throwable {
runTest(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..0fccf79 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;
@@ -31,6 +33,7 @@
import com.android.launcher3.util.rule.ShellCommandRule;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -45,12 +48,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 */);
@@ -80,6 +86,7 @@
* A custom shortcut is a 1x1 widget that launches a specific intent when user tap on it.
* Custom shortcuts are replaced by deep shortcuts after api 25.
*/
+ @Ignore
@Test
@PortraitLandscape
public void testDragCustomShortcut() throws Throwable {
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/ui/widget/RequestPinItemTest.java b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
index 0db719e..9669010 100644
--- a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
@@ -147,7 +147,8 @@
// Set callback
PendingIntent callback = PendingIntent.getBroadcast(mTargetContext, 0,
- new Intent(mCallbackAction), FLAG_ONE_SHOT | FLAG_MUTABLE);
+ new Intent(mCallbackAction).setPackage(mTargetContext.getPackageName()),
+ FLAG_ONE_SHOT | FLAG_MUTABLE);
mTargetContext.sendBroadcast(RequestPinItemActivity.getCommandIntent(
RequestPinItemActivity.class, "setCallback").putExtra(
RequestPinItemActivity.EXTRA_PARAM + "0", callback));
diff --git a/tests/src/com/android/launcher3/ui/workspace/ThemeIconsTest.java b/tests/src/com/android/launcher3/ui/workspace/ThemeIconsTest.java
index e66810c..7ba0b53 100644
--- a/tests/src/com/android/launcher3/ui/workspace/ThemeIconsTest.java
+++ b/tests/src/com/android/launcher3/ui/workspace/ThemeIconsTest.java
@@ -32,8 +32,10 @@
import com.android.launcher3.icons.ThemedIconDrawable;
import com.android.launcher3.tapl.HomeAllApps;
import com.android.launcher3.tapl.HomeAppIcon;
+import com.android.launcher3.tapl.HomeAppIconMenuItem;
import com.android.launcher3.ui.AbstractLauncherUiTest;
import com.android.launcher3.ui.TaplTestsLauncher3;
+import com.android.launcher3.util.Executors;
import org.junit.Test;
@@ -48,7 +50,9 @@
@LargeTest
public class ThemeIconsTest extends AbstractLauncherUiTest {
- private static final String APP_NAME = "ThemeIconTestActivity";
+ private static final String APP_NAME = "IconThemedActivity";
+ private static final String SHORTCUT_APP_NAME = "LauncherTestApp";
+ private static final String SHORTCUT_NAME = "Shortcut 1";
@Test
public void testIconWithoutTheme() throws Exception {
@@ -60,9 +64,28 @@
try {
HomeAppIcon icon = allApps.getAppIcon(APP_NAME);
- executeOnLauncher(l -> verifyIconTheme(l.getAppsView(), false));
+ executeOnLauncher(l -> verifyIconTheme(APP_NAME, l.getAppsView(), false));
icon.dragToWorkspace(false, false);
- executeOnLauncher(l -> verifyIconTheme(l.getWorkspace(), false));
+ executeOnLauncher(l -> verifyIconTheme(APP_NAME, l.getWorkspace(), false));
+ } finally {
+ allApps.unfreeze();
+ }
+ }
+
+ @Test
+ public void testShortcutIconWithoutTheme() throws Exception {
+ setThemeEnabled(false);
+ TaplTestsLauncher3.initialize(this);
+
+ HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps();
+ allApps.freeze();
+
+ try {
+ HomeAppIcon icon = allApps.getAppIcon(SHORTCUT_APP_NAME);
+ HomeAppIconMenuItem shortcutItem =
+ (HomeAppIconMenuItem) icon.openDeepShortcutMenu().getMenuItem(SHORTCUT_NAME);
+ shortcutItem.dragToWorkspace(false, false);
+ executeOnLauncher(l -> verifyIconTheme(SHORTCUT_NAME, l.getWorkspace(), false));
} finally {
allApps.unfreeze();
}
@@ -78,15 +101,41 @@
try {
HomeAppIcon icon = allApps.getAppIcon(APP_NAME);
- executeOnLauncher(l -> verifyIconTheme(l.getAppsView(), false));
+ executeOnLauncher(l -> verifyIconTheme(APP_NAME, l.getAppsView(), false));
icon.dragToWorkspace(false, false);
- executeOnLauncher(l -> verifyIconTheme(l.getWorkspace(), true));
+ executeOnLauncher(l -> verifyIconTheme(APP_NAME, l.getWorkspace(), true));
} finally {
allApps.unfreeze();
}
}
- private void verifyIconTheme(ViewGroup parent, boolean isThemed) {
+ @Test
+ public void testShortcutIconWithTheme() throws Exception {
+ setThemeEnabled(true);
+ TaplTestsLauncher3.initialize(this);
+
+ HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps();
+ allApps.freeze();
+
+ try {
+ HomeAppIcon icon = allApps.getAppIcon(SHORTCUT_APP_NAME);
+ HomeAppIconMenuItem shortcutItem =
+ (HomeAppIconMenuItem) icon.openDeepShortcutMenu().getMenuItem(SHORTCUT_NAME);
+ shortcutItem.dragToWorkspace(false, false);
+ executeOnLauncher(l -> verifyIconTheme(SHORTCUT_NAME, l.getWorkspace(), true));
+ } finally {
+ allApps.unfreeze();
+ }
+ }
+
+ private void verifyIconTheme(String title, ViewGroup parent, boolean isThemed) {
+ // Wait for Launcher model to be completed
+ try {
+ Executors.MODEL_EXECUTOR.submit(() -> { }).get();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+
// Find the app icon
Queue<View> viewQueue = new ArrayDeque<>();
viewQueue.add(parent);
@@ -100,7 +149,7 @@
}
} else if (view instanceof BubbleTextView) {
BubbleTextView btv = (BubbleTextView) view;
- if (APP_NAME.equals(btv.getText())) {
+ if (title.equals(btv.getText())) {
icon = btv;
break;
}
diff --git a/tests/src/com/android/launcher3/ui/workspace/TwoPanelWorkspaceTest.java b/tests/src/com/android/launcher3/ui/workspace/TwoPanelWorkspaceTest.java
index f646b50..6a938da 100644
--- a/tests/src/com/android/launcher3/ui/workspace/TwoPanelWorkspaceTest.java
+++ b/tests/src/com/android/launcher3/ui/workspace/TwoPanelWorkspaceTest.java
@@ -31,6 +31,7 @@
import com.android.launcher3.ui.AbstractLauncherUiTest;
import com.android.launcher3.ui.TaplTestsLauncher3;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -51,6 +52,7 @@
@Before
public void setUp() throws Exception {
super.setUp();
+ mLauncher.useTest2WorkspaceLayoutOnReload();
TaplTestsLauncher3.initialize(this);
assumeTrue(mLauncher.isTwoPanels());
@@ -63,6 +65,11 @@
});
}
+ @After
+ public void tearDown() {
+ mLauncher.useDefaultWorkspaceLayoutOnReload();
+ }
+
@Test
@PortraitLandscape
public void testDragIconToRightPanel() {
@@ -339,4 +346,4 @@
+ itemTitleSet.stream().collect(Collectors.joining(",")),
itemTitleSet.isEmpty());
}
-}
+}
\ No newline at end of file
diff --git a/tests/src/com/android/launcher3/util/ActivityContextWrapper.java b/tests/src/com/android/launcher3/util/ActivityContextWrapper.java
index 2618a2e..191d284 100644
--- a/tests/src/com/android/launcher3/util/ActivityContextWrapper.java
+++ b/tests/src/com/android/launcher3/util/ActivityContextWrapper.java
@@ -20,15 +20,21 @@
import android.view.ContextThemeWrapper;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.BaseDragLayer;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* {@link ContextWrapper} with internal Launcher interface for testing
*/
public class ActivityContextWrapper extends ContextThemeWrapper implements ActivityContext {
+ private final List<OnDeviceProfileChangeListener> mDpChangeListeners = new ArrayList<>();
+
private final DeviceProfile mProfile;
private final MyDragLayer mMyDragLayer;
@@ -44,6 +50,11 @@
}
@Override
+ public List<OnDeviceProfileChangeListener> getOnDeviceProfileChangeListeners() {
+ return mDpChangeListeners;
+ }
+
+ @Override
public DeviceProfile getDeviceProfile() {
return mProfile;
}
diff --git a/tests/src/com/android/launcher3/util/KotlinMockitoHelpers.kt b/tests/src/com/android/launcher3/util/KotlinMockitoHelpers.kt
index 57db13a..4303bfb 100644
--- a/tests/src/com/android/launcher3/util/KotlinMockitoHelpers.kt
+++ b/tests/src/com/android/launcher3/util/KotlinMockitoHelpers.kt
@@ -22,43 +22,41 @@
* be null"). To fix this, we can use methods that modify the return type to be nullable. This
* causes Kotlin to skip the null checks.
*/
-
import org.mockito.ArgumentCaptor
import org.mockito.Mockito
/**
- * Returns Mockito.eq() as nullable type to avoid java.lang.IllegalStateException when
- * null is returned.
+ * Returns Mockito.eq() as nullable type to avoid java.lang.IllegalStateException when null is
+ * returned.
*
* Generic T is nullable because implicitly bounded by Any?.
*/
fun <T> eq(obj: T): T = Mockito.eq<T>(obj)
/**
- * Returns Mockito.same() as nullable type to avoid java.lang.IllegalStateException when
- * null is returned.
+ * Returns Mockito.same() as nullable type to avoid java.lang.IllegalStateException when null is
+ * returned.
*
* Generic T is nullable because implicitly bounded by Any?.
*/
fun <T> same(obj: T): T = Mockito.same<T>(obj)
/**
- * Returns Mockito.any() as nullable type to avoid java.lang.IllegalStateException when
- * null is returned.
+ * Returns Mockito.any() as nullable type to avoid java.lang.IllegalStateException when null is
+ * returned.
*
* Generic T is nullable because implicitly bounded by Any?.
*/
fun <T> any(type: Class<T>): T = Mockito.any<T>(type)
+
inline fun <reified T> any(): T = any(T::class.java)
-/**
- * Kotlin type-inferred version of Mockito.nullable()
- */
+/** Kotlin type-inferred version of Mockito.nullable() */
inline fun <reified T> nullable(): T? = Mockito.nullable(T::class.java)
/**
- * Returns ArgumentCaptor.capture() as nullable type to avoid java.lang.IllegalStateException
- * when null is returned.
+ * Returns ArgumentCaptor.capture() as nullable type to avoid java.lang.IllegalStateException when
+ * null is returned.
*
* Generic T is nullable because implicitly bounded by Any?.
*/
@@ -82,8 +80,9 @@
/**
* A kotlin implemented wrapper of [ArgumentCaptor] which prevents the following exception when
* kotlin tests are mocking kotlin objects and the methods take non-null parameters:
- *
+ * ```
* java.lang.NullPointerException: capture() must not be null
+ * ```
*/
class KotlinArgumentCaptor<T> constructor(clazz: Class<T>) {
private val wrapped: ArgumentCaptor<T> = ArgumentCaptor.forClass(clazz)
@@ -102,15 +101,15 @@
/**
* Helper function for creating and using a single-use ArgumentCaptor in kotlin.
- *
+ * ```
* val captor = argumentCaptor<Foo>()
* verify(...).someMethod(captor.capture())
* val captured = captor.value
- *
+ * ```
* becomes:
- *
+ * ```
* val captured = withArgCaptor<Foo> { verify(...).someMethod(capture()) }
- *
+ * ```
* NOTE: this uses the KotlinArgumentCaptor to avoid the NullPointerException.
*/
inline fun <reified T : Any> withArgCaptor(block: KotlinArgumentCaptor<T>.() -> Unit): T =
diff --git a/tests/src/com/android/launcher3/util/LauncherModelHelper.java b/tests/src/com/android/launcher3/util/LauncherModelHelper.java
index 3324959..caec301 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;
@@ -54,6 +55,7 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherModel.ModelUpdateTask;
+import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.LauncherProvider;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.model.AllAppsList;
@@ -194,8 +196,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);
}
@@ -360,6 +363,12 @@
sandboxContext.getContentResolver().insert(contentUri, values);
}
+ public void deleteItem(int itemId, @NonNull final String tableName) {
+ final Uri uri = Uri.parse("content://"
+ + LauncherProvider.AUTHORITY + "/" + tableName + "/" + itemId);
+ sandboxContext.getContentResolver().delete(uri, null, null);
+ }
+
public int[][][] createGrid(int[][][] typeArray) {
return createGrid(typeArray, 1);
}
@@ -498,7 +507,7 @@
SanboxModelContext() {
super(ApplicationProvider.getApplicationContext(),
- UserCache.INSTANCE, InstallSessionHelper.INSTANCE,
+ UserCache.INSTANCE, InstallSessionHelper.INSTANCE, LauncherPrefs.INSTANCE,
LauncherAppState.INSTANCE, InvariantDeviceProfile.INSTANCE,
DisplayController.INSTANCE, CustomWidgetManager.INSTANCE,
SettingsCache.INSTANCE, PluginManagerWrapper.INSTANCE,
diff --git a/tests/src/com/android/launcher3/util/LockedUserStateTest.kt b/tests/src/com/android/launcher3/util/LockedUserStateTest.kt
new file mode 100644
index 0000000..84156e7
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/LockedUserStateTest.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.util
+
+import android.content.Context
+import android.content.Intent
+import android.os.Process
+import android.os.UserManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyZeroInteractions
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
+
+/** Unit tests for {@link LockedUserUtil} */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class LockedUserStateTest {
+
+ @Mock lateinit var userManager: UserManager
+ @Mock lateinit var context: Context
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ `when`(context.getSystemService(UserManager::class.java)).thenReturn(userManager)
+ }
+
+ @Test
+ fun runOnUserUnlocked_runs_action_immediately_if_already_unlocked() {
+ `when`(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(true)
+ LockedUserState.INSTANCE.initializeForTesting(LockedUserState(context))
+ val action: Runnable = mock()
+
+ LockedUserState.get(context).runOnUserUnlocked(action)
+ verify(action).run()
+ }
+
+ @Test
+ fun runOnUserUnlocked_waits_to_run_action_until_user_is_unlocked() {
+ `when`(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(false)
+ LockedUserState.INSTANCE.initializeForTesting(LockedUserState(context))
+ val action: Runnable = mock()
+
+ LockedUserState.get(context).runOnUserUnlocked(action)
+ verifyZeroInteractions(action)
+
+ LockedUserState.get(context)
+ .mUserUnlockedReceiver
+ .onReceive(context, Intent(Intent.ACTION_USER_UNLOCKED))
+
+ verify(action).run()
+ }
+
+ @Test
+ fun isUserUnlocked_returns_true_when_user_is_unlocked() {
+ `when`(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(true)
+ LockedUserState.INSTANCE.initializeForTesting(LockedUserState(context))
+ assertThat(LockedUserState.get(context).isUserUnlocked).isTrue()
+ }
+
+ @Test
+ fun isUserUnlocked_returns_false_when_user_is_locked() {
+ `when`(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(false)
+ LockedUserState.INSTANCE.initializeForTesting(LockedUserState(context))
+ assertThat(LockedUserState.get(context).isUserUnlocked).isFalse()
+ }
+}
diff --git a/tests/src/com/android/launcher3/util/MultiAdditivePropertyTest.kt b/tests/src/com/android/launcher3/util/MultiPropertyFactoryTest.kt
similarity index 66%
rename from tests/src/com/android/launcher3/util/MultiAdditivePropertyTest.kt
rename to tests/src/com/android/launcher3/util/MultiPropertyFactoryTest.kt
index 309d055..a63136e 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) {
+ 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/TouchUtilTest.kt b/tests/src/com/android/launcher3/util/TouchUtilTest.kt
new file mode 100644
index 0000000..235d6ec
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/TouchUtilTest.kt
@@ -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.util
+
+import android.view.InputDevice
+import android.view.MotionEvent
+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 [TouchUtil] */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class TouchUtilTest {
+
+ @Test
+ fun isMouseRightClickDownOrMove_onMouseRightButton_returnsTrue() {
+ val ev = MotionEvent.obtain(200, 300, MotionEvent.ACTION_MOVE, 1.0f, 0.0f, 0)
+ ev.source = InputDevice.SOURCE_MOUSE
+ ev.buttonState = MotionEvent.BUTTON_SECONDARY
+
+ assertThat(TouchUtil.isMouseRightClickDownOrMove(ev)).isTrue()
+ }
+
+ @Test
+ fun isMouseRightClickDownOrMove_onMouseLeftButton_returnsFalse() {
+ val ev = MotionEvent.obtain(200, 300, MotionEvent.ACTION_MOVE, 1.0f, 0.0f, 0)
+ ev.source = InputDevice.SOURCE_MOUSE
+ ev.buttonState = MotionEvent.BUTTON_PRIMARY
+
+ assertThat(TouchUtil.isMouseRightClickDownOrMove(ev)).isFalse()
+ }
+
+ @Test
+ fun isMouseRightClickDownOrMove_onMouseTertiaryButton_returnsFalse() {
+ val ev = MotionEvent.obtain(200, 300, MotionEvent.ACTION_MOVE, 1.0f, 0.0f, 0)
+ ev.source = InputDevice.SOURCE_MOUSE
+ ev.buttonState = MotionEvent.BUTTON_TERTIARY
+
+ assertThat(TouchUtil.isMouseRightClickDownOrMove(ev)).isFalse()
+ }
+
+ @Test
+ fun isMouseRightClickDownOrMove_onDpadRightButton_returnsFalse() {
+ val ev = MotionEvent.obtain(200, 300, MotionEvent.ACTION_MOVE, 1.0f, 0.0f, 0)
+ ev.source = InputDevice.SOURCE_DPAD
+ ev.buttonState = MotionEvent.BUTTON_SECONDARY
+
+ assertThat(TouchUtil.isMouseRightClickDownOrMove(ev)).isFalse()
+ }
+}
diff --git a/tests/src/com/android/launcher3/util/WidgetUtils.java b/tests/src/com/android/launcher3/util/WidgetUtils.java
index 6fc8491..b0df055 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 = LauncherWidgetHolder.newInstance(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/AddToHomeScreenPrompt.java b/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java
index 98eb32e..10afe13 100644
--- a/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java
+++ b/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java
@@ -45,7 +45,8 @@
mLauncher.clickObject(
mLauncher.waitForObjectInContainer(
mWidgetCell.getParent().getParent().getParent().getParent(),
- By.text(ADD_AUTOMATICALLY)));
+ By.text(ADD_AUTOMATICALLY)),
+ LauncherInstrumentation.GestureScope.OUTSIDE_WITHOUT_PILFER);
mLauncher.waitUntilLauncherObjectGone(getSelector());
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java
index 4791846..6f6428a 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,7 +240,11 @@
.getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
}
- private UiObject2 getSearchBox(UiObject2 allAppsContainer) {
+ private UiObject2 getAppListRecycler(UiObject2 allAppsContainer) {
+ return mLauncher.waitForObjectInContainer(allAppsContainer, "apps_list_view");
+ }
+
+ protected 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/AllAppsQsb.java b/tests/tapl/com/android/launcher3/tapl/AllAppsQsb.java
new file mode 100644
index 0000000..0931cd4
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/AllAppsQsb.java
@@ -0,0 +1,37 @@
+/*
+ * 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.tapl;
+
+import androidx.test.uiautomator.UiObject2;
+
+/**
+ * Operations on AllApp screen qsb.
+ */
+class AllAppsQsb extends Qsb {
+
+ private final UiObject2 mAllAppsContainer;
+
+ AllAppsQsb(LauncherInstrumentation launcher, UiObject2 allAppsContainer) {
+ super(launcher);
+ mAllAppsContainer = allAppsContainer;
+ waitForQsbObject();
+ }
+
+ @Override
+ protected UiObject2 waitForQsbObject() {
+ return mLauncher.waitForObjectInContainer(mAllAppsContainer, "search_container_all_apps");
+ }
+}
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 6a11336..afeb8d7 100644
--- a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
+++ b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
@@ -244,42 +244,44 @@
* 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()) {
- 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() {
- UiObject2 focusedTask = getFocusedTaskForTablet();
- if (focusedTask == null) {
- return false;
- }
- return Math.abs(
- focusedTask.getVisibleBounds().exactCenterX() - mLauncher.getExactScreenCenterX())
- < 1;
- }
-
- /**
* Returns Overview focused task if it exists.
*
* @throws IllegalStateException if not run on a tablet device.
*/
- UiObject2 getFocusedTaskForTablet() {
+ OverviewTask getFocusedTaskForTablet() {
if (!mLauncher.isTablet()) {
throw new IllegalStateException("Must be run on tablet device.");
}
@@ -290,9 +292,9 @@
int focusedTaskHeight = mLauncher.getFocusedTaskHeightForTablet();
for (UiObject2 task : taskViews) {
if (task.getVisibleBounds().height() == focusedTaskHeight) {
- return task;
+ return new OverviewTask(mLauncher, task, this);
}
}
return null;
}
-}
\ No newline at end of file
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java b/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java
index 7123de4..8ac1aef 100644
--- a/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java
@@ -15,9 +15,17 @@
*/
package com.android.launcher3.tapl;
+import static com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL;
+
+import android.graphics.Rect;
+
import androidx.annotation.NonNull;
import androidx.test.uiautomator.UiObject2;
+import com.android.launcher3.testing.shared.TestProtocol;
+
+import java.util.Objects;
+
public class HomeAllApps extends AllApps {
private static final String BOTTOM_SHEET_RES_ID = "bottom_sheet_background";
@@ -25,6 +33,42 @@
super(launcher);
}
+ /**
+ * Swipes down to Workspace.
+ *
+ * @return the Workspace object.
+ */
+ @NonNull
+ public Workspace switchToWorkspace() {
+ try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
+ LauncherInstrumentation.Closable c =
+ mLauncher.addContextLayer("want to switch from all apps to workspace")) {
+ UiObject2 allAppsContainer = verifyActiveContainer();
+
+ final Rect searchBoxBounds = Objects.requireNonNull(
+ mLauncher.getVisibleBounds(getSearchBox(allAppsContainer)));
+ final int startX = searchBoxBounds.centerX();
+ final int startY = searchBoxBounds.bottom;
+ final int endY = mLauncher.getDevice().getDisplayHeight();
+ LauncherInstrumentation.log(
+ "switchToWorkspace: startY = " + startY + ", endY = " + endY
+ + ", slop = " + mLauncher.getTouchSlop());
+
+ mLauncher.swipeToState(
+ startX,
+ startY,
+ startX,
+ endY,
+ 12 /* steps */,
+ NORMAL_STATE_ORDINAL, LauncherInstrumentation.GestureScope.INSIDE);
+
+ try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
+ "swiped to workspace")) {
+ return mLauncher.getWorkspace();
+ }
+ }
+ }
+
@Override
protected LauncherInstrumentation.ContainerType getContainerType() {
return LauncherInstrumentation.ContainerType.HOME_ALL_APPS;
@@ -47,6 +91,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.
@@ -65,4 +115,13 @@
}
}
}
+
+ /**
+ * Return the QSB UI object on the AllApps screen.
+ * @return the QSB UI object.
+ */
+ @NonNull
+ public Qsb getQsb() {
+ return new AllAppsQsb(mLauncher, verifyActiveContainer());
+ }
}
diff --git a/tests/tapl/com/android/launcher3/tapl/HomeQsb.java b/tests/tapl/com/android/launcher3/tapl/HomeQsb.java
index 5f92199..20d09a1 100644
--- a/tests/tapl/com/android/launcher3/tapl/HomeQsb.java
+++ b/tests/tapl/com/android/launcher3/tapl/HomeQsb.java
@@ -15,31 +15,23 @@
*/
package com.android.launcher3.tapl;
+import androidx.test.uiautomator.UiObject2;
+
/**
- * Operations on home screen qsb.
+ * Operations on Home screen qsb.
*/
-public class HomeQsb {
+class HomeQsb extends Qsb {
- private final LauncherInstrumentation mLauncher;
+ private final UiObject2 mHotSeat;
- HomeQsb(LauncherInstrumentation launcher) {
- mLauncher = launcher;
- mLauncher.waitForLauncherObject("search_container_hotseat");
+ HomeQsb(LauncherInstrumentation launcher, UiObject2 hotseat) {
+ super(launcher);
+ mHotSeat = hotseat;
+ waitForQsbObject();
}
- /**
- * Show search result page from tapping qsb.
- */
- public SearchResultFromQsb showSearchResult() {
- try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
- "want to open search result page");
- LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
- mLauncher.clickLauncherObject(
- mLauncher.waitForLauncherObject("search_container_hotseat"));
- try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer(
- "clicked qsb to open search result page")) {
- return new SearchResultFromQsb(mLauncher);
- }
- }
+ @Override
+ protected UiObject2 waitForQsbObject() {
+ return mLauncher.waitForObjectInContainer(mHotSeat, "search_container_hotseat");
}
}
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 fca2ab7..c203154 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,8 +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";
@@ -177,12 +190,14 @@
private final UiDevice mDevice;
private final Instrumentation mInstrumentation;
- private int mExpectedRotation = Surface.ROTATION_0;
+ private Integer mExpectedRotation = null;
private boolean mExpectedRotationCheckEnabled = true;
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 +252,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 +278,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 +299,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 +394,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 +562,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 +697,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;
}
@@ -705,7 +755,7 @@
private UiObject2 verifyContainerType(ContainerType containerType) {
waitForLauncherInitialized();
- if (mExpectedRotationCheckEnabled) {
+ if (mExpectedRotationCheckEnabled && mExpectedRotation != null) {
assertEquals("Unexpected display rotation",
mExpectedRotation, mDevice.getDisplayRotation());
}
@@ -728,56 +778,97 @@
switch (containerType) {
case WORKSPACE: {
waitUntilLauncherObjectGone(APPS_RES_ID);
- waitUntilLauncherObjectGone(OVERVIEW_RES_ID);
waitUntilLauncherObjectGone(WIDGETS_RES_ID);
- waitUntilLauncherObjectGone(TASKBAR_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);
+ 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 TASKBAR_ALL_APPS: {
+ waitUntilLauncherObjectGone(WORKSPACE_RES_ID);
+ waitUntilLauncherObjectGone(WIDGETS_RES_ID);
+ waitUntilSystemLauncherObjectGone(OVERVIEW_RES_ID);
+ waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
+ waitUntilSystemLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID);
+
+ return waitForLauncherObject(APPS_RES_ID);
+ }
case HOME_ALL_APPS: {
waitUntilLauncherObjectGone(WORKSPACE_RES_ID);
- waitUntilLauncherObjectGone(OVERVIEW_RES_ID);
waitUntilLauncherObjectGone(WIDGETS_RES_ID);
- waitUntilLauncherObjectGone(TASKBAR_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 SPLIT_SCREEN_SELECT: {
- waitUntilLauncherObjectGone(APPS_RES_ID);
- waitUntilLauncherObjectGone(WORKSPACE_RES_ID);
- waitUntilLauncherObjectGone(WIDGETS_RES_ID);
- waitUntilLauncherObjectGone(TASKBAR_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);
+ if (isTablet()) {
+ waitForSystemLauncherObject(TASKBAR_RES_ID);
+ } else {
+ waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
+ }
+ waitUntilSystemLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID);
- return waitForFallbackLauncherObject(OVERVIEW_RES_ID);
+ return waitForSystemLauncherObject(OVERVIEW_RES_ID);
+ }
+ case SPLIT_SCREEN_SELECT: {
+ waitUntilLauncherObjectGone(APPS_RES_ID);
+ waitUntilLauncherObjectGone(WORKSPACE_RES_ID);
+ waitUntilLauncherObjectGone(WIDGETS_RES_ID);
+ if (isTablet()) {
+ waitForSystemLauncherObject(TASKBAR_RES_ID);
+ } else {
+ waitUntilSystemLauncherObjectGone(TASKBAR_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);
+ 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;
}
@@ -788,6 +879,10 @@
}
}
+ public void waitForModelQueueCleared() {
+ getTestInfo(TestProtocol.REQUEST_MODEL_QUEUE_CLEARED);
+ }
+
public void waitForLauncherInitialized() {
for (int i = 0; i < 100; ++i) {
if (getTestInfo(
@@ -883,7 +978,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.
*/
@@ -901,14 +1003,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);
@@ -941,7 +1038,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(
@@ -976,8 +1073,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);
+ }
}
}
}
@@ -993,7 +1094,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));
}
/**
@@ -1076,6 +1178,14 @@
waitUntilGoneBySelector(getOverviewObjectSelector(resId));
}
+ void waitUntilSystemLauncherObjectGone(String resId) {
+ if (is3PLauncher()) {
+ waitUntilOverviewObjectGone(resId);
+ } else {
+ waitUntilLauncherObjectGone(resId);
+ }
+ }
+
void waitUntilLauncherObjectGone(BySelector selector) {
waitUntilGoneBySelector(makeLauncherSelector(selector));
}
@@ -1116,6 +1226,16 @@
}
@Nullable
+ UiObject2 findObjectInContainer(UiObject2 container, String resName) {
+ try {
+ return container.findObject(getLauncherObjectSelector(resName));
+ } catch (StaleObjectException e) {
+ fail("The container disappeared from screen");
+ return null;
+ }
+ }
+
+ @Nullable
UiObject2 findObjectInContainer(UiObject2 container, BySelector selector) {
try {
return container.findObject(selector);
@@ -1196,6 +1316,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));
}
@@ -1215,6 +1340,12 @@
}
@NonNull
+ UiObject2 waitForSystemLauncherObject(String resName) {
+ return is3PLauncher() ? waitForOverviewObject(resName)
+ : waitForLauncherObject(resName);
+ }
+
+ @NonNull
UiObject2 waitForLauncherObject(BySelector selector) {
return waitForObjectBySelector(makeLauncherSelector(selector));
}
@@ -1225,11 +1356,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);
@@ -1266,7 +1392,7 @@
return mDevice.getLauncherPackageName();
}
- boolean isFallbackOverview() {
+ boolean is3PLauncher() {
return !getOverviewPackageName().equals(getLauncherPackageName());
}
@@ -1345,19 +1471,22 @@
return getRealDisplaySize().x - getWindowInsets().right - 1;
}
- void clickObject(UiObject2 object) {
- waitForObjectEnabled(object, "clickObject");
- if (!isLauncher3() && getNavigationModel() != NavigationModel.THREE_BUTTON) {
- expectEvent(TestProtocol.SEQUENCE_TIS, LauncherInstrumentation.EVENT_TOUCH_DOWN_TIS);
- expectEvent(TestProtocol.SEQUENCE_TIS, LauncherInstrumentation.EVENT_TOUCH_UP_TIS);
- }
- object.click();
+ /**
+ * Click on the ui object right away without waiting for animation.
+ *
+ * [UiObject2.click] would wait for all animations finished before clicking. Not waiting for
+ * animations because in some scenarios there is a playing animations when the click is
+ * attempted.
+ */
+ void clickObject(UiObject2 uiObject, GestureScope gestureScope) {
+ final long clickTime = SystemClock.uptimeMillis();
+ final Point center = uiObject.getVisibleCenter();
+ sendPointer(clickTime, clickTime, MotionEvent.ACTION_DOWN, center, gestureScope);
+ sendPointer(clickTime, clickTime, MotionEvent.ACTION_UP, center, gestureScope);
}
void clickLauncherObject(UiObject2 object) {
- expectEvent(TestProtocol.SEQUENCE_MAIN, LauncherInstrumentation.EVENT_TOUCH_DOWN);
- expectEvent(TestProtocol.SEQUENCE_MAIN, LauncherInstrumentation.EVENT_TOUCH_UP);
- clickObject(object);
+ clickObject(object, GestureScope.INSIDE);
}
void scrollToLastVisibleRow(
@@ -1727,6 +1856,14 @@
getTestInfo(TestProtocol.REQUEST_USE_TEST_WORKSPACE_LAYOUT);
}
+ /**
+ * Reloads the workspace with a test layout that includes Maps/Play on workspace, and
+ * Dialer/Messaging/Chrome/Camera on hotseat.
+ */
+ public void useTest2WorkspaceLayoutOnReload() {
+ getTestInfo(TestProtocol.REQUEST_USE_TEST2_WORKSPACE_LAYOUT);
+ }
+
/** Reloads the workspace with the default layout defined by the user's grid size selection. */
public void useDefaultWorkspaceLayoutOnReload() {
getTestInfo(TestProtocol.REQUEST_USE_DEFAULT_WORKSPACE_LAYOUT);
@@ -1737,6 +1874,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);
@@ -1861,8 +2021,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(
@@ -1880,4 +2041,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/OverviewTask.java b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
index 72a39df..adc993d 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
@@ -20,6 +20,7 @@
import android.graphics.Rect;
+import androidx.annotation.NonNull;
import androidx.test.uiautomator.By;
import androidx.test.uiautomator.BySelector;
import androidx.test.uiautomator.UiObject2;
@@ -67,6 +68,10 @@
return mTask.getVisibleCenter().x;
}
+ float getExactCenterX() {
+ return mTask.getVisibleBounds().exactCenterX();
+ }
+
/**
* Dismisses the task by swiping up.
*/
@@ -159,4 +164,24 @@
}
}
}
+
+ /** Taps the task menu. */
+ @NonNull
+ public OverviewTaskMenu tapMenu() {
+ try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
+ LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "want to tap the task menu")) {
+ mLauncher.clickLauncherObject(
+ mLauncher.waitForObjectInContainer(mTask.getParent(), "icon"));
+
+ try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
+ "tapped the task menu")) {
+ return new OverviewTaskMenu(mLauncher);
+ }
+ }
+ }
+
+ boolean isTaskSplit() {
+ return mLauncher.findObjectInContainer(mTask.getParent(), "bottomright_snapshot") != null;
+ }
}
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenu.java b/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenu.java
new file mode 100644
index 0000000..8cdc8a0
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenu.java
@@ -0,0 +1,52 @@
+/*
+ * 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.tapl;
+
+import androidx.annotation.NonNull;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.UiObject2;
+
+/** Represents the menu of an overview task. */
+public class OverviewTaskMenu {
+
+ private final LauncherInstrumentation mLauncher;
+ private final UiObject2 mMenu;
+
+ OverviewTaskMenu(LauncherInstrumentation launcher) {
+ mLauncher = launcher;
+
+ mMenu = mLauncher.waitForLauncherObject("menu_option_layout");
+ mLauncher.assertTrue("The overview task menus is not visible",
+ !mMenu.getVisibleBounds().isEmpty());
+ }
+
+ /** Taps the split menu item from the overview task menu. */
+ @NonNull
+ public SplitScreenSelect tapSplitMenuItem() {
+ try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
+ LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "tap split menu item")) {
+ mLauncher.clickLauncherObject(
+ mLauncher.findObjectInContainer(mMenu, By.textStartsWith("Split")));
+
+ try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
+ "tapped split menu item")) {
+ return new SplitScreenSelect(mLauncher);
+ }
+ }
+ }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/Qsb.java b/tests/tapl/com/android/launcher3/tapl/Qsb.java
new file mode 100644
index 0000000..6bc4f21
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/Qsb.java
@@ -0,0 +1,86 @@
+/*
+ * 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.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 qsb from either Home screen or AllApp screen.
+ */
+public abstract class Qsb {
+
+ private static final String ASSISTANT_APP_PACKAGE = "com.google.android.googlequicksearchbox";
+ private static final String ASSISTANT_ICON_RES_ID = "mic_icon";
+ protected final LauncherInstrumentation mLauncher;
+
+ protected Qsb(LauncherInstrumentation launcher) {
+ mLauncher = launcher;
+ }
+
+ // Waits for the quick search box.
+ protected abstract UiObject2 waitForQsbObject();
+ /**
+ * 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("Qsb.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() {
+ try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "want to open search result page");
+ LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
+ mLauncher.clickLauncherObject(waitForQsbObject());
+ // wait for the result rendering to complete
+ mLauncher.waitForIdle();
+ try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer(
+ "clicked qsb to open search result page")) {
+ return new SearchResultFromQsb(mLauncher);
+ }
+ }
+ }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java b/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java
index ddeeac2..80176e9 100644
--- a/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java
+++ b/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java
@@ -20,13 +20,18 @@
import androidx.test.uiautomator.By;
import androidx.test.uiautomator.UiObject2;
+import java.util.ArrayList;
+
/**
- * Operations on search result page opened from home screen qsb.
+ * Operations on search result page opened from qsb.
*/
public class SearchResultFromQsb {
// The input resource id in the search box.
private static final String INPUT_RES = "input";
private static final String BOTTOM_SHEET_RES_ID = "bottom_sheet_background";
+
+ // This particular ID change should happen with caution
+ private static final String SEARCH_CONTAINER_RES_ID = "search_results_list_view";
private final LauncherInstrumentation mLauncher;
SearchResultFromQsb(LauncherInstrumentation launcher) {
@@ -49,6 +54,33 @@
return new AllAppsAppIcon(mLauncher, icon);
}
+ /** Find the web suggestion from search suggestion's title text */
+ public void verifyWebSuggestIsPresent(String text) {
+ ArrayList<UiObject2> goldenGateResults =
+ new ArrayList<>(mLauncher.waitForObjectsInContainer(
+ mLauncher.waitForSystemLauncherObject(SEARCH_CONTAINER_RES_ID),
+ By.clazz(TextView.class)));
+ boolean found = false;
+ for(UiObject2 uiObject: goldenGateResults) {
+ String currentString = uiObject.getText();
+ if (currentString.equals(text)) {
+ found = true;
+ }
+ }
+ if (!found) {
+ throw new IllegalStateException("Web suggestion title: " + text + " not found");
+ }
+ }
+
+ /** Find the total amount of views being displayed and return the size */
+ public int getSearchResultItemSize() {
+ ArrayList<UiObject2> searchResultItems =
+ new ArrayList<>(mLauncher.waitForObjectsInContainer(
+ mLauncher.waitForSystemLauncherObject(SEARCH_CONTAINER_RES_ID),
+ By.clazz(TextView.class)));
+ return searchResultItems.size();
+ }
+
/**
* 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/SplitScreenSelect.java b/tests/tapl/com/android/launcher3/tapl/SplitScreenSelect.java
index 3cf3ed6..9fa74e4 100644
--- a/tests/tapl/com/android/launcher3/tapl/SplitScreenSelect.java
+++ b/tests/tapl/com/android/launcher3/tapl/SplitScreenSelect.java
@@ -32,4 +32,10 @@
protected ContainerType getContainerType() {
return ContainerType.SPLIT_SCREEN_SELECT;
}
+
+ @Override
+ protected boolean isActionsViewVisible() {
+ // We don't show overview actions in split select state.
+ return false;
+ }
}
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/Widget.java b/tests/tapl/com/android/launcher3/tapl/Widget.java
index 29eb46c..d440903 100644
--- a/tests/tapl/com/android/launcher3/tapl/Widget.java
+++ b/tests/tapl/com/android/launcher3/tapl/Widget.java
@@ -32,7 +32,7 @@
*/
public final class Widget extends Launchable implements WorkspaceDragSource {
- private static final Pattern LONG_CLICK_EVENT = Pattern.compile("Widgets.onLongClick");
+ static final Pattern LONG_CLICK_EVENT = Pattern.compile("Widgets.onLongClick");
Widget(LauncherInstrumentation launcher, UiObject2 icon) {
super(launcher, icon);
@@ -71,7 +71,7 @@
public WidgetResizeFrame dragWidgetToWorkspace() {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
return dragWidgetToWorkspace(/* configurable= */ false, /* acceptsConfig= */ false, -1,
- -1);
+ -1, 1, 1);
}
}
@@ -80,12 +80,12 @@
* cellY and returns the resize frame that is shown after the widget is added.
*/
@NonNull
- public WidgetResizeFrame dragWidgetToWorkspace(int cellX, int cellY) {
+ public WidgetResizeFrame dragWidgetToWorkspace(int cellX, int cellY, int spanX, int spanY) {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"Dragging widget to workspace cell " + cellX + "," + cellY)) {
return dragWidgetToWorkspace(/* configurable= */ false, /* acceptsConfig= */ false,
- cellX, cellY);
+ cellX, cellY, spanX, spanY);
}
}
@@ -98,7 +98,7 @@
public WidgetResizeFrame dragConfigWidgetToWorkspace(boolean acceptsConfig) {
// TODO(b/239438337, fransebas) add correct event checking for this case
//try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
- return dragWidgetToWorkspace(/* configurable= */ true, acceptsConfig, -1, -1);
+ return dragWidgetToWorkspace(/* configurable= */ true, acceptsConfig, -1, -1, 1, 1);
//}
}
@@ -110,18 +110,17 @@
* @param cellX X position in the CellLayout
* @param cellY Y position in the CellLayout
*/
- private void dragToWorkspace(boolean startsActivity, boolean isWidgetShortcut, int cellX,
- int cellY) {
+ private void dragToWorkspaceCellPosition(boolean startsActivity, boolean isWidgetShortcut,
+ int cellX, int cellY, int spanX, int spanY) {
Launchable launchable = getLaunchable();
LauncherInstrumentation launcher = launchable.mLauncher;
- Workspace.dragIconToWorkspace(
+ Workspace.dragIconToWorkspaceCellPosition(
launcher,
launchable,
- () -> Workspace.getCellCenter(launchable.mLauncher, cellX, cellY),
+ cellX, cellY, spanX, spanY,
startsActivity,
isWidgetShortcut,
launchable::addExpectedEventsForLongClick);
-
}
/**
@@ -144,13 +143,13 @@
*/
@Nullable
private WidgetResizeFrame dragWidgetToWorkspace(boolean configurable, boolean acceptsConfig,
- int cellX, int cellY) {
+ int cellX, int cellY, int spanX, int spanY) {
if (cellX == -1 || cellY == -1) {
internalDragToWorkspace(/* startsActivity= */ configurable, /* isWidgetShortcut= */
false);
} else {
- dragToWorkspace(/* startsActivity= */ configurable, /* isWidgetShortcut= */ false,
- cellX, cellY);
+ dragToWorkspaceCellPosition(/* startsActivity= */ configurable, /* isWidgetShortcut= */
+ false, cellX, cellY, spanX, spanY);
}
if (configurable) {
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index efbdb23..62665de 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;
@@ -117,10 +118,11 @@
*
* The qsb must already be visible when calling this method.
*/
- public HomeQsb getQsb() {
+ @NonNull
+ public Qsb getQsb() {
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"want to get the home qsb")) {
- return new HomeQsb(mLauncher);
+ return new HomeQsb(mLauncher, mHotseat);
}
}
@@ -192,6 +194,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 +304,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.
@@ -319,6 +352,7 @@
}
}
+
/**
* Uninstall the appIcon by dragging it to the 'uninstall' drop point of the drop_target_bar.
*
@@ -352,7 +386,7 @@
Until.hasObject(installerAlert), LauncherInstrumentation.WAIT_TIME_MS));
final UiObject2 ok = device.findObject(By.text("OK"));
assertNotNull("OK button is not shown", ok);
- launcher.clickObject(ok);
+ launcher.clickObject(ok, LauncherInstrumentation.GestureScope.OUTSIDE_WITHOUT_PILFER);
assertTrue("Uninstall alert is not dismissed after clicking OK", device.wait(
Until.gone(installerAlert), LauncherInstrumentation.WAIT_TIME_MS));
@@ -375,9 +409,15 @@
}
static Point getCellCenter(LauncherInstrumentation launcher, int cellX, int cellY) {
- return launcher.getTestInfo(WorkspaceCellCenterRequest.builder().setCellX(
- cellX).setCellY(cellY).build()).getParcelable(
- TestProtocol.TEST_INFO_RESPONSE_FIELD);
+ return launcher.getTestInfo(WorkspaceCellCenterRequest.builder().setCellX(cellX).setCellY(
+ cellY).build()).getParcelable(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+ }
+
+ static Point getCellCenter(LauncherInstrumentation launcher, int cellX, int cellY, int spanX,
+ int spanY) {
+ return launcher.getTestInfo(WorkspaceCellCenterRequest.builder().setCellX(cellX)
+ .setCellY(cellY).setSpanX(spanX).setSpanY(spanY).build())
+ .getParcelable(TestProtocol.TEST_INFO_RESPONSE_FIELD);
}
static Point getHotseatCellCenter(LauncherInstrumentation launcher, int cellInd) {
@@ -385,6 +425,12 @@
.setCellInd(cellInd).build()).getParcelable(TestProtocol.TEST_INFO_RESPONSE_FIELD);
}
+ /** Returns the number of rows and columns in the workspace */
+ public Point getRowsAndCols() {
+ return mLauncher.getTestInfo(TestProtocol.REQUEST_WORKSPACE_COLUMNS_ROWS).getParcelable(
+ TestProtocol.TEST_INFO_RESPONSE_FIELD);
+ }
+
/**
* Finds folder icons in the current workspace.
*
@@ -423,6 +469,19 @@
launcher, launchable, dest, expectLongClickEvents, expectDropEvents);
}
+ static void dragIconToWorkspaceCellPosition(LauncherInstrumentation launcher,
+ Launchable launchable, int cellX, int cellY, int spanX, int spanY,
+ boolean startsActivity, boolean isWidgetShortcut, Runnable expectLongClickEvents) {
+ Runnable expectDropEvents = null;
+ if (startsActivity || isWidgetShortcut) {
+ expectDropEvents = () -> launcher.expectEvent(TestProtocol.SEQUENCE_MAIN,
+ LauncherInstrumentation.EVENT_START);
+ }
+ dragIconToWorkspaceCellPosition(
+ launcher, launchable, cellX, cellY, spanX, spanY, true, expectLongClickEvents,
+ expectDropEvents);
+ }
+
/**
* Drag icon in workspace to else where and drop it immediately.
* (There is no slow down time before drop event)
@@ -469,7 +528,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 +546,52 @@
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);
+ }
+ }
+
+ static void dragIconToWorkspaceCellPosition(
+ LauncherInstrumentation launcher,
+ Launchable launchable,
+ int cellX, int cellY, int spanX, int spanY,
+ boolean isDecelerating,
+ Runnable expectLongClickEvents,
+ @Nullable Runnable expectDropEvents) {
+ try (LauncherInstrumentation.Closable ignored = launcher.addContextLayer(
+ "want to drag icon to workspace")) {
+ Point rowsAndCols = launcher.getWorkspace().getRowsAndCols();
+ int destinationWorkspace = cellX / rowsAndCols.x;
+ cellX = cellX % rowsAndCols.x;
+
+ final long downTime = SystemClock.uptimeMillis();
+ Point dragStart = launchable.startDrag(
+ downTime,
+ expectLongClickEvents,
+ /* runToSpringLoadedState= */ true);
+ Point targetDest = getCellCenter(launcher, cellX, cellY, spanX, spanY);
+ int displayX = launcher.getRealDisplaySize().x;
+
+ // Since the destination can be on another page, we need to drag to the edge first
+ // until we reach the target page
+ for (int i = 0; i < destinationWorkspace; i++) {
+ // Don't drag all the way to the edge to prevent touch events from getting out of
+ //screen bounds.
+ Point screenEdge = new Point(displayX - 1, targetDest.y);
+ Point finalDragStart = dragStart;
+ executeAndWaitForPageScroll(launcher,
+ () -> launcher.movePointer(finalDragStart, screenEdge, DEFAULT_DRAG_STEPS,
+ true, downTime, downTime, true,
+ LauncherInstrumentation.GestureScope.INSIDE));
+ dragStart = screenEdge;
+ }
+
+ // targetDest.x is now between 0 and displayX so we found the target page,
+ // we just have to put move the icon to the destination and drop it
+ 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);
}
}