diff options
author | alk3pInjection <webmaster@raspii.tech> | 2023-04-20 00:08:54 +0800 |
---|---|---|
committer | alk3pInjection <webmaster@raspii.tech> | 2023-04-22 01:50:12 +0800 |
commit | db8377dbce7c98d8c50315d42b52584b1bc25f5d (patch) | |
tree | 1a7751c6272bd514b0d00671e5365f983017bd5a | |
parent | 10948aa3c6484e4dac665d1fbfafb63e1f9cf9a5 (diff) | |
parent | 40dffe41d661b151b352c300455615084290e75a (diff) |
Merge tag 'LA.QSSI.13.0.r1-09800-qssi.0' into tachibana
"LA.QSSI.13.0.r1-09800-qssi.0"
Conflicts:
Android.bp
AndroidManifest-common.xml
quickstep/src/com/android/quickstep/views/OverviewActionsView.java
res/drawable/bg_all_apps_searchbox.xml
res/layout/search_container_all_apps.xml
res/layout/secondary_launcher.xml
res/values/styles.xml
res/xml/device_profiles.xml
src/com/android/launcher3/Workspace.java
Change-Id: If9fa9e2f0e388e5cc34243b2d087121952af04b4
815 files changed, 31924 insertions, 16666 deletions
diff --git a/Android.bp b/Android.bp index 0a55675d43..6b6bb8a1ee 100644 --- a/Android.bp +++ b/Android.bp @@ -19,6 +19,54 @@ package { 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: [ @@ -35,9 +83,7 @@ android_library { ], srcs: [ "tests/tapl/**/*.java", - "src/com/android/launcher3/ResourceUtils.java", - "src/com/android/launcher3/testing/TestProtocol.java", - "src/com/android/launcher3/testing/*Request.java", + "src/com/android/launcher3/testing/shared/**/*.java", ], resource_dirs: [ ], manifest: "tests/tapl/AndroidManifest.xml", @@ -107,6 +153,7 @@ android_library { "androidx.cardview_cardview", "com.google.android.material_material", "iconloader_base", + "view_capture" ], manifest: "AndroidManifest-common.xml", sdk_version: "current", @@ -141,14 +188,10 @@ android_app { "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", @@ -204,61 +247,14 @@ android_library { } -// 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", @@ -289,7 +285,9 @@ android_library { android_library { name: "Launcher3QuickStepLib", srcs: [ - ":launcher-src-no-build-config", + ":launcher-src", + ":launcher-quickstep-src", + ":launcher-src_shortcuts_overrides", ], resource_dirs: [], libs: [ @@ -313,3 +311,134 @@ android_library { baseline_filename: "lint-baseline-launcher3.xml", }, } + +// Build rule for Launcher3 Go app for Android Go devices. +android_app { + name: "Launcher3Go", + + static_libs: ["Launcher3CommonDepsLib"], + + srcs: [ + ":launcher-src", + ":launcher-go-src", + ":launcher-src_ui_overrides", + ], + + resource_dirs: ["go/res"], + + optimize: { + proguard_flags_files: ["proguard.flags"], + }, + + sdk_version: "current", + min_sdk_version: "current", + target_sdk_version: "current", + privileged: true, + system_ext_specific: true, + overrides: [ + "Home", + "Launcher2", + "Launcher3", + "Launcher3QuickStep", + ], + required: ["privapp_whitelist_com.android.launcher3"], + + additional_manifests: [ + "AndroidManifest.xml", + "AndroidManifest-common.xml", + ], + + manifest: "go/AndroidManifest.xml", + jacoco: { + include_filter: ["com.android.launcher3.*"], + } + +} + +// Build rule for Quickstep app. +android_app { + name: "Launcher3QuickStep", + + static_libs: ["Launcher3QuickStepLib"], + optimize: { + enabled: false, + }, + + certificate: "platform", + platform_apis: true, + min_sdk_version: "current", + target_sdk_version: "current", + + privileged: true, + system_ext_specific: true, + overrides: [ + "Home", + "Launcher2", + "Launcher3", + ], + required: ["privapp_whitelist_com.android.launcher3"], + + resource_dirs: ["quickstep/res"], + + additional_manifests: [ + "quickstep/AndroidManifest-launcher.xml", + "AndroidManifest-common.xml", + ], + + manifest: "quickstep/AndroidManifest.xml", + jacoco: { + include_filter: ["com.android.launcher3.*"], + } + +} + +// Build rule for Launcher3 Go app with quickstep for Android Go devices. +android_app { + name: "Launcher3QuickStepGo", + + static_libs: [ + "SystemUI-statsd", + "SystemUISharedLib", + "LauncherGoResLib", + ], + + platform_apis: true, + min_sdk_version: "current", + target_sdk_version: "current", + + srcs: [ ], + + resource_dirs: [ + "go/quickstep/res", + "go/res", + "quickstep/res", + ], + + optimize: { + proguard_flags_files: ["proguard.flags"], + enabled: true, + }, + + privileged: true, + system_ext_specific: true, + overrides: [ + "Home", + "Launcher2", + "Launcher3", + "Launcher3QuickStep", + ], + required: ["privapp_whitelist_com.android.launcher3"], + + additional_manifests: [ + "go/AndroidManifest.xml", + "go/AndroidManifest-launcher.xml", + "AndroidManifest-common.xml", + ], + + manifest: "quickstep/AndroidManifest.xml", + jacoco: { + include_filter: ["com.android.launcher3.*"], + } + +} + diff --git a/Android.mk b/Android.mk deleted file mode 100644 index 295a3a3596..0000000000 --- a/Android.mk +++ /dev/null @@ -1,148 +0,0 @@ -# -# Copyright (C) 2013 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -LOCAL_PATH := $(call my-dir) - -# -# Build rule for Launcher3 Go app for Android Go devices. -# -include $(CLEAR_VARS) -LOCAL_USE_AAPT2 := true -LOCAL_MODULE_TAGS := optional -LOCAL_STATIC_ANDROID_LIBRARIES := Launcher3CommonDepsLib - -LOCAL_SRC_FILES := \ - $(call all-java-files-under, src) \ - $(call all-java-files-under, src_ui_overrides) \ - $(call all-java-files-under, go/src) - -LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/go/res - -LOCAL_PROGUARD_FLAG_FILES := proguard.flags - -LOCAL_SDK_VERSION := current -LOCAL_MIN_SDK_VERSION := 26 -LOCAL_PACKAGE_NAME := Launcher3Go -LOCAL_PRIVILEGED_MODULE := true -LOCAL_SYSTEM_EXT_MODULE := true -LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3 Launcher3QuickStep -LOCAL_REQUIRED_MODULES := privapp_whitelist_com.android.launcher3 - -LOCAL_FULL_LIBS_MANIFEST_FILES := \ - $(LOCAL_PATH)/AndroidManifest.xml \ - $(LOCAL_PATH)/AndroidManifest-common.xml - -LOCAL_MANIFEST_FILE := go/AndroidManifest.xml -LOCAL_JACK_COVERAGE_INCLUDE_FILTER := com.android.launcher3.* -LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 -LOCAL_LICENSE_CONDITIONS := notice -LOCAL_LICENSE_PACKAGE_NAME := Android Launcher3 -LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE -include $(BUILD_PACKAGE) - -# -# Build rule for Quickstep app. -# -include $(CLEAR_VARS) -LOCAL_USE_AAPT2 := true -LOCAL_MODULE_TAGS := optional - -LOCAL_STATIC_ANDROID_LIBRARIES := Launcher3QuickStepLib -LOCAL_PROGUARD_ENABLED := disabled - -ifneq (,$(wildcard frameworks/base)) - LOCAL_PRIVATE_PLATFORM_APIS := true -else - LOCAL_SDK_VERSION := system_current - LOCAL_MIN_SDK_VERSION := 26 -endif -LOCAL_PACKAGE_NAME := Launcher3QuickStep -LOCAL_CERTIFICATE := platform -LOCAL_PRIVILEGED_MODULE := true -LOCAL_SYSTEM_EXT_MODULE := true -LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3 -LOCAL_REQUIRED_MODULES := privapp_whitelist_com.android.launcher3 - -LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/quickstep/res - -LOCAL_FULL_LIBS_MANIFEST_FILES := \ - $(LOCAL_PATH)/quickstep/AndroidManifest-launcher.xml \ - $(LOCAL_PATH)/AndroidManifest-common.xml - -LOCAL_MANIFEST_FILE := quickstep/AndroidManifest.xml -LOCAL_JACK_COVERAGE_INCLUDE_FILTER := com.android.launcher3.* - -LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 -LOCAL_LICENSE_CONDITIONS := notice -LOCAL_LICENSE_PACKAGE_NAME := Android Launcher3 -LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE -include $(BUILD_PACKAGE) - - -# -# Build rule for Launcher3 Go app with quickstep for Android Go devices. -# -include $(CLEAR_VARS) -LOCAL_USE_AAPT2 := true -LOCAL_MODULE_TAGS := optional - -LOCAL_STATIC_JAVA_LIBRARIES := \ - SystemUI-statsd \ - SystemUISharedLib -ifneq (,$(wildcard frameworks/base)) - LOCAL_PRIVATE_PLATFORM_APIS := true -else - LOCAL_SDK_VERSION := system_current - LOCAL_MIN_SDK_VERSION := 26 -endif -LOCAL_STATIC_ANDROID_LIBRARIES := LauncherGoResLib - -LOCAL_SRC_FILES := \ - $(call all-java-files-under, src) \ - $(call all-java-files-under, quickstep/src) \ - $(call all-java-files-under, go/src) \ - $(call all-java-files-under, go/quickstep/src) - -LOCAL_RESOURCE_DIR := \ - $(LOCAL_PATH)/go/quickstep/res \ - $(LOCAL_PATH)/go/res \ - $(LOCAL_PATH)/quickstep/res - -LOCAL_PROGUARD_FLAG_FILES := proguard.flags -LOCAL_PROGUARD_ENABLED := full - -LOCAL_PACKAGE_NAME := Launcher3QuickStepGo -LOCAL_PRIVILEGED_MODULE := true -LOCAL_SYSTEM_EXT_MODULE := true -LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3 Launcher3QuickStep Launcher3Go -LOCAL_REQUIRED_MODULES := privapp_whitelist_com.android.launcher3 - -LOCAL_FULL_LIBS_MANIFEST_FILES := \ - $(LOCAL_PATH)/go/AndroidManifest.xml \ - $(LOCAL_PATH)/go/AndroidManifest-launcher.xml \ - $(LOCAL_PATH)/AndroidManifest-common.xml - -LOCAL_MANIFEST_FILE := quickstep/AndroidManifest.xml -LOCAL_JACK_COVERAGE_INCLUDE_FILTER := com.android.launcher3.* -LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 -LOCAL_LICENSE_CONDITIONS := notice -LOCAL_LICENSE_PACKAGE_NAME := Android Launcher3 -LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE -include $(BUILD_PACKAGE) - - -# ================================================== -include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml index 441e6af511..d53caea50c 100644 --- a/AndroidManifest-common.xml +++ b/AndroidManifest-common.xml @@ -43,6 +43,7 @@ <!-- for rotating surface by arbitrary degree --> <uses-permission android:name="android.permission.ROTATE_SURFACE_FLINGER" /> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> + <uses-permission android:name="android.permission.READ_HOME_APP_SEARCH_DATA" /> <!-- for double tap to sleep --> <uses-permission android:name="android.permission.DEVICE_POWER" /> @@ -7,13 +7,6 @@ alexchau@google.com andraskloczl@google.com patmanning@google.com -petrcermak@google.com -pbdr@google.com -kideckel@google.com -stevenckng@google.com -ydixit@google.com -boadway@google.com -alinazaidi@google.com adamcohen@google.com hyunyoungs@google.com mrcasey@google.com @@ -24,10 +17,8 @@ winsonc@google.com zakcohen@google.com santie@google.com vadimt@google.com -mett@google.com jonmiranda@google.com pinyaoting@google.com -sfufa@google.com gwasserman@google.com jamesoleary@google.com joshtrask@google.com @@ -37,8 +28,6 @@ hwwang@google.com tracyzhou@google.com peanutbutter@google.com xuqiu@google.com -sreyasr@google.com -thiruram@google.com brianji@google.com per-file FeatureFlags.java, globs = set noparent diff --git a/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java b/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java index d16e12c9b8..a645e58ecc 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; @@ -33,6 +34,7 @@ import com.android.launcher3.BubbleTextView; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherSettings; import com.android.launcher3.ShortcutAndWidgetContainer; +import com.android.launcher3.testing.shared.TestProtocol; import java.util.ArrayList; import java.util.Collection; @@ -208,12 +210,19 @@ public class DebugTestInformationHandler extends TestInformationHandler { } 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; } @@ -247,17 +256,25 @@ public class DebugTestInformationHandler extends TestInformationHandler { 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 1bfaf66b99..ed3479783a 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 676ac43284..664bd94219 100644 --- a/go/quickstep/res/values-en-rCA/strings.xml +++ b/go/quickstep/res/values-en-rCA/strings.xml @@ -1,19 +1,19 @@ <?xml version="1.0" encoding="UTF-8"?> <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_share_drop_target_label" msgid="5804774105974539508">"Share app"</string> + <string name="app_share_drop_target_label" msgid="5804774105974539508">"Share App"</string> <string name="action_listen" msgid="2370304050784689486">"Listen"</string> <string name="action_translate" msgid="8028378961867277746">"Translate"</string> <string name="action_search" msgid="6269564710943755464">"Lens"</string> - <string name="dialog_acknowledge" msgid="2804025517675853172">"OK"</string> + <string name="dialog_acknowledge" msgid="2804025517675853172">"GOT IT"</string> <string name="dialog_cancel" msgid="6464336969134856366">"CANCEL"</string> <string name="dialog_settings" msgid="6564397136021186148">"SETTINGS"</string> <string name="niu_actions_confirmation_title" msgid="3863451714863526143">"Translate or listen to text on screen"</string> - <string name="niu_actions_confirmation_text" msgid="2105271481950866089">"Information such as text on your screen, web addresses and screenshots may be shared with Google.\n\nTo change what information you share, go to "<b>"Settings > Apps > Default apps > Digital assistant app"</b>"."</string> + <string name="niu_actions_confirmation_text" msgid="2105271481950866089">"Information such as text on your screen, web addresses, and screenshots may be shared with Google.\n\nTo change what information you share, go to "<b>"Settings > Apps > Default apps > Digital assistant app"</b>"."</string> <string name="assistant_not_selected_title" msgid="5017072974603345228">"Choose an assistant to use this feature"</string> - <string name="assistant_not_selected_text" msgid="3244613673884359276">"To listen to or translate text on your screen, choose a digital assistant app in settings"</string> + <string name="assistant_not_selected_text" msgid="3244613673884359276">"To listen to or translate text on your screen, choose a digital assistant app in Settings"</string> <string name="assistant_not_supported_title" msgid="1675788067597484142">"Change your assistant to use this feature"</string> - <string name="assistant_not_supported_text" msgid="1708031078549268884">"To listen to or translate text on your screen, change your digital assistant app in settings"</string> + <string name="assistant_not_supported_text" msgid="1708031078549268884">"To listen to or translate text on your screen, change your digital assistant app in Settings"</string> <string name="tooltip_listen" msgid="7634466447860989102">"Tap here to listen to text on this screen"</string> <string name="tooltip_translate" msgid="4184845868901542567">"Tap here to translate text on this screen"</string> <string name="toast_p2p_app_not_shareable" msgid="7229739094132131536">"This app can’t be shared"</string> diff --git a/go/quickstep/res/values-my/strings.xml b/go/quickstep/res/values-my/strings.xml index 0ca0e9c0bf..cbb485a6d2 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 2e76e2d972..6a3c5dc0c8 100644 --- a/go/quickstep/res/values-or/strings.xml +++ b/go/quickstep/res/values-or/strings.xml @@ -1,12 +1,12 @@ <?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> <string name="dialog_acknowledge" msgid="2804025517675853172">"ବୁଝିଗଲି"</string> - <string name="dialog_cancel" msgid="6464336969134856366">"ବାତିଲ୍ କରନ୍ତୁ"</string> + <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>"ସେଟିଂସ > ଆପ୍ସ > ଡିଫଲ୍ଟ ଆପ୍ସ > Digital assistant ଆପ"</b>"କୁ ଯାଆନ୍ତୁ।"</string> diff --git a/go/quickstep/res/values-ro/strings.xml b/go/quickstep/res/values-ro/strings.xml index 0be8cce7fe..3d6f0d8dd6 100644 --- a/go/quickstep/res/values-ro/strings.xml +++ b/go/quickstep/res/values-ro/strings.xml @@ -1,20 +1,20 @@ <?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">"Trimiteți aplicația"</string> - <string name="action_listen" msgid="2370304050784689486">"Ascultați"</string> - <string name="action_translate" msgid="8028378961867277746">"Traduceți"</string> + <string name="app_share_drop_target_label" msgid="5804774105974539508">"Trimite aplicația"</string> + <string name="action_listen" msgid="2370304050784689486">"Ascultă"</string> + <string name="action_translate" msgid="8028378961867277746">"Tradu"</string> <string name="action_search" msgid="6269564710943755464">"Lens"</string> <string name="dialog_acknowledge" msgid="2804025517675853172">"OK"</string> - <string name="dialog_cancel" msgid="6464336969134856366">"ANULAȚI"</string> + <string name="dialog_cancel" msgid="6464336969134856366">"ANULEAZĂ"</string> <string name="dialog_settings" msgid="6564397136021186148">"SETĂRI"</string> - <string name="niu_actions_confirmation_title" msgid="3863451714863526143">"Traduceți sau ascultați textul de pe ecran"</string> - <string name="niu_actions_confirmation_text" msgid="2105271481950866089">"Informații precum textul de pe ecran, adresele web și capturile de ecran pot fi trimise la Google.\n\nCa să schimbați informațiile trimise, accesați "<b>"Setări > Aplicații > Aplicații prestabilite > Aplicația asistent digital"</b>"."</string> - <string name="assistant_not_selected_title" msgid="5017072974603345228">"Alegeți un asistent pentru a folosi această funcție"</string> - <string name="assistant_not_selected_text" msgid="3244613673884359276">"Pentru a asculta sau a traduce text de pe ecran, alegeți o aplicație asistent digital în Setări"</string> - <string name="assistant_not_supported_title" msgid="1675788067597484142">"Schimbați asistentul pentru a folosi această funcție"</string> - <string name="assistant_not_supported_text" msgid="1708031078549268884">"Pentru a asculta sau a traduce text de pe ecran, schimbați aplicația asistent digital în Setări"</string> - <string name="tooltip_listen" msgid="7634466447860989102">"Atingeți aici pentru a asculta text de pe ecran"</string> - <string name="tooltip_translate" msgid="4184845868901542567">"Atingeți aici pentru a traduce text de pe ecran"</string> + <string name="niu_actions_confirmation_title" msgid="3863451714863526143">"Tradu sau ascultă textul de pe ecran"</string> + <string name="niu_actions_confirmation_text" msgid="2105271481950866089">"Informații precum textul de pe ecran, adresele web și capturile de ecran pot fi trimise la Google.\n\nCa să schimbi informațiile trimise, accesează "<b>"Setări > Aplicații > Aplicații prestabilite > Aplicația asistent digital"</b>"."</string> + <string name="assistant_not_selected_title" msgid="5017072974603345228">"Alege un asistent pentru a folosi această funcție"</string> + <string name="assistant_not_selected_text" msgid="3244613673884359276">"Pentru a asculta sau a traduce text de pe ecran, alege o aplicație asistent digital în Setări"</string> + <string name="assistant_not_supported_title" msgid="1675788067597484142">"Schimbă asistentul pentru a folosi această funcție"</string> + <string name="assistant_not_supported_text" msgid="1708031078549268884">"Pentru a asculta sau a traduce text de pe ecran, schimbă aplicația asistent digital în Setări"</string> + <string name="tooltip_listen" msgid="7634466447860989102">"Atinge aici pentru a asculta text de pe ecran"</string> + <string name="tooltip_translate" msgid="4184845868901542567">"Atinge aici pentru a traduce text de pe ecran"</string> <string name="toast_p2p_app_not_shareable" msgid="7229739094132131536">"Aplicația nu poate fi distribuită"</string> </resources> diff --git a/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java b/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java index c997e52dbd..253147d96b 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.IntDef; 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 @@ public final class TaskOverlayFactoryGo extends TaskOverlayFactory { AssistContentRequester assistContentRequester) { super(taskThumbnailView); mFactoryContentRequester = assistContentRequester; - mSharedPreferences = Utilities.getPrefs(mApplicationContext); + mSharedPreferences = LauncherPrefs.getPrefs(mApplicationContext); } /** @@ -151,7 +151,7 @@ public final class TaskOverlayFactoryGo extends TaskOverlayFactory { 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/lint-baseline-launcher3.xml b/lint-baseline-launcher3.xml index 107a34694b..a9dc0c604e 100644 --- a/lint-baseline-launcher3.xml +++ b/lint-baseline-launcher3.xml @@ -606,4 +606,20 @@ column="61"/> </issue> + <issue + id="NewApi" + message="Call requires API level 33 (current min is 26): `android.app.Activity#getOnBackInvokedDispatcher`"> + <location + file="packages/apps/Launcher3/src/com/android/launcher3/BaseActivity.java" + line="182"/> + </issue> + + <issue + id="NewApi" + message="Call requires API level 33 (current min is 26): `android.window.OnBackInvokedDispatcher#registerOnBackInvokedCallback`"> + <location + file="packages/apps/Launcher3/src/com/android/launcher3/BaseActivity.java" + line="182"/> + </issue> + </issues> diff --git a/proguard.flags b/proguard.flags index a45018346a..53a68ded91 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 10eedc8578..677c9921a0 100644 --- a/protos/launcher_atom.proto +++ b/protos/launcher_atom.proto @@ -45,6 +45,9 @@ message ItemInfo { // 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 @@ message TaskBarContainer { 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 @@ enum Attribute { 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/quickstep/Android.bp b/quickstep/Android.bp index 70b1438c38..f5a8253563 100644 --- a/quickstep/Android.bp +++ b/quickstep/Android.bp @@ -18,6 +18,11 @@ package { } filegroup { + name: "launcher3-quickstep-manifest", + srcs: ["AndroidManifest.xml"], +} + +filegroup { name: "launcher3-quickstep-robolectric-src", path: "robolectric_tests", srcs: ["robolectric_tests/src/**/*.java"], @@ -26,13 +31,14 @@ filegroup { filegroup { name: "launcher3-quickstep-tests-src", path: "tests", - srcs: ["tests/src/**/*.java"], + srcs: ["tests/src/**/*.java", "tests/src/**/*.kt"], } filegroup { 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 352cd3e7b6..3647e05f0a 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 e5f0295ff6..0b17a7b7d7 100644 --- a/quickstep/ext_tests/src/com/android/quickstep/DebugQuickstepTestInformationHandler.java +++ b/quickstep/ext_tests/src/com/android/quickstep/DebugQuickstepTestInformationHandler.java @@ -15,22 +15,13 @@ */ 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.TestProtocol; - -import java.util.concurrent.ExecutionException; +import com.android.launcher3.testing.shared.TestProtocol; /** * Class to handle requests from tests, including debug ones, to Quickstep Launcher builds. @@ -47,74 +38,14 @@ public abstract class DebugQuickstepTestInformationHandler extends QuickstepTest @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/res/drawable/ic_uninstall_shadow.xml b/quickstep/res/drawable/split_instructions_background.xml index b441b0e7b6..6d0e7dba1c 100644 --- a/res/drawable/ic_uninstall_shadow.xml +++ b/quickstep/res/drawable/split_instructions_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,9 @@ 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" /> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" + android:shape="rectangle"> + <solid android:color="?androidprv:attr/colorAccentPrimary" /> + <corners android:radius="@dimen/split_instructions_radius" /> +</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 Binary files differdeleted file mode 100644 index f9d2a63a0e..0000000000 --- a/quickstep/res/drawable/taskbar_edu_splitscreen.png +++ /dev/null diff --git a/quickstep/res/drawable/taskbar_edu_stashing.png b/quickstep/res/drawable/taskbar_edu_stashing.png Binary files differdeleted file mode 100644 index f9d2a63a0e..0000000000 --- a/quickstep/res/drawable/taskbar_edu_stashing.png +++ /dev/null diff --git a/quickstep/res/drawable/taskbar_edu_switch_apps.png b/quickstep/res/drawable/taskbar_edu_switch_apps.png Binary files differdeleted file mode 100644 index f9d2a63a0e..0000000000 --- a/quickstep/res/drawable/taskbar_edu_switch_apps.png +++ /dev/null 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 0000000000..44b3ecbd97 --- /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 0cae733dbe..f08cabe13e 100644 --- a/quickstep/res/layout/activity_allset.xml +++ b/quickstep/res/layout/activity_allset.xml @@ -26,7 +26,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/content_view" - android:fitsSystemWindows="true"> + android:fitsSystemWindows="false"> <com.airbnb.lottie.LottieAnimationView android:id="@+id/animated_background" @@ -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" /> - - <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" /> + <include layout="@layout/allset_navigation_and_hint"/> - <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 e7ef6e699a..0dd4df14e0 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 0000000000..4d5cf01eaa --- /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 c4642e443c..d5e367097a 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/split_instructions_view.xml b/quickstep/res/layout/split_instructions_view.xml new file mode 100644 index 0000000000..91fb05cd0a --- /dev/null +++ b/quickstep/res/layout/split_instructions_view.xml @@ -0,0 +1,35 @@ +<?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.SplitInstructionsView + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:background="@drawable/split_instructions_background" + android:paddingRight="@dimen/split_instructions_horizontal_padding" + android:paddingLeft="@dimen/split_instructions_horizontal_padding" + android:paddingTop="@dimen/split_instructions_vertical_padding" + android:paddingBottom="@dimen/split_instructions_vertical_padding" + android:elevation="@dimen/split_instructions_elevation" + android:visibility="gone"> + <androidx.appcompat.widget.AppCompatTextView + android:id="@+id/split_instructions_text" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:gravity="center" + android:textColor="?androidprv:attr/textColorOnAccent" + android:text="@string/toast_split_select_app" /> +</com.android.quickstep.views.SplitInstructionsView>
\ No newline at end of file diff --git a/quickstep/res/layout/task.xml b/quickstep/res/layout/task.xml index 7e5b85c41d..bd11c1ef05 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 0000000000..0c8543f414 --- /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 cd5bcbdddd..ec03c694b8 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.xml b/quickstep/res/layout/taskbar.xml index 3b1d217ec5..94388b42ec 100644 --- a/quickstep/res/layout/taskbar.xml +++ b/quickstep/res/layout/taskbar.xml @@ -45,8 +45,8 @@ android:id="@+id/start_contextual_buttons" android:layout_width="wrap_content" android:layout_height="match_parent" - android:paddingLeft="@dimen/taskbar_nav_buttons_spacing" - android:paddingRight="@dimen/taskbar_nav_buttons_spacing" + 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"/> @@ -56,9 +56,6 @@ android:layout_width="wrap_content" android:layout_height="match_parent" android:orientation="horizontal" - android:paddingLeft="@dimen/taskbar_nav_buttons_spacing" - android:paddingRight="@dimen/taskbar_nav_buttons_spacing" - android:layout_marginEnd="@dimen/taskbar_contextual_button_margin" android:gravity="center_vertical" android:layout_gravity="end"/> @@ -66,8 +63,6 @@ android:id="@+id/end_contextual_buttons" android:layout_width="wrap_content" android:layout_height="match_parent" - android:paddingLeft="@dimen/taskbar_nav_buttons_spacing" - android:paddingRight="@dimen/taskbar_nav_buttons_spacing" android:paddingTop="@dimen/taskbar_contextual_padding_top" android:gravity="center_vertical" android:layout_gravity="end"/> diff --git a/quickstep/res/layout/taskbar_all_apps.xml b/quickstep/res/layout/taskbar_all_apps.xml index 34d4b231b1..976cd9e2a7 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 0000000000..6b665e5623 --- /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 3796ff930e..d7daea31cd 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_pages_persistent.xml b/quickstep/res/layout/taskbar_edu_pages_persistent.xml new file mode 100644 index 0000000000..77ce31af95 --- /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 0000000000..b68e723280 --- /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/transient_taskbar.xml b/quickstep/res/layout/transient_taskbar.xml new file mode 100644 index 0000000000..f9ece84a4d --- /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 0000000000..20280831c9 --- /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 0000000000..0c0e45f132 --- /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 0000000000..7714b41a66 --- /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 0000000000..eec2c243f3 --- /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 0000000000..e7a4cacf70 --- /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 0000000000..0863c31f61 --- /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 0000000000..14e8933da8 --- /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 index 9705837912..859d3567a2 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_persistent.json b/quickstep/res/raw/taskbar_edu_splitscreen_persistent.json new file mode 100644 index 0000000000..1399828446 --- /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 0000000000..0c65a0633b --- /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_transient.json b/quickstep/res/raw/taskbar_edu_stashing_transient.json new file mode 100644 index 0000000000..847a607607 --- /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_persistent.json b/quickstep/res/raw/taskbar_edu_suggestions_persistent.json new file mode 100644 index 0000000000..86ad8cfc1e --- /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 0000000000..b2624b0aba --- /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 e834157504..1605c241c0 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Sleep na die kant om 2 apps tegelyk te gebruik"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Kortswiep op om die taakbalk te wys"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"Die taakbalk stel apps op grond van jou roetine voor"</string> <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 +109,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 342bbe26fa..df9fe66ec0 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"በአንድ ጊዜ 2 መተግበሪያዎችን ለመጠቀም ወደ ጎን ይጎትቱ"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"የተግባር አሞሌውን ለማሳየት አጭር ወደ ላይ ያንሸራትቱ"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"የተግባር አሞሌው የዕለት ተዕለት ተግባርዎ ላይ በመመስረት መተግበሪያዎችን ይጠቁማል"</string> <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 +109,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 f15fcd4a32..a6781ef379 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"اسحبه إلى جانب الشاشة لاستخدام تطبيقَين في آنٍ واحد."</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"مرِّر سريعًا للأعلى تمريرة قصيرة لإظهار شريط التطبيقات."</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"يقترح شريط المهام تطبيقات بناءً على روتينك."</string> <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 +109,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 b33abede88..6e43c19aaf 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"এবাৰতে দুটা এপ্ ব্যৱহাৰ কৰিবলৈ কাষলৈ টানি আনি এৰক"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"টাস্কবাৰ দেখুৱাবলৈ সামান্য পৰিমাণে ওপৰলৈ ছোৱাইপ কৰক"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"টাস্কবাৰে আপোনাৰ ৰুটিনৰ ভিত্তিত এপৰ পৰামৰ্শ দিয়ে"</string> <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 +109,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 07e29bb7db..e844ec1ef9 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Eyni anda 2 tətbiqi istifadə etmək üçün yan tərəfə çəkin"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Tapşırıq panelini göstərmək üçün azca yuxarı sürüşdürün"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"Tapşırıq paneli rejiminizə əsasən tətbiqlər təklif edir"</string> <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 +109,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 42023d3af6..2699c3e4d7 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Prevucite na stranu da biste koristili 2 aplikacije odjednom"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Nakratko prevucite nagore da biste prikazali traku zadataka"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"Traka zadataka predlaže aplikacije na osnovu rutine"</string> <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 +109,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 dc4dc6570c..0f38c75f3e 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Перацягніце ўбок, каб адначасова скарыстаць 2 праграмы"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Хутка правядзіце пальцам уверх, каб убачыць панэль задач"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"На панэлі задач праграмы паказваюцца з улікам вашых дзеянняў"</string> <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 +109,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 560a03b7e1..a2843940ef 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Плъзнете встрани, за да използвате едновременно 2 приложения"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Прекарайте пръст нагоре, за да видите лентата на задачите"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"Лентата на задачите предлага приложения според навиците ви"</string> <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 +109,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 d4774a7ad4..e7691311bc 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"একসাথে ২টি অ্যাপ ব্যবহার করতে পাশে টেনে আনুন"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"টাস্কবার দেখানোর জন্য শর্ট সোয়াইপ করুন"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"আপনার রুটিনের উপর নির্ভর করে টাস্কবার অ্যাপ সাজেস্ট করে"</string> <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 +109,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 3743e24f87..f0118a72c1 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Prevucite u stranu da istovremeno koristite 2 aplikacije"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Nakratko prevucite nagore da prikažete traku zadataka"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"Traka zadataka predlaže aplikacije na osnovu vaše rutine"</string> <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 +109,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 9400fba601..d1219ad26a 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Arrossega al costat per utilitzar 2 aplicacions alhora"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Llisca una mica cap amunt per mostrar la barra de tasques"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"La barra de tasques suggereix apps basades en la teva rutina"</string> <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 +109,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 cb49a35673..868393d343 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Po přetažení na stranu lze používat dvě aplikace současně"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Krátkým přejetím nahoru zobrazíte panel aplikací"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"Panel aplikací navrhuje aplikace na základě vašeho používání"</string> <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 +109,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 621006b8ab..ae2a00a9bc 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Træk til siden for at bruge 2 apps samtidig"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Stryg hurtigt opad for at se proceslinjen"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"Proceslinjen foreslår apps baseret på din rutine"</string> <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 +109,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 d19c23bd68..c7664be036 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Zur Seite ziehen, um zwei Apps gleichzeitig zu verwenden"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Kurz nach oben wischen, um die Taskleiste anzuzeigen"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"Taskleiste empfiehlt Apps basierend auf deinen Gewohnheiten"</string> <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 +109,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 2c6702bcc7..807ce43174 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Σύρετε στο πλάι για ταυτόχρονη χρήση δύο εφαρμογών"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Σύρετε σύντομα προς τα πάνω για εμφάνιση γραμμής εργαλείων"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"Η γραμμή εργαλείων συνιστά εφαρμογές βάσει της ρουτίνας σας"</string> <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 +109,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 7b297af793..414593ab62 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Drag to the side to use two apps at once"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Short swipe up to show the taskbar"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"The taskbar suggests apps based on your routine"</string> <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 +109,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 4013a7193b..41fb803fc8 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,60 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Drag to the side to use 2 apps at once"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Short swipe up to show the taskbar"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"The taskbar suggests apps based on your routine"</string> <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 +109,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 7b297af793..414593ab62 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Drag to the side to use two apps at once"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Short swipe up to show the taskbar"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"The taskbar suggests apps based on your routine"</string> <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 +109,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 7b297af793..414593ab62 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Drag to the side to use two apps at once"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Short swipe up to show the taskbar"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"The taskbar suggests apps based on your routine"</string> <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 +109,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 bd2a02471e..45ce0c1b67 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Drag to the side to use 2 apps at once"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Short swipe up to show the taskbar"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"The taskbar suggests apps based on your routine"</string> <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 +109,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 06ee5bb1b5..cb7f51919f 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Arrastra a un lado para usar 2 apps a la vez"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Desliza un poco hacia arriba para mostrar la barra de tareas"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"La barra de tareas sugiere apps según tu rutina"</string> <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 +109,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 80c8b0bd26..09cd305eed 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Arrastra hacia un lado para usar 2 aplicaciones a la vez"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Desliza un poco hacia arriba para ver la barra de tareas"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"La barra de tareas sugiere aplicaciones según tus hábitos"</string> <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 +109,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 7f37a4bcc3..416031600d 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Kahe rakenduse korraga kasutamiseks lohistage külje poole"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Tegumiriba kuvamiseks pühkige korraks"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"Tegumiribal soovitatakse rakendusi teie rutiinide põhjal"</string> <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 +109,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 4169838294..b302888a32 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Bi aplikazio batera erabiltzeko, arrastatu hatza albo batera"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Ataza-barra ikusteko, pasatu hatza bizkor gora"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"Ataza-barran erabileran oinarritutako aplikazioak iradokitzen dira"</string> <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 +109,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 de6d7e546c..c922f8063e 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"برای استفاده همزمان از دو برنامه، آن را به کنار بکشید"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"برای نمایش نوار وظیفه، کمی به بالا بکشید"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"نوار وظیفه برنامهها را براساس روال شما پیشنهاد میدهد"</string> <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 +109,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 8c50f70fb3..a07fdea641 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Vetämällä sivuun voit käyttää kahta sovellusta samaan aikaan"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Näytä tehtäväpalkki pyyhkäisemällä ylös lyhyesti"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"Tehtäväpalkki ehdottaa sovelluksia ohjelmasi perusteella"</string> <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 +109,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 f92d55c246..cf0fef5f54 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Faites glisser vers le côté pour utiliser 2 applis à la fois"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Faites glisser vers le haut pour afficher la barre de tâches"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"La barre des tâches suggère des applis selon votre routine"</string> <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 +109,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 9ffa0cc415..a20fbc4992 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Faites glisser sur le côté pour utiliser 2 applis à la fois"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Balayez rapidement vers haut pour afficher barre des tâches"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"La barre des tâches suggère des applis selon vos habitudes"</string> <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 +109,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 bea23b7880..528dd0ad42 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Para usar 2 aplicacións á vez, arrastra cara ao lado"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Pasa o dedo un pouco cara arriba para ver a barra de tarefas"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"Na barra de tarefas suxírense apps segundo a túa rutina"</string> <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 +109,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 a146b24c88..0ae9b03972 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"એક જ સમયે 2 ઍપનો ઉપયોગ કરવા માટે, ખેંચીને બાજુ પર લઈ જાઓ"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"ટાસ્કબાર બતાવવા માટે ઉપર થોડું સ્વાઇપ કરો"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"ટાસ્કબાર તમારા રૂટિનના આધારે ઍપ સૂચવે છે"</string> <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 +109,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 643b1d5af5..c9f3930eff 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> @@ -45,18 +44,18 @@ <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"सुझाए गए ऐप्लिकेशन की सुविधा चालू है"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"सुझाए गए ऐप्लिकेशन की सुविधा बंद है"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"सुझाया गया ऐप्लिकेशन: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"पक्का करें कि आप स्क्रीन की दाईं या बाईं ओर के बिल्कुल किनारे से स्वाइप कर रहे हों."</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"पक्का करें कि आप स्क्रीन की दाईं या बाईं ओर के बिलकुल किनारे से स्वाइप कर रहे हों."</string> <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"स्क्रीन के दाएं या बाएं किनारे से स्क्रीन के बीच तक स्वाइप करें और अपनी उंगली उठा लें."</string> <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"आपने स्क्रीन के दाएं किनारे से स्वाइप करके, पिछली स्क्रीन पर वापस जाने का तरीका सीख लिया है. अब, एक ऐप से दूसरे ऐप पर जाने का तरीका सीखें."</string> <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"आपने पेज पर पीछे ले जाने वाले हाथ के जेस्चर (हाव-भाव) के बारे में जान लिया है."</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"देख लें कि आप स्क्रीन पर बिल्कुल नीचे तक स्वाइप न कर रहे हों."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"देख लें कि आप स्क्रीन पर बिलकुल नीचे तक स्वाइप न कर रहे हों."</string> <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"\'सेटिंग\' में जाकर, पीछे जाने के लिए इस्तेमाल होने वाले हाथ के जेस्चर (हाव-भाव) की संवेदनशीलता बदलें"</string> <string name="back_gesture_intro_title" msgid="19551256430224428">"पिछली स्क्रीन पर वापस जाने के लिए स्वाइप करें"</string> <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"पिछली स्क्रीन पर वापस जाने के लिए, स्क्रीन के बाएं या दाएं किनारे से बीचों-बीच तक स्वाइप करें."</string> <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"पिछली स्क्रीन पर वापस जाने के लिए, स्क्रीन के बाएं या दाएं किनारे से स्क्रीन के बीच तक दो उंगलियों से स्वाइप करें."</string> <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_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> @@ -64,7 +63,7 @@ <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> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"देख लें कि आप स्क्रीन पर ऊपर की तरफ़, बिलकुल सीधे स्वाइप कर रहे हों और फिर रुकें."</string> <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"आपने हाथ के जेस्चर इस्तेमाल करने सीख लिए हैं. जेस्चर बंद करने के लिए, सेटिंग में जाएं."</string> <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"आपने एक ऐप्लिकेशन से दूसरे पर जाने के लिए इस्तेमाल होने वाले हाथ के जेस्चर के बारे में जान लिया है."</string> <string name="overview_gesture_intro_title" msgid="2902054412868489378">"एक ऐप्लिकेशन से दूसरे पर जाने के लिए स्वाइप करें"</string> @@ -78,25 +77,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"एक साथ दो ऐप इस्तेमाल करने के लिए, उन्हें किनारे की ओर खींचें और छोड़ें"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"टास्कबार दिखाने के लिए, ऊपर की ओर थोड़ा स्वाइप करें"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"टास्कबार, डिवाइस के इस्तेमाल के आधार पर ऐप के सुझाव देता है"</string> <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 +109,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 640d7ccb1e..dd7de8cbbc 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Povucite u stranu za istovremeno korištenje 2 aplikacije"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Kratki pokret prema gore za prikaz trake sa zadacima"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"Traka sa zadacima predlaže aplikacije na temelju vaše rutine"</string> <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 +109,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 6369293de6..4d01f69666 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Húzza oldalra, ha egyszerre két appot szeretne használni"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Röviden csúsztasson fel a Feladatsáv megjelenítéséhez"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"A Feladatsávban a szokásai alapján javasolt appok láthatók"</string> <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 +109,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 9eeaa98690..28f13a937d 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Քաշեք մի կողմ՝ միաժամանակ 2 հավելված օգտագործելու համար"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Հավելվածների վահանակը բացելու համար մատն արագ սահեցրեք վերև"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"Վահանակն առաջարկում է հավելվածներ ձեր գործողությունների հիման վրա"</string> <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 +109,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 14d8c3effe..6d42c3d470 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Tarik ke samping untuk menggunakan 2 aplikasi sekaligus"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Geser sedikit ke atas untuk menampilkan taskbar"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"Taskbar menyarankan aplikasi berdasarkan rutinitas Anda"</string> <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 +109,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 1761203bf3..1b4ea2bc63 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Dragðu til hliðar til að nota 2 forrit í einu"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Stutt stroka upp til að sýna forritastiku"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"Forritastikan mælir með forritum byggt á rútínunni þinni"</string> <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 +109,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 874d07223d..9a5cc9845a 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Trascina di lato per usare 2 app contemporaneamente"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Scorri verso l\'alto per mostrare la barra delle applicazioni"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"Barra delle applicazioni suggerisce app in base alla routine"</string> <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 +109,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 09d76f89a1..8722476002 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"כדי להשתמש בשתי אפליקציות בו-זמנית, צריך לגרור לצד"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"צריך להחליק מעט כדי להציג את סרגל האפליקציות"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"האפליקציות מוצעות בסרגל האפליקציות על סמך השימוש השגרתי שלך"</string> <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 +109,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 50c031ee90..7bdb5ecd0c 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"2 個のアプリを同時に使用するには、横にドラッグします"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"タスクバーを表示するには、上に短くスワイプします"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"ルーティンに基づくおすすめのアプリがタスクバーに表示されます"</string> <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 +109,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 2bc0108cd6..7259f048f3 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"გადაათრიეთ კიდეზე 2 აპის ერთდროულად გამოსაყენებლად"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"მოკლედ გადაფურცლეთ ზემოთ, რომ ამოცანათა ზოლი გამოაჩინოთ"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"ამოცანათა ზოლი გთავაზობთ აპებს თქვენი რუტინის მიხედვით"</string> <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 +109,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 7758125cad..d0f6a9a9b0 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Екі қолданбаны бір уақытта пайдалану үшін шетке сүйреңіз."</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Тапсырмалар жолағын көру үшін жоғары қарай тез сырғытыңыз."</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"Тапсырмалар жолағындағы ұсыныстар әдеттеріңізге негізделеді."</string> <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 +109,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 93b23611be..f9a7209b8d 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"អូសទៅចំហៀង ដើម្បីប្រើកម្មវិធី 2 ក្នុងពេលតែមួយ"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"អូសឡើងលើបន្តិច ដើម្បីបង្ហាញរបារកិច្ចការ"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"របារកិច្ចការនេះណែនាំកម្មវិធីផ្អែកលើទម្លាប់របស់អ្នក"</string> <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 +109,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 2c1f6995c1..b7c774ae72 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"ಏಕಕಾಲದಲ್ಲಿ 2 ಆ್ಯಪ್ಗಳನ್ನು ಬಳಸಲು ಬದಿಗೆ ಡ್ರ್ಯಾಗ್ ಮಾಡಿ"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"ಟಾಸ್ಕ್ಬಾರ್ ಅನ್ನು ತೋರಿಸಲು ಚಿಕ್ಕದಾಗಿ ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"ಟಾಸ್ಕ್ಬಾರ್ ನಿಮ್ಮ ದಿನಚರಿ ಆಧರಿಸಿ ಆ್ಯಪ್ಗಳನ್ನು ಸೂಚಿಸುತ್ತದೆ"</string> <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 +109,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 6626bc1759..99309d260c 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"옆으로 드래그하여 2개의 앱을 동시에 사용합니다."</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"위로 짧게 스와이프하여 태스크 바를 표시합니다."</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"태스크 바에서 루틴에 따라 앱을 제안합니다."</string> <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 +109,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 dac4d1d7cf..55e5a11872 100644 --- a/quickstep/res/values-ky/strings.xml +++ b/quickstep/res/values-ky/strings.xml @@ -35,7 +35,6 @@ <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Сунушталган колдонмолор башкы экрандагы тандалмалардын катарында көрүнөт."</string> <string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Көп колдонулган колдонмолор башкы экранда жайгашып, алардын тизмеси маал-маалы менен өзгөрүп турат. Ылдый жакта жайгашкан тилкедеги колдонмолор башкы экранга жылдырылат."</string> <string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Көп иштетилген колдонмолорго Башкы экрандан оңой кириңиз. Сунуштар тартиптин негизинде өзгөрөт. Тандалмалардын катарындагы колдонмолор башкы экраныңызга жылдырылат."</string> - <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Көп колдонулган колдонмолор башкы экранда жайгашып, алардын тизмеси маал-маалы менен өзгөрүп турат. Ылдый жакта жайгашкан тилкедеги колдонмолор жаңы папкага жылдырылат."</string> <string name="hotseat_edu_accept" msgid="1611544083278999837">"Сунушталган колдонолорду алуу"</string> <string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Жок, рахмат"</string> <string name="hotseat_prediction_settings" msgid="6246554993566070818">"Жөндөөлөр"</string> @@ -78,25 +77,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"2 колдонмону бир убакта пайдалануу үчүн капталга сүйрөңүз"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Тапшырмалар тактасын көрүү үчүн экранды өйдө серпиңиз"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"Тапшырмалар тактасы колдонмолорду аракеттериңизге жараша сунуштайт"</string> <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 +109,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 f233bde73a..30983c4d5a 100644 --- a/quickstep/res/values-land/dimens.xml +++ b/quickstep/res/values-land/dimens.xml @@ -15,7 +15,8 @@ limitations under the License. --> <resources> - <dimen name="overview_task_margin">8dp</dimen> + <!-- Overview actions --> + <dimen name="overview_actions_top_margin">12dp</dimen> <!-- Tips Gesture Tutorial --> <dimen name="gesture_tutorial_feedback_margin_start_end">126dp</dimen> @@ -73,4 +74,12 @@ <!-- Gesture Tutorial mock taskbar --> <dimen name="gesture_tutorial_taskbar_padding_start_end">218dp</dimen> + + <!-- Taskbar 3 button spacing --> + <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 f3c3cbda0e..4be7c8cb37 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"ລາກໄປທາງຂ້າງເພື່ອໃຊ້ 2 ແອັບພ້ອມກັນ"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"ປັດຂຶ້ນສັ້ນໆເພື່ອສະແດງແຖບໜ້າວຽກ"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"ແຖບໜ້າວຽກຈະແນະນຳແອັບໂດຍອີງຕາມສິ່ງທີ່ເຮັດປະຈຳຂອງທ່ານ"</string> <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 +109,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 25a84a7313..56a5d08e3e 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Nuvilkite į šoną, kad vienu metu naudotumėte dvi programas"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Trumpai perbraukite, kad būtų rodoma užduočių juosta"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"Užduočių juostoje siūlomos programos pagal jūsų veiklą"</string> <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 +109,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 ee25cb5008..da911c1378 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Velciet uz malu, lai izmantotu divas lietotnes vienlaikus"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Īsi velciet augšup, lai skatītu uzdevumu joslu"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"Uzdevumu joslā tiek rādītas lietotnes, ņemot vērā lietojumu"</string> <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 +109,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 75f093316e..9deb0dcde3 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Повлечете кон страната за да користите две апликации одеднаш"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Кратко повлечете нагоре за да се прикаже лентата со задачи"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"Лентата со задачи предложува апликации според вашата рутина"</string> <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 +109,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 9f00e91b64..bca7e350de 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"ഒരേ സമയം 2 ആപ്പുകൾ ഉപയോഗിക്കാൻ വശത്തേയ്ക്ക് വലിച്ചിടുക"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"ടാസ്ക്ബാർ ദൃശ്യമാക്കാൻ മുകളിലേക്ക് ചെറുതായി സ്വൈപ്പ് ചെയ്യൂ"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"ടാസ്ക്ബാർ നിങ്ങളുടെ ദിനചര്യ അനുസരിച്ച് ആപ്പുകൾ നിർദ്ദേശിക്കുന്നു"</string> <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 +109,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 59728a6136..9ad53c2a68 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"2 аппыг зэрэг ашиглахын тулд хажуу тийш чирнэ үү"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Ажлын хэсгийг харуулахын тулд бага зэрэг дээш шударна уу"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"Ажлын хэсэг таны хэвшилд тулгуурлан аппууд санал болгодог"</string> <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 +109,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 5f2fb0c5d2..ab97cc9114 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"दोन ॲप्स एकत्र वापरण्यासाठी, ती बाजूला ड्रॅग करा"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"टास्कबार दाखवण्यासाठी थोडे वरती स्वाइप करा"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"टास्कबार हे तुमच्या दिनक्रमानुसार अॅप्स सुचवते"</string> <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 +109,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 27dcc41cb6..b7e82d0009 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Seret ke tepi untuk menggunakan 2 apl serentak"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Leret pendek ke atas untuk menunjukkan bar tugas"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"Bar tugas mencadangkan apl berdasarkan rutin anda"</string> <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 +109,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 825b6f23e9..a5100f1d60 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"အက်ပ် ၂ ခု တစ်ပြိုင်တည်းသုံးရန် ဘေးဖက်သို့ ဖိဆွဲပါ"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"လုပ်ဆောင်စရာဘားပြရန် အမြန် အပေါ်ပွတ်ဆွဲပါ"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"ပုံမှန်အစီအစဉ်ပေါ် အခြေခံ၍ လုပ်ဆောင်စရာဘားက အက်ပ်အကြံပြုသည်"</string> <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 +109,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 250724e1af..ce7634b632 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Dra til siden for å bruke to apper samtidig"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Kort sveip opp for å vise oppgavelinjen"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"Oppgavelinjen foreslår apper basert på rutinene dine"</string> <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 +109,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 f7214b02d0..b984de8102 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"टास्कबार छेउतिर ड्र्याग गरेर एकै पटक २ वटा एप चलाउनुहोस्"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"टास्कबार देखाउन माथितिर थोरै स्वाइप गर्नुहोस्"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"टास्कबारले तपाईंको रुटिनका आधारमा एपसम्बन्धी सुझाव देखाउँछ"</string> <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 +109,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 ff5e45ebd6..ad207c0734 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Sleep naar de zijkant om 2 apps tegelijk te gebruiken"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Swipe kort omhoog om de taakbalk te bekijken"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"De taakbalk stelt apps voor op basis van je routine"</string> <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 +109,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 9398f523a9..2f0614c002 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,41 @@ <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_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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"ଥରକେ 2ଟି ଆପ୍ସ ବ୍ୟବହାର କରିବା ପାଇଁ ପାର୍ଶ୍ୱକୁ ଡ୍ରାଗ କରନ୍ତୁ"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"ଟାସ୍କବାର ଦେଖାଇବା ପାଇଁ ଉପରକୁ ଅଳ୍ପ ସମୟ ସ୍ୱାଇପ କରନ୍ତୁ"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"ଟାସ୍କବାର ଆପଣଙ୍କ ରୁଟିନ ଆଧାରରେ ଆପ୍ସ ପରାମର୍ଶ ଦିଏ"</string> <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 83def858df..dc9d78e571 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"ਇੱਕ ਵਾਰ ਵਿੱਚ 2 ਐਪਾਂ ਵਰਤਣ ਲਈ ਉਨ੍ਹਾਂ ਨੂੰ ਸਾਈਡ ਵੱਲ ਘਸੀਟੋ"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"ਟਾਸਕਬਾਰ ਦਿਖਾਉਣ ਲਈ ਥੋੜ੍ਹਾ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰੋ"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"ਟਾਸਕਬਾਰ ਤੁਹਾਡੇ ਨਿਯਮਬੱਧ ਕੰਮ ਮੁਤਾਬਕ ਐਪਾਂ ਦਾ ਸੁਝਾਅ ਦਿੰਦਾ ਹੈ"</string> <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 +109,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 077982a30b..08fc4e2eac 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Przeciągnij w bok, aby używać 2 aplikacji jednocześnie"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Przesuń palcem krótko w górę, aby wyświetlić pasek aplikacji"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"Aplikacje na pasku są dobierane na podstawie Twojej rutyny"</string> <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 +109,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 124b33b758..b663528b07 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Arraste para o lado para usar duas apps em simultâneo"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Deslize rápido curto para cima para ver a barra de tarefas"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"A barra de tarefas sugere apps baseadas na sua rotina"</string> <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 +109,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 199c2f45ff..fbd5b782b8 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Arraste para o lado para usar dois apps ao mesmo tempo"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Deslize para cima para mostrar a barra de tarefas"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"A barra de tarefas sugere apps com base na sua rotina"</string> <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 +109,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 fb661f24f5..a324d4a4d5 100644 --- a/quickstep/res/values-ro/strings.xml +++ b/quickstep/res/values-ro/strings.xml @@ -19,11 +19,11 @@ <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">"Fixați"</string> + <string name="recent_task_option_pin" msgid="7929860679018978258">"Fixează"</string> <string name="recent_task_option_freeform" msgid="48863056265284071">"Formă liberă"</string> <string name="recents_empty_message" msgid="7040467240571714191">"Niciun element recent"</string> <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Setări de utilizare a aplicației"</string> - <string name="recents_clear_all" msgid="5328176793634888831">"Ștergeți tot"</string> + <string name="recents_clear_all" msgid="5328176793634888831">"Șterge tot"</string> <string name="accessibility_recent_apps" msgid="4058661986695117371">"Aplicații recente"</string> <string name="task_view_closed" msgid="9170038230110856166">"Activitatea s-a încheiat"</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> @@ -31,75 +31,76 @@ <string name="time_left_for_app" msgid="3111996412933644358">"Au mai rămas <xliff:g id="TIME">%1$s</xliff:g> astăzi"</string> <string name="title_app_suggestions" msgid="4185902664111965088">"Sugestii de aplicații"</string> <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Aplicațiile estimate"</string> - <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Primiți sugestii de aplicații în rândul de jos al ecranului de pornire"</string> - <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Primiți sugestii de aplicații în rândul de preferințe al ecranului de pornire"</string> - <string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Accesați 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">"Accesați cu ușurință cele mai folosite aplicații direct din ecranul de pornire. Sugestiile se vor schimba în funcție de rutina dvs. 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">"Accesați 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">"Primiți sugestii de aplicații"</string> + <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Primește sugestii de aplicații în rândul de jos al ecranului de pornire"</string> + <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_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> <string name="hotseat_auto_enrolled" msgid="522100018967146807">"Cele mai folosite aplicații apar aici și se modifică în funcție de rutine"</string> - <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Trageți aplicații din rândul de jos pentru a primi sugestii de aplicații"</string> + <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Trage aplicații din rândul de jos pentru a primi sugestii de aplicații"</string> <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"Sugestiile de aplicații sunt adăugate în spațiile goale"</string> <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Sugestiile de aplicații au fost activate"</string> <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Sugestiile de aplicații au fost dezactivate"</string> <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Aplicația estimată: <xliff:g id="TITLE">%1$s</xliff:g>"</string> - <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Glisați dinspre marginea dreaptă îndepărtată sau dinspre marginea stângă îndepărtată."</string> - <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Glisați dinspre marginea dreaptă sau stângă spre mijlocul ecranului și eliberați."</string> - <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Ați învățat cum să glisați dinspre dreapta pentru a reveni. În continuare, aflați cum să comutați aplicațiile."</string> - <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"Ați finalizat gestul „înapoi”."</string> - <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Nu glisați prea aproape de partea de jos a ecranului."</string> - <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Schimbați sensibilitatea gestului „Înapoi” accesând Setările"</string> - <string name="back_gesture_intro_title" msgid="19551256430224428">"Glisați pentru a reveni"</string> - <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Pentru a reveni la ultimul ecran, glisați de la marginea stângă sau dreaptă spre mijlocul ecranului."</string> - <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Pentru a reveni la ultimul ecran, glisați cu două degete dinspre marginea stângă sau dreaptă spre mijlocul ecranului."</string> - <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Glisați în sus dinspre marginea de jos a ecranului."</string> - <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Nu întrerupeți gestul înainte de a elibera."</string> - <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Glisați direct în sus."</string> - <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"Ați finalizat gestul „accesați ecranul de pornire”. În continuare, aflați cum să reveniți."</string> - <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"Ați finalizat gestul „accesați ecranul de pornire”."</string> - <string name="home_gesture_intro_title" msgid="836590312858441830">"Glisați pentru a accesa ecranul de pornire"</string> - <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Glisați în sus din partea de jos a ecranului. Cu acest gest accesați întotdeauna ecranul de pornire."</string> - <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Glisați în sus cu 2 degete din partea de jos. Cu acest gest accesați întotdeauna ecranul de pornire."</string> - <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Glisați în sus dinspre marginea de jos a ecranului."</string> - <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Încercați să țineți fereastra mai mult înainte s-o eliberați."</string> - <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Glisați direct în sus, apoi întrerupeți."</string> - <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Ați învățat cum să folosiți gesturi. Pentru a dezactiva gesturile, accesați Setările."</string> - <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"Ați finalizat gestul „comutați între aplicații”."</string> - <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Glisați pentru a comuta între aplicații"</string> - <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Ca să comutați între aplicații, glisați în sus din partea de jos a ecranului, așteptați și eliberați."</string> - <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Ca să comutați între aplicații, glisați în sus cu 2 degete din partea de jos, așteptați și eliberați"</string> + <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="1711645592102201538">"Glisează dinspre marginea dreaptă îndepărtată sau dinspre marginea stângă îndepărtată."</string> + <string name="back_gesture_feedback_cancelled" msgid="3274382913290074496">"Glisează dinspre marginea dreaptă sau stângă spre mijlocul ecranului și eliberează."</string> + <string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Ai învățat cum să glisezi din dreapta pentru a reveni. Acum află cum să comuți aplicațiile."</string> + <string name="back_gesture_feedback_complete_without_follow_up" msgid="6405649621667113830">"Ai finalizat gestul „înapoi”."</string> + <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Nu glisa prea aproape de partea de jos a ecranului."</string> + <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Schimbă sensibilitatea gestului „Înapoi” accesând Setările"</string> + <string name="back_gesture_intro_title" msgid="19551256430224428">"Glisează pentru a reveni"</string> + <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Pentru a reveni la ultimul ecran, glisează de la marginea stângă sau dreaptă spre mijlocul ecranului."</string> + <string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Pentru a reveni la ultimul ecran, glisează cu două degete dinspre marginea stângă sau dreaptă spre mijlocul ecranului."</string> + <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Glisează în sus dinspre marginea de jos a ecranului."</string> + <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Nu întrerupe gestul înainte de a elibera."</string> + <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Glisează direct în sus."</string> + <string name="home_gesture_feedback_complete_with_follow_up" msgid="1427872029729605034">"Ai finalizat gestul „accesează ecranul de pornire”. Acum află cum să revii."</string> + <string name="home_gesture_feedback_complete_without_follow_up" msgid="8049099486868933882">"Ai finalizat gestul „accesează ecranul de pornire”."</string> + <string name="home_gesture_intro_title" msgid="836590312858441830">"Glisează pentru a accesa ecranul de pornire"</string> + <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Glisează în sus din partea de jos a ecranului. Cu acest gest accesezi mereu ecranul de pornire."</string> + <string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Glisează în sus cu două degete din partea de jos. Cu acest gest accesezi mereu ecranul de pornire."</string> + <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Glisează în sus dinspre marginea de jos a ecranului."</string> + <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Încearcă să ții fereastra mai mult înainte s-o eliberezi."</string> + <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Glisează direct în sus, apoi întrerupe."</string> + <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Ai învățat să folosești gesturi. Pentru a dezactiva gesturile, accesează Setările."</string> + <string name="overview_gesture_feedback_complete_without_follow_up" msgid="3199486203448379152">"Ai finalizat gestul „comută între aplicații”."</string> + <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Glisează pentru a comuta între aplicații"</string> + <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Ca să comuți între aplicații, glisează în sus din partea de jos a ecranului, așteaptă și eliberează."</string> + <string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Ca să comuți între aplicații, glisează cu două degete de jos în sus, așteaptă și eliberează"</string> <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Gata"</string> <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Gata"</string> <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Setări"</string> - <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Reîncercați"</string> + <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Reîncearcă"</string> <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Bravo!"</string> <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">"Glisați în sus pentru a accesa ecranul de pornire"</string> - <string name="allset_description" msgid="6350320429953234580">"Sunteți gata să folosiți telefonul"</string> - <string name="allset_description_tablet" msgid="7332070270570039247">"Sunteți gata să folosiți tableta"</string> + <string name="allset_hint" msgid="2384632994739392447">"Glisează în sus pentru a accesa ecranul de pornire"</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">"Distribuiți"</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ți altă aplicație pentru a folosi ecranul împărțit"</string> - <string name="toast_split_app_unsupported" msgid="3271526028981899666">"Aplicația nu acceptă 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 dvs."</string> - <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Omiteți tutorialul de navigare?"</string> - <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Îl puteț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">"Anulați"</string> - <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Omiteți"</string> - <string name="accessibility_rotate_button" msgid="4771825231336502943">"Rotiți ecranul"</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="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">"Folosiți bara de activități ca să comutați între aplicații"</string> - <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Trageți în lateral ca să folosiți două aplicații deodată"</string> - <string name="taskbar_edu_stashing" msgid="5212374387411764031">"Atingeți lung oricând pentru a ascunde bara de activități"</string> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Trage în lateral ca să folosești două aplicații deodată"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Glisează scurt în sus pentru a afișa bara de activități"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"Bara de activități sugerează aplicații în funcție de rutina ta"</string> <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ți"</string> + <string name="taskbar_edu_close" msgid="887022990168191073">"Închide"</string> <string name="taskbar_edu_done" msgid="6880178093977704569">"Gata"</string> <string name="taskbar_button_home" msgid="2151398979630664652">"Ecran de pornire"</string> <string name="taskbar_button_a11y" msgid="5241161324875094465">"Accesibilitate"</string> @@ -108,6 +109,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="move_drop_target_top_or_left" msgid="2988702185049595807">"Mutați în stânga sus"</string> - <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mutați în dreapta jos"</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 44b72cf7d2..d1c2e8d9dd 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Перетащите в сторону, чтобы использовать 2 приложения сразу."</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Чтобы открыть панель задач, быстро проведите снизу вверх."</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"Панель задач предлагает приложения, учитывая ваши действия."</string> <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 +109,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 31749ccf04..e2f0944902 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"එකවර යෙදුම් 2 ක් භාවිත කිරීමට පැත්තට අදින්න"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"කාර්ය තීරුව පෙන්වීමට ඉහළට කෙටි ස්වයිප් කරන්න"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"කාර්ය තීරුව ඔබේ දිනචරියාව මත පදනම්ව යෙදුම් යෝජනා කරයි"</string> <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 +109,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 5d51d45424..573c17b168 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Po presunutí na stranu je možné používať dve aplikácie naraz"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Krátkym potiahnutím nahor zobrazíte panel aplikácií"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"Panel aplikácií navrhuje aplikácie na základe vašich zvykov"</string> <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 +109,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 e196e5df09..491b13de49 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Povlecite vstran za uporabo dveh aplikacij hkrati."</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Na kratko povlecite navzgor za prikaz opravilne vrstice."</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"Opravilna vrstica predlaga aplikacije na podlagi vaših navad."</string> <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 +109,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 39771cb616..d73aa4836d 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Zvarrit në anë për të përdorur 2 aplikacione njëherësh"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Kryej një rrëshqitje të shkurtër lart për të shfaqur shiritin e detyrave"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"Shiriti i detyrave sugjeron aplikacione bazuar në rutinën tënde"</string> <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 +109,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 067a7fe325..6e819abdf8 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Превуците на страну да бисте користили 2 апликације одједном"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Накратко превуците нагоре да бисте приказали траку задатака"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"Трака задатака предлаже апликације на основу рутине"</string> <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 +109,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 3bb47c3caf..bab195241c 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Dra till sidan om du vill använda två appar samtidigt"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Svep en kort bit uppåt för att visa aktivitetsfältet"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"I aktivitetsfältet visas förslag på appar utifrån din rutin"</string> <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 +109,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 16891a8ceb..f0990b3a27 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Buruta pembeni ili utumie programu 2 kwa wakati mmoja"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Telezesha kidole juu ili uonyeshe upauzana"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"Upauzana hupendekeza programu kulingana na ratiba yako"</string> <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 +109,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 0cd9b2b700..dc10c24745 100644 --- a/quickstep/res/values-sw600dp-land/dimens.xml +++ b/quickstep/res/values-sw600dp-land/dimens.xml @@ -17,4 +17,8 @@ <resources> <!-- Overview actions --> <dimen name="overview_actions_top_margin">12dp</dimen> + + <!-- All Set page --> + <dimen name="allset_page_margin_horizontal">48dp</dimen> + </resources> diff --git a/quickstep/res/values-sw600dp/dimens.xml b/quickstep/res/values-sw600dp/dimens.xml index 7494683880..5899814600 100644 --- a/quickstep/res/values-sw600dp/dimens.xml +++ b/quickstep/res/values-sw600dp/dimens.xml @@ -14,16 +14,29 @@ * 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> + + <!-- 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 ceaa8f866b..d27561ab1d 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 5c73734a14..1db0fab7d9 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"ஒரே நேரத்தில் 2 ஆப்ஸை உபயோகிக்க பக்கவாட்டிற்கு இழுக்கவும்"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"செயல் பட்டியைக் காட்டுவதற்கு மேலே சிறிது ஸ்வைப் செய்யவும்"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"உங்கள் வழக்கத்திற்கேற்ப ஆப்ஸை செயல் பட்டி பரிந்துரைக்கும்"</string> <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 +109,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 332e95dfe8..c5564ddf43 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"ఒకేసారి 2 యాప్లను ఉపయోగించడానికి పక్కకు లాగండి"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"టాస్క్బార్ను చూపడానికి చిన్నగా పైకి స్వైప్ చేయండి"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"మీ రొటీన్ ఆధారంగా టాస్క్బార్ యాప్లను సూచిస్తుంది"</string> <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 +109,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 59a84ff286..7c7f44d4ee 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"ลากไปด้านข้างเพื่อใช้ 2 แอปพร้อมกัน"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"ปัดขึ้นสั้นๆ เพื่อแสดงแถบงาน"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"แถบงานจะแนะนำแอปโดยอิงตามกิจวัตรของคุณ"</string> <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 +109,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 b22d85d9af..7dcae6b8b6 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"I-drag sa gilid para makagamit ng 2 app nang sabay"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Mag-swipe nang bahagya pataas para ipakita ang taskbar"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"Nagmumungkahi ang taskbar ng mga app batay sa iyong routine"</string> <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 +109,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 b3054f7a69..ea3fb5e14b 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Tek seferde iki uygulamayı kullanmak için yana sürükleyin"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Görev çubuğunu göstermek için yukarı doğru kısa kaydırın"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"Görev çubuğu, rutininize dayalı olarak uygulamalar önerir"</string> <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 +109,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 490ac2da1a..b426c8baec 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Перетягніть убік, щоб використовувати 2 додатки одночасно"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Швидко проведіть пальцем угору, щоб відкрити панель завдань"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"Панель завдань пропонує додатки на основі вашої програми"</string> <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 +109,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 8f2da6560d..576386a395 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"ایک وقت میں 2 ایپس استعمال کرنے کے لیے سائیڈ پر گھسیٹیں"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"ٹاسک بار دکھانے کے لیے تھوڑا اوپر سوائپ کریں"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"ٹاسک بار آپ کی روٹین کی بنیاد پر ایپس تجویز کرتا ہے"</string> <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 +109,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 9c47cef299..65197a4ae7 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Bir vaqtda 2 ta ilova ochish uchun birini yoniga torting"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Vazifalar panelini koʻrsatish uchun tepaga qisqa suring"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"Vazifalar paneli harakatlaringiz asosida ilova taklif qiladi"</string> <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 +109,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 2e5c3e7976..b0a6dab134 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Kéo sang bên để dùng 2 ứng dụng cùng một lúc"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Vuốt lên một chút để hiển thị thanh tác vụ"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"Thanh tác vụ đề xuất các ứng dụng dựa trên thói quen của bạn"</string> <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 +109,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 0b5193f449..6b8f3e35b9 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"拖动到一侧,即可一次使用两个应用"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"稍微向上滑动即可显示任务栏"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"任务栏会根据您的日常安排向您推荐应用"</string> <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 +109,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 b700da01bd..5a9e0a1e01 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"拖曳到一邊即可同時使用 2 個應用程式"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"輕輕向上滑動即可顯示工作列"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"工作列會根據您的日常安排提供應用程式建議"</string> <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 +109,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 e7b74a4c84..30ea100882 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"拖曳到一邊即可同時使用 2 個應用程式"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"稍微向上滑動即可讓工作列顯示在畫面上"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"工作列會根據你的日常習慣提供應用程式建議"</string> <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 +109,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 c69f63d200..0a7e418d81 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,27 @@ <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> + <string name="taskbar_edu_splitscreen" msgid="5563823414110661454">"Hudula ngaseceleni ukuze usebenzise ama-app angu-2 ngesikhathi esisodwa"</string> + <string name="taskbar_edu_stashing" msgid="2805035263048176462">"Swayiphela phezulu okufushane ukuze ubonise i-taskbar"</string> + <string name="taskbar_edu_suggestions" msgid="1416699696825090402">"I-taskbar iphakamisa ama-app ezisuselwe kumjikelezo wakho"</string> <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 +109,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 185c815ea1..f63997bdbb 100644 --- a/quickstep/res/values/colors.xml +++ b/quickstep/res/values/colors.xml @@ -76,4 +76,7 @@ <color name="all_set_page_background">#FFFFFFFF</color> + <!-- Recents overview --> + <color name="recents_filter_icon">#333333</color> + </resources>
\ No newline at end of file diff --git a/quickstep/res/values/config.xml b/quickstep/res/values/config.xml index 3b4a28b39c..a91507ce92 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 3072a3ee10..2eb4abc3fb 100644 --- a/quickstep/res/values/dimens.xml +++ b/quickstep/res/values/dimens.xml @@ -31,13 +31,19 @@ <dimen name="overview_minimum_next_prev_size">50dp</dimen> - <!-- 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 horizontal space between tasks --> <dimen name="overview_page_spacing">16dp</dimen> - <item name="overview_max_scale" format="float" type="dimen">0.7</item> + <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. --> @@ -66,7 +72,6 @@ <dimen name="quickstep_fling_threshold_speed">0.5dp</dimen> <!-- Launcher app transition --> - <item name="content_scale" format="float" type="dimen">0.97</item> <dimen name="closing_window_trans_y">115dp</dimen> <dimen name="quick_switch_scaling_scroll_threshold">100dp</dimen> @@ -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> @@ -239,7 +247,7 @@ <dimen name="navigation_key_padding">0dp</dimen> <!-- Floating rotation button --> - <dimen name="floating_rotation_button_diameter">40dp</dimen> + <dimen name="floating_rotation_button_diameter">52dp</dimen> <dimen name="floating_rotation_button_min_margin">20dp</dimen> <dimen name="floating_rotation_button_taskbar_left_margin">20dp</dimen> <dimen name="floating_rotation_button_taskbar_bottom_margin">10dp</dimen> @@ -247,25 +255,65 @@ <!-- 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_nav_buttons_spacing">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">40dp</dimen> - <dimen name="taskbar_hotseat_nav_spacing">42dp</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> <dimen name="taskbar_back_button_left_margin_kids">48dp</dimen> <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_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> + + <!-- 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/override.xml b/quickstep/res/values/override.xml index 705ec9d48c..4f472f0625 100644 --- a/quickstep/res/values/override.xml +++ b/quickstep/res/values/override.xml @@ -25,4 +25,6 @@ <string name="model_delegate_class" translatable="false">com.android.launcher3.model.QuickstepModelDelegate</string> + <string name="secondary_display_predictions_class" translatable="false">com.android.launcher3.secondarydisplay.SecondaryDisplayPredictionsImpl</string> + </resources> diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml index 81b0dd297c..e691522d5e 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,18 @@ <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 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">The taskbar suggests apps based on your routine</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 +265,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 7225220876..eb75084697 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> diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java deleted file mode 100644 index 2239102c4d..0000000000 --- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java +++ /dev/null @@ -1,641 +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.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.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.UnfoldTransitionConfig; - -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; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - 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 = UnfoldTransitionFactory.createConfig(this); - if (config.isEnabled()) { - mUnfoldTransitionProgressProvider = - UnfoldTransitionFactory.createUnfoldTransitionProgressProvider( - this, - config, - ProxyScreenStatusProvider.INSTANCE, - getSystemService(DeviceStateManager.class), - 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); - } - } -} diff --git a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java index 62603e9d54..95a94ec149 100644 --- a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java +++ b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java @@ -28,13 +28,13 @@ import android.annotation.TargetApi; import android.content.Context; import android.os.Build; import android.os.Handler; +import android.view.RemoteAnimationTarget; import androidx.annotation.BinderThread; import androidx.annotation.Nullable; import androidx.annotation.UiThread; import com.android.systemui.shared.system.RemoteAnimationRunnerCompat; -import com.android.systemui.shared.system.RemoteAnimationTargetCompat; import java.lang.ref.WeakReference; @@ -55,7 +55,7 @@ import java.lang.ref.WeakReference; * reference to the runner, leaving only the weak ref from the runner. */ @TargetApi(Build.VERSION_CODES.P) -public class LauncherAnimationRunner implements RemoteAnimationRunnerCompat { +public class LauncherAnimationRunner extends RemoteAnimationRunnerCompat { private static final RemoteAnimationFactory DEFAULT_FACTORY = (transit, appTargets, wallpaperTargets, nonAppTargets, result) -> @@ -82,9 +82,9 @@ public class LauncherAnimationRunner implements RemoteAnimationRunnerCompat { @BinderThread public void onAnimationStart( int transit, - RemoteAnimationTargetCompat[] appTargets, - RemoteAnimationTargetCompat[] wallpaperTargets, - RemoteAnimationTargetCompat[] nonAppTargets, + RemoteAnimationTarget[] appTargets, + RemoteAnimationTarget[] wallpaperTargets, + RemoteAnimationTarget[] nonAppTargets, Runnable runnable) { Runnable r = () -> { finishExistingAnimation(); @@ -99,22 +99,6 @@ public class LauncherAnimationRunner implements RemoteAnimationRunnerCompat { } } - // Called only in R platform - @BinderThread - public void onAnimationStart(RemoteAnimationTargetCompat[] appTargets, - RemoteAnimationTargetCompat[] wallpaperTargets, Runnable runnable) { - onAnimationStart(0 /* transit */, appTargets, wallpaperTargets, - new RemoteAnimationTargetCompat[0], runnable); - } - - // Called only in Q platform - @BinderThread - @Deprecated - public void onAnimationStart(RemoteAnimationTargetCompat[] appTargets, Runnable runnable) { - onAnimationStart(appTargets, new RemoteAnimationTargetCompat[0], runnable); - } - - private RemoteAnimationFactory getFactory() { RemoteAnimationFactory factory = mFactory.get(); return factory != null ? factory : DEFAULT_FACTORY; @@ -133,7 +117,7 @@ public class LauncherAnimationRunner implements RemoteAnimationRunnerCompat { */ @BinderThread @Override - public void onAnimationCancelled() { + public void onAnimationCancelled(boolean isKeyguardOccluded) { postAsyncCallback(mHandler, () -> { finishExistingAnimation(); getFactory().onAnimationCancelled(); @@ -229,9 +213,9 @@ public class LauncherAnimationRunner implements RemoteAnimationRunnerCompat { * call {@link AnimationResult#setAnimation} with the target animation to be run. */ void onCreateAnimation(int transit, - RemoteAnimationTargetCompat[] appTargets, - RemoteAnimationTargetCompat[] wallpaperTargets, - RemoteAnimationTargetCompat[] nonAppTargets, + RemoteAnimationTarget[] appTargets, + RemoteAnimationTarget[] wallpaperTargets, + RemoteAnimationTarget[] nonAppTargets, LauncherAnimationRunner.AnimationResult result); /** diff --git a/quickstep/src/com/android/launcher3/LauncherInitListener.java b/quickstep/src/com/android/launcher3/LauncherInitListener.java index 35151f1a68..28bd701a48 100644 --- a/quickstep/src/com/android/launcher3/LauncherInitListener.java +++ b/quickstep/src/com/android/launcher3/LauncherInitListener.java @@ -19,10 +19,11 @@ import android.animation.AnimatorSet; 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 class LauncherInitListener extends ActivityInitListener<Launcher> { 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 e1a3b729c1..2aa0af4113 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; @@ -31,7 +41,6 @@ import static com.android.launcher3.LauncherState.BACKGROUND_APP; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.Utilities.mapBoundToRange; -import static com.android.launcher3.Utilities.postAsyncCallback; import static com.android.launcher3.anim.Interpolators.ACCEL_1_5; import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE; import static com.android.launcher3.anim.Interpolators.DEACCEL_1_5; @@ -43,22 +52,21 @@ import static com.android.launcher3.config.FeatureFlags.ENABLE_SCRIM_FOR_APP_LAU 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.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.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; @@ -78,16 +86,22 @@ import android.os.Looper; 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; @@ -96,13 +110,18 @@ import androidx.core.graphics.ColorUtils; 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; import com.android.launcher3.statehandlers.DepthController; 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 +137,19 @@ import com.android.quickstep.util.MultiValueUpdateListener; 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 +158,6 @@ import java.util.List; */ 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 +194,11 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener 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. + public static final int TASKBAR_TO_HOME_DURATION = 300; protected static final int CONTENT_SCALE_DURATION = 350; protected static final int CONTENT_SCRIM_DURATION = 350; @@ -192,12 +207,11 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener // 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; - private final float mContentScale; private final float mClosingWindowTransY; private final float mMaxShadowRadius; @@ -212,7 +226,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener private RemoteAnimationFactory mKeyguardGoingAwayRunner; private RemoteAnimationFactory mWallpaperOpenTransitionRunner; - private RemoteTransitionCompat mLauncherOpenTransition; + private RemoteTransition mLauncherOpenTransition; private LauncherBackAnimationController mBackAnimationController; private final AnimatorListenerAdapter mForceInvisibleListener = new AnimatorListenerAdapter() { @@ -242,7 +256,6 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener mBackAnimationController = new LauncherBackAnimationController(mLauncher, this); Resources res = mLauncher.getResources(); - mContentScale = res.getFloat(R.dimen.content_scale); mClosingWindowTransY = res.getDimensionPixelSize(R.dimen.closing_window_trans_y); mMaxShadowRadius = res.getDimensionPixelSize(R.dimen.max_shadow_radius); @@ -290,11 +303,11 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener 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); } /** @@ -308,7 +321,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener * @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; } @@ -322,18 +335,18 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener * @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; } @@ -351,21 +364,18 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener * @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) { @@ -379,40 +389,19 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener 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)); } /** @@ -420,10 +409,10 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener * 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; @@ -445,7 +434,9 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener 4 - rotationChange); } } - if (mDeviceProfile.isTaskbarPresentInApps) { + if (mDeviceProfile.isTaskbarPresentInApps + && !target.willShowImeOnTarget + && !DisplayController.isTransientTaskbar(mLauncher)) { // Animate to above the taskbar. bounds.bottom -= target.contentInsets.bottom; } @@ -480,8 +471,8 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener : new float[]{0, 1}; float[] scales = isAppOpening - ? new float[]{1, mContentScale} - : new float[]{mContentScale, 1}; + ? new float[]{1, mDeviceProfile.workspaceContentScale} + : new float[]{mDeviceProfile.workspaceContentScale, 1}; // Pause expensive view updates as they can lead to layer thrashing and skipped frames. mLauncher.pauseExpensiveViewUpdates(); @@ -491,6 +482,10 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener 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); @@ -518,6 +513,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener 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); @@ -527,7 +523,15 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener workspace.forEachVisiblePage( view -> viewsToAnimate.add(((CellLayout) view).getShortcutsAndWidgets())); - viewsToAnimate.add(mLauncher.getHotseat()); + // Do not scale hotseat as a whole when taskbar is present, and scale QSB only if it's + // not inline. + if (mDeviceProfile.isTaskbarPresent) { + if (!mDeviceProfile.isQsbInline) { + viewsToAnimate.add(mLauncher.getHotseat().getQsb()); + } + } else { + viewsToAnimate.add(mLauncher.getHotseat()); + } viewsToAnimate.forEach(view -> { view.setLayerType(View.LAYER_TYPE_HARDWARE, null); @@ -540,7 +544,8 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener 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); @@ -613,28 +618,9 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener 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); @@ -643,10 +629,10 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener 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(); }; } @@ -654,10 +640,14 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener * @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 */); @@ -669,7 +659,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener SurfaceTransactionApplier surfaceApplier = new SurfaceTransactionApplier(floatingView); openingTargets.addReleaseCheck(surfaceApplier); - RemoteAnimationTargetCompat navBarTarget = openingTargets.getNavBarRemoteAnimationTarget(); + RemoteAnimationTarget navBarTarget = openingTargets.getNavBarRemoteAnimationTarget(); int[] dragLayerBounds = new int[2]; mDragLayer.getLocationOnScreen(dragLayerBounds); @@ -821,10 +811,11 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener 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); @@ -845,14 +836,13 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener 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); @@ -869,29 +859,26 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener 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); @@ -900,7 +887,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener // 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()); @@ -909,17 +896,19 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener } 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) @@ -943,7 +932,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener 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); @@ -1007,43 +996,39 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener 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()); @@ -1057,54 +1042,37 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener 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)) - .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; - } + boolean allowBlurringLauncher = mLauncher.getStateManager().getState() != OVERVIEW + && BlurUtils.supportsBlursOnWindows(); - depthController.setSurface(dimLayer); - backgroundRadiusAnim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationStart(Animator animation) { - depthController.setIsInLaunchTransition(true); - } + MyDepthController depthController = new MyDepthController(mLauncher); + ObjectAnimator backgroundRadiusAnim = ObjectAnimator.ofFloat(depthController.stateDepth, + MULTI_PROPERTY_VALUE, BACKGROUND_APP.getDepth(mLauncher)) + .setDuration(APP_LAUNCH_DURATION); - @Override - public void onAnimationEnd(Animator animation) { - depthController.setIsInLaunchTransition(false); - depthController.setSurface(null); - if (dimLayer != null) { - new SurfaceControl.Transaction() - .remove(dimLayer) - .apply(); - } - } - }); + if (allowBlurringLauncher) { + // 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(); + + backgroundRadiusAnim.addListener(AnimatorListeners.forEndCallback(() -> + new SurfaceControl.Transaction().remove(dimLayer).apply())); } + return backgroundRadiusAnim; } @@ -1118,28 +1086,26 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener 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); } } @@ -1152,11 +1118,25 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener } 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); @@ -1175,7 +1155,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener 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 @@ -1202,8 +1182,8 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener } } - 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. @@ -1215,9 +1195,9 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener 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++; } @@ -1239,8 +1219,8 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener /** * 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); @@ -1249,24 +1229,23 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener 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; } @@ -1277,8 +1256,8 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener /** * 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) { @@ -1292,7 +1271,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener /** * 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; } @@ -1353,15 +1332,15 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener * 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; @@ -1446,7 +1425,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener animation.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { - anim.start(mLauncher, velocityPxPerS); + anim.start(mLauncher, mDeviceProfile, velocityPxPerS); } }); return anim; @@ -1455,7 +1434,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener /** * 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(); @@ -1476,10 +1455,10 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener @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); @@ -1501,20 +1480,19 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener 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); } }); @@ -1578,8 +1556,8 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener * the transition. */ public Pair<RectFSpringAnim, AnimatorSet> createWallpaperOpenAnimations( - RemoteAnimationTargetCompat[] appTargets, - RemoteAnimationTargetCompat[] wallpaperTargets, + RemoteAnimationTarget[] appTargets, + RemoteAnimationTarget[] wallpaperTargets, boolean fromUnlock, RectF startRect, float startWindowCornerRadius) { @@ -1621,7 +1599,8 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener 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))); } @@ -1687,9 +1666,9 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener @Override public void onCreateAnimation(int transit, - RemoteAnimationTargetCompat[] appTargets, - RemoteAnimationTargetCompat[] wallpaperTargets, - RemoteAnimationTargetCompat[] nonAppTargets, + RemoteAnimationTarget[] appTargets, + RemoteAnimationTarget[] wallpaperTargets, + RemoteAnimationTarget[] nonAppTargets, LauncherAnimationRunner.AnimationResult result) { if (mLauncher.isDestroyed()) { AnimatorSet anim = new AnimatorSet(); @@ -1703,9 +1682,10 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener 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), + appTargets, wallpaperTargets, mFromUnlock, windowTargetBounds, QuickStepContract.getWindowCornerRadius(mLauncher)); mLauncher.clearForceInvisibleFlag(INVISIBLE_ALL); @@ -1728,9 +1708,9 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener @Override public void onCreateAnimation(int transit, - RemoteAnimationTargetCompat[] appTargets, - RemoteAnimationTargetCompat[] wallpaperTargets, - RemoteAnimationTargetCompat[] nonAppTargets, + RemoteAnimationTarget[] appTargets, + RemoteAnimationTarget[] wallpaperTargets, + RemoteAnimationTarget[] nonAppTargets, LauncherAnimationRunner.AnimationResult result) { AnimatorSet anim = new AnimatorSet(); boolean launcherClosing = @@ -1741,7 +1721,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener 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; @@ -1857,7 +1837,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener * 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(); @@ -1868,7 +1848,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener private final Rect mTmpRect = new Rect(); - SpringAnimRunner(RemoteAnimationTargetCompat[] appTargets, RectF targetRect, + SpringAnimRunner(RemoteAnimationTarget[] appTargets, RectF targetRect, Rect windowTargetBounds, float startWindowCornerRadius) { mAppTargets = appTargets; mStartRadius = startWindowCornerRadius; @@ -1883,10 +1863,10 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener @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); @@ -1921,18 +1901,17 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener 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) { @@ -1949,4 +1928,17 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener 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/AppsDividerView.java b/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java index f42b39fb2d..e8374b813c 100644 --- a/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java +++ b/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java @@ -35,6 +35,7 @@ import androidx.core.content.ContextCompat; import com.android.launcher3.R; import com.android.launcher3.allapps.FloatingHeaderRow; import com.android.launcher3.allapps.FloatingHeaderView; +import com.android.launcher3.util.OnboardingPrefs; import com.android.launcher3.util.Themes; import com.android.launcher3.views.ActivityContext; @@ -92,8 +93,10 @@ public class AppsDividerView extends View implements FloatingHeaderRow { ? R.color.all_apps_label_text_dark : R.color.all_apps_label_text); - mShowAllAppsLabel = !ActivityContext.lookupContext( - getContext()).getOnboardingPrefs().hasReachedMaxCount(ALL_APPS_VISITED_COUNT); + OnboardingPrefs<?> onboardingPrefs = ActivityContext.lookupContext( + getContext()).getOnboardingPrefs(); + mShowAllAppsLabel = onboardingPrefs == null || !onboardingPrefs.hasReachedMaxCount( + ALL_APPS_VISITED_COUNT); } public void setup(FloatingHeaderView parent, FloatingHeaderRow[] rows, boolean tabsHidden) { @@ -216,8 +219,8 @@ public class AppsDividerView extends View implements FloatingHeaderRow { CharSequence allAppsLabelText = getResources().getText(R.string.all_apps_label); mAllAppsLabelLayout = StaticLayout.Builder.obtain( - allAppsLabelText, 0, allAppsLabelText.length(), mPaint, - Math.round(mPaint.measureText(allAppsLabelText.toString()))) + allAppsLabelText, 0, allAppsLabelText.length(), mPaint, + Math.round(mPaint.measureText(allAppsLabelText.toString()))) .setAlignment(Layout.Alignment.ALIGN_CENTER) .setMaxLines(1) .setIncludePad(true) diff --git a/quickstep/src/com/android/launcher3/appprediction/InstantAppItemInfo.java b/quickstep/src/com/android/launcher3/appprediction/InstantAppItemInfo.java index 9c3b8816cb..8baee004cc 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.ComponentName; 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 @@ public class InstantAppItemInfo extends AppInfo { 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 351a3bc164..4fbe8cfd26 100644 --- a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java +++ b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java @@ -30,9 +30,9 @@ import androidx.annotation.Nullable; 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.List; 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 @@ public class PredictionRowView<T extends Context & ActivityContext & DeviceProfi @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 d63bc18a33..048243e345 100644 --- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java +++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java @@ -26,22 +26,17 @@ import android.view.View; 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 @@ public class HotseatEduController { */ 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 119ae907f7..ba412c9909 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,9 +38,8 @@ import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.Launcher; import com.android.launcher3.R; import com.android.launcher3.anim.Interpolators; -import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.celllayout.CellLayoutLayoutParams; import com.android.launcher3.model.data.WorkspaceItemInfo; -import com.android.launcher3.uioverrides.ApiWrapper; import com.android.launcher3.uioverrides.PredictedAppIcon; import com.android.launcher3.views.AbstractSlideInView; @@ -107,19 +105,12 @@ public class HotseatEduDialog extends AbstractSlideInView<Launcher> implements I mDismissBtn.setOnClickListener(this::onDismiss); LinearLayout buttonContainer = findViewById(R.id.button_container); - int adjustedMarginEnd = ApiWrapper.getHotseatEndOffset(context) - - buttonContainer.getPaddingEnd(); + int adjustedMarginEnd = grid.hotseatBarEndOffset - buttonContainer.getPaddingEnd(); if (InvariantDeviceProfile.INSTANCE.get(context) .getDeviceProfile(context).isTaskbarPresent && adjustedMarginEnd > 0) { ((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) { @@ -202,7 +193,7 @@ public class HotseatEduDialog extends AbstractSlideInView<Launcher> implements I 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 05b81671c3..0a2a9b37ba 100644 --- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java +++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java @@ -305,7 +305,7 @@ public class HotseatPredictionController implements DragController.DragListener, * 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 7e3ee7dada..e504141024 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.LauncherApps; 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 @@ public class PredictionUpdateTask 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) { 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 @@ public class PredictionUpdateTask extends BaseModelUpdateTask { .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 @@ public class PredictionUpdateTask extends BaseModelUpdateTask { } } - 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 770dfb2e07..118cfc6620 100644 --- a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java +++ b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java @@ -18,12 +18,12 @@ package com.android.launcher3.model; 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 @@ public class QuickstepModelDelegate extends ModelDelegate { // 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 @@ public class QuickstepModelDelegate extends ModelDelegate { 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 @@ public class QuickstepModelDelegate extends ModelDelegate { 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 @@ public class QuickstepModelDelegate extends ModelDelegate { 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 68ed682792..fb2d0dcc3c 100644 --- a/quickstep/src/com/android/launcher3/model/WellbeingModel.java +++ b/quickstep/src/com/android/launcher3/model/WellbeingModel.java @@ -18,8 +18,6 @@ package com.android.launcher3.model; import static android.content.ContentResolver.SCHEME_CONTENT; -import static com.android.launcher3.Utilities.newContentObserver; - import android.annotation.TargetApi; import android.app.RemoteAction; import android.content.ContentProviderClient; diff --git a/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java b/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java index 9cd9d8597c..6160378767 100644 --- a/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java +++ b/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java @@ -18,20 +18,23 @@ package com.android.launcher3.model; 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 @@ public final class WidgetsPredictionUpdateTask extends BaseModelUpdateTask { * 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 4e59790771..9554bd3d9c 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 @@ public interface QuickstepSystemShortcut { 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.findLastActiveTaskAndDoSplitOperation( + 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 interface QuickstepSystemShortcut { 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/secondarydisplay/SecondaryDisplayPredictionsImpl.java b/quickstep/src/com/android/launcher3/secondarydisplay/SecondaryDisplayPredictionsImpl.java new file mode 100644 index 0000000000..8720bd8f26 --- /dev/null +++ b/quickstep/src/com/android/launcher3/secondarydisplay/SecondaryDisplayPredictionsImpl.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.secondarydisplay; + +import static com.android.launcher3.util.OnboardingPrefs.ALL_APPS_VISITED_COUNT; + +import android.content.Context; +import android.view.View; + +import com.android.launcher3.allapps.ActivityAllAppsContainerView; +import com.android.launcher3.appprediction.AppsDividerView; +import com.android.launcher3.appprediction.PredictionRowView; +import com.android.launcher3.model.BgDataModel; +import com.android.launcher3.util.OnboardingPrefs; +import com.android.launcher3.views.ActivityContext; + +/** + * Implementation of SecondaryDisplayPredictions. + */ +@SuppressWarnings("unused") +public final class SecondaryDisplayPredictionsImpl extends SecondaryDisplayPredictions { + private final ActivityContext mActivityContext; + + public SecondaryDisplayPredictionsImpl(Context context) { + mActivityContext = ActivityContext.lookupContext(context); + } + + @Override + void updateAppDivider() { + OnboardingPrefs<?> onboardingPrefs = mActivityContext.getOnboardingPrefs(); + if (onboardingPrefs != null) { + mActivityContext.getAppsView().getFloatingHeaderView() + .findFixedRowByType(AppsDividerView.class) + .setShowAllAppsLabel( + !onboardingPrefs.hasReachedMaxCount(ALL_APPS_VISITED_COUNT)); + onboardingPrefs.incrementEventCount(ALL_APPS_VISITED_COUNT); + } + } + + @Override + public void setPredictedApps(BgDataModel.FixedContainerItems item) { + mActivityContext.getAppsView().getFloatingHeaderView() + .findFixedRowByType(PredictionRowView.class) + .setPredictedApps(item.items); + } + + @Override + public void setLongClickListener(ActivityAllAppsContainerView<?> appsView, + View.OnLongClickListener onIconLongClickListener) { + appsView.getFloatingHeaderView() + .findFixedRowByType(PredictionRowView.class) + .setOnIconLongClickListener(onIconLongClickListener); + } +} diff --git a/quickstep/src/com/android/launcher3/statehandlers/BackButtonAlphaHandler.java b/quickstep/src/com/android/launcher3/statehandlers/BackButtonAlphaHandler.java deleted file mode 100644 index 07d3a51603..0000000000 --- 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 eda08239d6..867e168dd2 100644 --- a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java +++ b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java @@ -19,17 +19,12 @@ package com.android.launcher3.statehandlers; 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 android.view.ViewTreeObserver; 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 @@ import java.util.function.Consumer; /** * 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); - } - - @Override - public Float get(DepthController depthController) { - return depthController.mDepth; - } - }; - - /** - * 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 ViewTreeObserver.OnDrawListener mOnDrawListener = this::onLauncherDraw; - 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 Consumer<Boolean> mCrossWindowBlurListener = this::setCrossWindowBlursEnabled; - 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 ensureDependencies() { - if (mWallpaperManager == null) { - mMaxBlurRadius = mLauncher.getResources().getInteger(R.integer.max_depth_blur_radius); - mWallpaperManager = mLauncher.getSystemService(WallpaperManager.class); - } + 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 (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 @@ public class DepthController implements StateHandler<LauncherState>, 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 @@ public class DepthController implements StateHandler<LauncherState>, } } - /** - * 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 @@ public class DepthController implements StateHandler<LauncherState>, } float toDepth = toState.getDepth(mLauncher); - if (Float.compare(mDepth, toDepth) != 0) { - animation.setFloat(this, DEPTH, 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); + animation.setFloat(stateDepth, MULTI_PROPERTY_VALUE, toDepth, + config.getInterpolator(ANIM_DEPTH, LINEAR)); } - 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 @@ public class DepthController implements StateHandler<LauncherState>, 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 0000000000..bbc0627517 --- /dev/null +++ b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.statehandlers; + +import android.os.SystemProperties; +import android.view.View; + +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherState; +import com.android.launcher3.statemanager.StatefulActivity; +import com.android.launcher3.uioverrides.QuickstepLauncher; + +/** + * Controls the visibility of the workspace and the resumed / paused state when desktop mode + * is enabled. + */ +public class DesktopVisibilityController { + + private final Launcher mLauncher; + + private boolean mFreeformTasksVisible; + private boolean mInOverviewState; + + public DesktopVisibilityController(Launcher launcher) { + mLauncher = launcher; + } + + /** + * Whether desktop mode is supported. + */ + private boolean isDesktopModeSupported() { + return SystemProperties.getBoolean("persist.wm.debug.desktop_mode", false) + || 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 (freeformTasksVisible != mFreeformTasksVisible) { + mFreeformTasksVisible = freeformTasksVisible; + updateLauncherVisibility(); + } + } + + /** + * Sets whether the overview is visible and updates launcher visibility based on that. + */ + public void setOverviewStateEnabled(boolean overviewStateEnabled) { + if (overviewStateEnabled != mInOverviewState) { + mInOverviewState = overviewStateEnabled; + updateLauncherVisibility(); + } + } + + /** + * Updates launcher visibility and state to look like it is paused or resumed depending on + * whether freeform windows are showing in desktop mode. + */ + private void updateLauncherVisibility() { + StatefulActivity<LauncherState> activity = + QuickstepLauncher.ACTIVITY_TRACKER.getCreatedActivity(); + View workspaceView = mLauncher.getWorkspace(); + if (activity == null || workspaceView == null || !isDesktopModeSupported()) { + return; + } + + if (mFreeformTasksVisible) { + workspaceView.setVisibility(View.INVISIBLE); + if (!mInOverviewState) { + // When freeform is visible & we're not in overview, we want launcher to appear + // paused, this ensures that taskbar displays. + activity.setPaused(); + } + } else { + workspaceView.setVisibility(View.VISIBLE); + // If freeform isn't visible ensure that launcher appears resumed to behave normally. + // 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.isResumed()) { + activity.setResumed(); + } + } + } +} diff --git a/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java b/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java index b4052e3d71..82d18307ca 100644 --- a/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java @@ -19,18 +19,16 @@ import android.content.Context; import android.view.ContextThemeWrapper; import android.view.LayoutInflater; -import com.android.launcher3.DeviceProfile.DeviceProfileListenable; import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener; 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<>(); diff --git a/quickstep/src/com/android/launcher3/taskbar/DesktopNavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/DesktopNavbarButtonsViewController.java index 0ab3cfd547..48481d88b9 100644 --- a/quickstep/src/com/android/launcher3/taskbar/DesktopNavbarButtonsViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/DesktopNavbarButtonsViewController.java @@ -18,6 +18,8 @@ package com.android.launcher3.taskbar; 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 @@ public class DesktopNavbarButtonsViewController extends NavbarButtonsViewControl 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 @@ public class DesktopNavbarButtonsViewController extends NavbarButtonsViewControl 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 @@ public class DesktopNavbarButtonsViewController extends NavbarButtonsViewControl 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/DesktopTaskbarRecentAppsController.java b/quickstep/src/com/android/launcher3/taskbar/DesktopTaskbarRecentAppsController.java new file mode 100644 index 0000000000..acfbea38b7 --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/DesktopTaskbarRecentAppsController.java @@ -0,0 +1,153 @@ +/* + * 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 android.app.ActivityManager; +import android.content.ComponentName; +import android.util.SparseArray; + +import com.android.launcher3.model.data.AppInfo; +import com.android.launcher3.model.data.ItemInfo; +import com.android.launcher3.model.data.WorkspaceItemInfo; +import com.android.quickstep.RecentsModel; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +/** + * Provides recent apps functionality specifically in a desktop environment. + */ +public class DesktopTaskbarRecentAppsController extends TaskbarRecentAppsController { + + private final TaskbarActivityContext mContext; + private ArrayList<ItemInfo> mRunningApps = new ArrayList<>(); + private AppInfo[] mApps; + + public DesktopTaskbarRecentAppsController(TaskbarActivityContext context) { + mContext = context; + } + + @Override + protected void onDestroy() { + super.onDestroy(); + mApps = null; + } + + @Override + protected void setApps(AppInfo[] apps) { + mApps = apps; + } + + @Override + protected boolean isEnabled() { + return true; + } + + /** + * Set mRunningApps to hold currently running applications using the list of currently running + * tasks. Filtering is also done to ignore applications that are already on the taskbar in the + * original hotseat. + */ + @Override + protected void updateRunningApps(SparseArray<ItemInfo> hotseatItems) { + ArrayList<AppInfo> runningApps = getRunningAppsFromTasks(); + ArrayList<ItemInfo> filteredRunningApps = new ArrayList<>(); + for (AppInfo runningApp : runningApps) { + boolean shouldAddOnTaskbar = true; + for (int i = 0; i < hotseatItems.size(); i++) { + if (hotseatItems.keyAt(i) >= mControllers.taskbarActivityContext.getDeviceProfile() + .numShownHotseatIcons) { + break; + } + if (hotseatItems.valueAt(i).getTargetPackage() + .equals(runningApp.getTargetPackage())) { + shouldAddOnTaskbar = false; + break; + } + } + if (shouldAddOnTaskbar) { + filteredRunningApps.add(new WorkspaceItemInfo(runningApp)); + } + } + mRunningApps = filteredRunningApps; + mControllers.taskbarViewController.commitRunningAppsToUI(); + } + + /** + * Returns a copy of hotseatItems with the addition of currently running applications. + */ + @Override + protected ItemInfo[] updateHotseatItemInfos(ItemInfo[] hotseatItemInfos) { + // hotseatItemInfos.length would be 0 if deviceProfile.numShownHotseatIcons is 0, so we + // don't want to show anything in the hotseat + if (hotseatItemInfos.length == 0) return hotseatItemInfos; + + int runningAppsIndex = 0; + ItemInfo[] newHotseatItemsInfo = Arrays.copyOf( + hotseatItemInfos, hotseatItemInfos.length + mRunningApps.size()); + for (int i = hotseatItemInfos.length; i < newHotseatItemsInfo.length; i++) { + newHotseatItemsInfo[i] = mRunningApps.get(runningAppsIndex); + runningAppsIndex++; + } + return newHotseatItemsInfo; + } + + + /** + * Returns a list of running applications from the list of currently running tasks. + */ + private ArrayList<AppInfo> getRunningAppsFromTasks() { + ArrayList<ActivityManager.RunningTaskInfo> tasks = + RecentsModel.INSTANCE.get(mContext).getRunningTasks(); + ArrayList<AppInfo> runningApps = new ArrayList<>(); + // early return if apps is empty, since we would have no AppInfo to compare + if (mApps == null) { + return runningApps; + } + + Set<String> seenPackages = new HashSet<>(); + for (ActivityManager.RunningTaskInfo taskInfo : tasks) { + if (taskInfo.realActivity == null) continue; + + // If a different task for the same package has already been handled, skip this one + String taskPackage = taskInfo.realActivity.getPackageName(); + if (seenPackages.contains(taskPackage)) continue; + + // Otherwise, get the corresponding AppInfo and add it to the list + seenPackages.add(taskPackage); + AppInfo app = getAppInfo(taskInfo.realActivity); + if (app == null) continue; + runningApps.add(app); + } + return runningApps; + } + + /** + * Retrieves the corresponding AppInfo for the activity. + */ + private AppInfo getAppInfo(ComponentName activity) { + String packageName = activity.getPackageName(); + for (AppInfo app : mApps) { + if (!packageName.equals(app.getTargetPackage())) { + continue; + } + return app; + } + return null; + } +} diff --git a/quickstep/src/com/android/launcher3/taskbar/DesktopTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/DesktopTaskbarUIController.java index e2359c0d5e..9393b0f7ab 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,24 +24,27 @@ import com.android.launcher3.BaseQuickstepLauncher; */ public class DesktopTaskbarUIController extends TaskbarUIController { - private final BaseQuickstepLauncher mLauncher; + private final QuickstepLauncher mLauncher; - public DesktopTaskbarUIController(BaseQuickstepLauncher launcher) { + public DesktopTaskbarUIController(QuickstepLauncher launcher) { mLauncher = launcher; } @Override protected void init(TaskbarControllers taskbarControllers) { + super.init(taskbarControllers); mLauncher.getHotseat().setIconsAlpha(0f); + mControllers.taskbarViewController.updateRunningApps(); } @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 f1e67479f5..df867cb831 100644 --- a/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java +++ b/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java @@ -21,6 +21,7 @@ import static com.android.launcher3.taskbar.TaskbarStashController.TASKBAR_STASH 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 @@ public class FallbackTaskbarUIController extends TaskbarUIController { animateToRecentsState(toState); // Handle tapping on live tile. - RecentsView recentsView = mRecentsActivity.getOverviewPanel(); - recentsView.setTaskLaunchListener(toState == RecentsState.DEFAULT + getRecentsView().setTaskLaunchListener(toState == RecentsState.DEFAULT ? (() -> animateToRecentsState(RecentsState.BACKGROUND_APP)) : null); } }; @@ -70,12 +70,14 @@ public class FallbackTaskbarUIController extends TaskbarUIController { * Currently this animation just force stashes the taskbar in Overview. */ public Animator createAnimToRecentsState(RecentsState toState, long duration) { - boolean forceStashed = toState.hasOverviewActions(); + boolean useStashedLauncherState = toState.hasOverviewActions(); + boolean stashedLauncherState = + useStashedLauncherState && !FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get(); TaskbarStashController controller = mControllers.taskbarStashController; // Set both FLAG_IN_STASHED_LAUNCHER_STATE and FLAG_IN_APP to ensure the state is respected. // For all other states, just use the current stashed-in-app setting (e.g. if long clicked). - controller.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE, forceStashed); - controller.updateStateForFlag(FLAG_IN_APP, !forceStashed); + controller.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE, stashedLauncherState); + controller.updateStateForFlag(FLAG_IN_APP, !useStashedLauncherState); return controller.applyStateWithoutStart(duration); } @@ -85,4 +87,9 @@ public class FallbackTaskbarUIController extends TaskbarUIController { 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 ca30e72609..a18aabebab 100644 --- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java +++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java @@ -15,8 +15,11 @@ */ package com.android.launcher3.taskbar; +import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR; + +import static com.android.launcher3.QuickstepTransitionManager.TRANSIENT_TASKBAR_TRANSITION_DURATION; import static com.android.launcher3.taskbar.TaskbarLauncherStateController.FLAG_RESUMED; -import static com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_EXTRA_NAVIGATION_BAR; +import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS; import android.animation.Animator; import android.animation.AnimatorSet; @@ -24,30 +27,29 @@ 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.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; @@ -65,7 +67,7 @@ public class LauncherTaskbarUIController extends TaskbarUIController { private final SparseArray<Float> mTaskbarInAppDisplayProgress = new SparseArray<>(4); - private final BaseQuickstepLauncher mLauncher; + private final QuickstepLauncher mLauncher; private final DeviceProfile.OnDeviceProfileChangeListener mOnDeviceProfileChangeListener = dp -> { @@ -81,7 +83,7 @@ public class LauncherTaskbarUIController extends TaskbarUIController { private final TaskbarLauncherStateController mTaskbarLauncherStateController = new TaskbarLauncherStateController(); - public LauncherTaskbarUIController(BaseQuickstepLauncher launcher) { + public LauncherTaskbarUIController(QuickstepLauncher launcher) { mLauncher = launcher; } @@ -115,7 +117,8 @@ public class LauncherTaskbarUIController extends TaskbarUIController { @Override protected boolean isTaskbarTouchable() { - return !mTaskbarLauncherStateController.isAnimatingToLauncher(); + return !(mTaskbarLauncherStateController.isAnimatingToLauncher() + && mTaskbarLauncherStateController.goingToAlignedLauncherState()); } public void setShouldDelayLauncherStateAnim(boolean shouldDelayLauncherStateAnim) { @@ -124,24 +127,6 @@ public class LauncherTaskbarUIController extends TaskbarUIController { } /** - * 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,7 +156,11 @@ public class LauncherTaskbarUIController extends TaskbarUIController { isResumed, fromInit, /* startAnimation= */ true, - QuickstepTransitionManager.CONTENT_ALPHA_DURATION); + DisplayController.isTransientTaskbar(mLauncher) + ? TRANSIENT_TASKBAR_TRANSITION_DURATION + : (!isResumed + ? QuickstepTransitionManager.TASKBAR_TO_APP_DURATION + : QuickstepTransitionManager.TASKBAR_TO_HOME_DURATION)); } @Nullable @@ -186,6 +175,13 @@ public class LauncherTaskbarUIController extends TaskbarUIController { } } + if (ENABLE_SHELL_TRANSITIONS && isResumed + && !mLauncher.getStateManager().getState().isTaskbarAlignedWithHotseat(mLauncher)) { + // Launcher is resumed, but in a state where taskbar is still independent, so + // ignore the state change. + return null; + } + mTaskbarLauncherStateController.updateStateForFlag(FLAG_RESUMED, isResumed); return mTaskbarLauncherStateController.applyState(fromInit ? 0 : duration, startAnimation); } @@ -198,16 +194,15 @@ public class LauncherTaskbarUIController extends TaskbarUIController { */ public Animator createAnimToLauncher(@NonNull LauncherState toState, @NonNull RecentsAnimationCallbacks callbacks, long duration) { - return mTaskbarLauncherStateController.createAnimToLauncher(toState, callbacks, duration); - } - - /** - * @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); + AnimatorSet set = new AnimatorSet(); + Animator taskbarState = mTaskbarLauncherStateController + .createAnimToLauncher(toState, callbacks, duration); + long halfDuration = Math.round(duration * 0.5f); + Animator translation = + mControllers.taskbarTranslationController.createAnimToLauncher(halfDuration); + + set.playTogether(taskbarState, translation); + return set; } public boolean isDraggingItem() { @@ -233,7 +228,9 @@ public class LauncherTaskbarUIController extends TaskbarUIController { } 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, @@ -257,11 +254,6 @@ public class LauncherTaskbarUIController extends TaskbarUIController { 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. */ @@ -282,13 +274,6 @@ public class LauncherTaskbarUIController extends TaskbarUIController { && !mLauncher.getOnboardingPrefs().getBoolean(OnboardingPrefs.TASKBAR_EDU_SEEN); } - /** - * Manually ends the taskbar education flow. - */ - public void hideEdu() { - mControllers.taskbarEduController.hideEdu(); - } - @Override public void onTaskbarIconLaunched(ItemInfo item) { InstanceId instanceId = new InstanceIdSequence().newInstanceId(); @@ -299,9 +284,16 @@ public class LauncherTaskbarUIController extends TaskbarUIController { @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); + } } /** @@ -318,7 +310,7 @@ public class LauncherTaskbarUIController extends TaskbarUIController { 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. @@ -352,6 +344,24 @@ public class LauncherTaskbarUIController extends TaskbarUIController { } @Override + public void onExpandPip() { + super.onExpandPip(); + mTaskbarLauncherStateController.updateStateForFlag(FLAG_RESUMED, false); + mTaskbarLauncherStateController.applyState(); + } + + @Override + public boolean isIconAlignedWithHotseat() { + return mTaskbarLauncherStateController.isIconAlignedWithHotseat(); + } + + @Override + public boolean isHotseatIconOnTopWhenAligned() { + return mTaskbarLauncherStateController.isInHotseatOnTopStates() + && getInAppDisplayProgress(MINUS_ONE_PAGE_PROGRESS_INDEX) == 0; + } + + @Override public void dumpLogs(String prefix, PrintWriter pw) { super.dumpLogs(prefix, pw); @@ -384,4 +394,9 @@ public class LauncherTaskbarUIController extends TaskbarUIController { 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 5d576f7bf5..cbee58bc2f 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; @@ -35,7 +42,7 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_N 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.ViewTreeObserverWrapper.InsetsInfo.TOUCHABLE_INSETS_REGION; +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING; import android.animation.ArgbEvaluator; import android.animation.ObjectAnimator; @@ -45,7 +52,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; @@ -61,25 +70,30 @@ import android.view.View.OnAttachStateChangeListener; 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; import android.widget.LinearLayout; +import com.android.launcher3.DeviceProfile; import com.android.launcher3.LauncherAnimUtils; 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; @@ -93,7 +107,7 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT private final Rect mTempRect = new Rect(); - private static final int FLAG_SWITCHER_SUPPORTED = 1 << 0; + private static final int FLAG_SWITCHER_SHOWING = 1 << 0; private static final int FLAG_IME_VISIBLE = 1 << 1; private static final int FLAG_ROTATION_BUTTON_VISIBLE = 1 << 2; private static final int FLAG_A11Y_VISIBLE = 1 << 3; @@ -105,14 +119,20 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT private static final int FLAG_DISABLE_BACK = 1 << 9; 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; - private static final int MASK_IME_SWITCHER_VISIBLE = FLAG_SWITCHER_SUPPORTED | FLAG_IME_VISIBLE; + /** 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<>(); @@ -120,12 +140,14 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT 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); @@ -140,6 +162,9 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT 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(); @@ -158,9 +183,10 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT // 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; @@ -171,6 +197,8 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT 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; } /** @@ -178,9 +206,15 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT */ 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) { @@ -189,16 +223,21 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT isThreeButtonNav ? mStartContextualContainer : mEndContextualContainer, mControllers.navButtonController, R.id.ime_switcher); mPropertyHolders.add(new StatePropertyHolder(imeSwitcherButton, - flags -> ((flags & MASK_IME_SWITCHER_VISIBLE) == MASK_IME_SWITCHER_VISIBLE) + flags -> ((flags & FLAG_SWITCHER_SHOWING) != 0) && ((flags & FLAG_ROTATION_BUTTON_VISIBLE) == 0))); } 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)); @@ -207,9 +246,12 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT boolean isInKidsMode = mContext.isNavBarKidsModeActive(); boolean alwaysShowButtons = isThreeButtonNav || isInSetup; - // Make sure to remove nav bar buttons translation when notification shade is expanded or - // IME is showing (add separate translation for IME). - int flagsToRemoveTranslation = FLAG_NOTIFICATION_SHADE_EXPANDED | FLAG_IME_VISIBLE; + // Make sure to remove nav bar buttons translation when any of the following occur: + // - Notification shade is expanded + // - IME is showing (add separate translation for IME) + // - VoiceInteractionWindow (assistant) is showing + int flagsToRemoveTranslation = FLAG_NOTIFICATION_SHADE_EXPANDED | FLAG_IME_VISIBLE + | FLAG_VOICE_INTERACTION_WINDOW_SHOWING; mPropertyHolders.add(new StatePropertyHolder(mNavButtonInAppDisplayProgressForSysui, flags -> (flags & flagsToRemoveTranslation) != 0, AnimatedFloat.VALUE, 1, 0)); @@ -222,87 +264,16 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT 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); - - 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); - } 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); - } + updateButtonLayoutSpacing(); + updateStateForFlag(FLAG_SMALL_SCREEN, isPhoneButtonNavMode(mContext)); // Animate taskbar background when either.. // notification shade expanded AND not on keyguard @@ -336,7 +307,7 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT 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)); @@ -371,7 +342,7 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT 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 @@ -399,26 +370,26 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT 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 -> { - navButtonController.onButtonClick(BUTTON_RECENTS); + 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())); @@ -443,22 +414,26 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT | SYSUI_STATE_QUICK_SETTINGS_EXPANDED; boolean isNotificationShadeExpanded = (sysUiStateFlags & shadeExpandedFlags) != 0; boolean isScreenPinningActive = (sysUiStateFlags & SYSUI_STATE_SCREEN_PINNING) != 0; + boolean isVoiceInteractionWindowShowing = + (sysUiStateFlags & SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING) != 0; // TODO(b/202218289) we're getting IME as not visible on lockscreen from system updateStateForFlag(FLAG_IME_VISIBLE, isImeVisible); - updateStateForFlag(FLAG_SWITCHER_SUPPORTED, isImeSwitcherShowing); + updateStateForFlag(FLAG_SWITCHER_SHOWING, isImeSwitcherShowing); updateStateForFlag(FLAG_A11Y_VISIBLE, a11yVisible); updateStateForFlag(FLAG_DISABLE_HOME, isHomeDisabled); updateStateForFlag(FLAG_DISABLE_RECENTS, isRecentsDisabled); updateStateForFlag(FLAG_DISABLE_BACK, isBackDisabled); updateStateForFlag(FLAG_NOTIFICATION_SHADE_EXPANDED, isNotificationShadeExpanded); updateStateForFlag(FLAG_SCREEN_PINNING_ACTIVE, isScreenPinningActive); + updateStateForFlag(FLAG_VOICE_INTERACTION_WINDOW_SHOWING, isVoiceInteractionWindowShowing); if (mA11yButton != null) { // Only used in 3 button boolean a11yLongClickable = (sysUiStateFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0; mA11yButton.setLongClickable(a11yLongClickable); + updateButtonLayoutSpacing(); } } @@ -474,6 +449,13 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT } /** + * @return {@code true} if A11y is showing in 3 button nav taskbar + */ + private boolean isContextualButtonShowing() { + return mContext.isThreeButtonNav() && (mState & FLAG_A11Y_VISIBLE) != 0; + } + + /** * Should be called when we need to show back button for bouncer */ public void setBackForBouncer(boolean isBouncerVisible) { @@ -491,6 +473,12 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT 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 */ @@ -499,6 +487,13 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT } /** + * Returns true if IME switcher is visible + */ + public boolean isImeSwitcherVisible() { + return (mState & FLAG_SWITCHER_SHOWING) != 0; + } + + /** * Returns true if the home button is disabled */ public boolean isHomeDisabled() { @@ -543,6 +538,26 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT 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; @@ -590,6 +605,9 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT } private void updateNavButtonTranslationY() { + if (isPhoneButtonNavMode(mContext)) { + return; + } final float normalTranslationY = mTaskbarNavButtonTranslationY.value; final float imeAdjustmentTranslationY = mTaskbarNavButtonTranslationYForIme.value; TaskbarUIController uiController = mControllers.uiController; @@ -606,8 +624,11 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT 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)); } @@ -626,9 +647,9 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT buttonView.setImageResource(drawableId); buttonView.setContentDescription(parent.getContext().getString( navButtonController.getButtonContentDescription(buttonType))); - buttonView.setOnClickListener(view -> navButtonController.onButtonClick(buttonType)); + buttonView.setOnClickListener(view -> navButtonController.onButtonClick(buttonType, view)); buttonView.setOnLongClickListener(view -> - navButtonController.onButtonLongClick(buttonType)); + navButtonController.onButtonLongClick(buttonType, view)); return buttonView; } @@ -649,6 +670,167 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT if (mFloatingRotationButton != null) { mFloatingRotationButton.onConfigurationChanged(configChanges); } + if (!mContext.isUserSetupComplete()) { + handleSetupUi(); + } + updateButtonLayoutSpacing(); + } + + 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() { + 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(); + + 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; + } + + if (isInSetup) { + handleSetupUi(); + + // Hide back button in SUW if keyboard is showing (IME draws its own back). + mPropertyHolders.add(new StatePropertyHolder( + mBackButtonAlpha.get(ALPHA_INDEX_SUW), + flags -> (flags & FLAG_IME_VISIBLE) == 0)); + + // TODO(b/210906568) Dark intensity is currently not propagated during setup, so set + // it based on dark theme for now. + int mode = res.getConfiguration().uiMode + & Configuration.UI_MODE_NIGHT_MASK; + boolean isDarkTheme = mode == Configuration.UI_MODE_NIGHT_YES; + mTaskbarNavButtonDarkIntensity.updateValue(isDarkTheme ? 0 : 1); + } else if (isInKidsMode) { + int iconSize = res.getDimensionPixelSize( + R.dimen.taskbar_icon_size_kids); + int buttonWidth = res.getDimensionPixelSize( + R.dimen.taskbar_nav_buttons_width_kids); + int buttonHeight = res.getDimensionPixelSize( + R.dimen.taskbar_nav_buttons_height_kids); + int buttonRadius = res.getDimensionPixelSize( + R.dimen.taskbar_nav_buttons_corner_radius_kids); + int paddingleft = (buttonWidth - iconSize) / 2; + int paddingRight = paddingleft; + int paddingTop = (buttonHeight - iconSize) / 2; + int paddingBottom = paddingTop; + + // Update icons + ((ImageView) mBackButton).setImageDrawable( + mBackButton.getContext().getDrawable(R.drawable.ic_sysbar_back_kids)); + ((ImageView) mBackButton).setScaleType(ImageView.ScaleType.FIT_CENTER); + mBackButton.setPadding(paddingleft, paddingTop, paddingRight, paddingBottom); + ((ImageView) mHomeButton).setImageDrawable( + mHomeButton.getContext().getDrawable(R.drawable.ic_sysbar_home_kids)); + ((ImageView) mHomeButton).setScaleType(ImageView.ScaleType.FIT_CENTER); + mHomeButton.setPadding(paddingleft, paddingTop, paddingRight, paddingBottom); + + // Home button layout + LinearLayout.LayoutParams homeLayoutparams = new LinearLayout.LayoutParams( + buttonWidth, + buttonHeight + ); + int homeButtonLeftMargin = res.getDimensionPixelSize( + R.dimen.taskbar_home_button_left_margin_kids); + homeLayoutparams.setMargins(homeButtonLeftMargin, 0, 0, 0); + mHomeButton.setLayoutParams(homeLayoutparams); + + // Back button layout + LinearLayout.LayoutParams backLayoutParams = new LinearLayout.LayoutParams( + buttonWidth, + buttonHeight + ); + int backButtonLeftMargin = res.getDimensionPixelSize( + R.dimen.taskbar_back_button_left_margin_kids); + backLayoutParams.setMargins(backButtonLeftMargin, 0, 0, 0); + mBackButton.setLayoutParams(backLayoutParams); + + // Button backgrounds + int whiteWith10PctAlpha = Color.argb(0.1f, 1, 1, 1); + PaintDrawable buttonBackground = new PaintDrawable(whiteWith10PctAlpha); + buttonBackground.setCornerRadius(buttonRadius); + mHomeButton.setBackground(buttonBackground); + mBackButton.setBackground(buttonBackground); + + // Update alignment within taskbar + FrameLayout.LayoutParams navButtonsLayoutParams = (FrameLayout.LayoutParams) + mNavButtonContainer.getLayoutParams(); + navButtonsLayoutParams.setMarginStart( + navButtonsLayoutParams.getMarginEnd() / 2); + navButtonsLayoutParams.setMarginEnd(navButtonsLayoutParams.getMarginStart()); + navButtonsLayoutParams.gravity = Gravity.CENTER; + mNavButtonContainer.requestLayout(); + + mHomeButton.setOnLongClickListener(null); + } else if (mContext.isThreeButtonNav()) { + // Setup normal 3 button + // Add spacing after the end of the last nav button + FrameLayout.LayoutParams navButtonParams = + (FrameLayout.LayoutParams) mNavButtonContainer.getLayoutParams(); + navButtonParams.gravity = Gravity.END; + navButtonParams.width = FrameLayout.LayoutParams.WRAP_CONTENT; + navButtonParams.height = MATCH_PARENT; + + int navMarginEnd = (int) res.getDimension(dp.inv.inlineNavButtonsEndSpacing); + int contextualWidth = mEndContextualContainer.getWidth(); + // If contextual buttons are showing, we check if the end margin is enough for the + // contextual button to be showing - if not, move the nav buttons over a smidge + if (isContextualButtonShowing() && navMarginEnd < contextualWidth) { + // Additional spacing, eat up half of space between last icon and nav button + navMarginEnd += res.getDimensionPixelSize(R.dimen.taskbar_hotseat_nav_spacing) / 2; + } + navButtonParams.setMarginEnd(navMarginEnd); + mNavButtonContainer.setLayoutParams(navButtonParams); + + // Add the spaces in between the nav buttons + int spaceInBetween = res.getDimensionPixelSize(R.dimen.taskbar_button_space_inbetween); + for (int i = 0; i < mNavButtonContainer.getChildCount(); i++) { + View navButton = mNavButtonContainer.getChildAt(i); + LinearLayout.LayoutParams buttonLayoutParams = + (LinearLayout.LayoutParams) navButton.getLayoutParams(); + buttonLayoutParams.weight = 0; + if (i == 0) { + buttonLayoutParams.setMarginEnd(spaceInBetween / 2); + } else if (i == mNavButtonContainer.getChildCount() - 1) { + buttonLayoutParams.setMarginStart(spaceInBetween / 2); + } else { + buttonLayoutParams.setMarginStart(spaceInBetween / 2); + buttonLayoutParams.setMarginEnd(spaceInBetween / 2); + } + } + } + } public void onDestroy() { @@ -659,6 +841,10 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT } moveNavButtonsBackToTaskbarWindow(); + mNavButtonContainer.removeAllViews(); + mEndContextualContainer.removeAllViews(); + mStartContextualContainer.removeAllViews(); + mAllButtons.clear(); } /** @@ -677,14 +863,14 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT 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); } }); @@ -712,7 +898,7 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT 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); } @@ -721,22 +907,17 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT public void dumpLogs(String prefix, PrintWriter pw) { pw.println(prefix + "NavbarButtonsViewController:"); - pw.println(String.format("%s\tmState=%s", prefix, getStateString(mState))); - pw.println(String.format( - "%s\tmLightIconColor=0x%s", prefix, Integer.toHexString(mLightIconColor))); - pw.println(String.format( - "%s\tmDarkIconColor=0x%s", prefix, Integer.toHexString(mDarkIconColor))); - pw.println(String.format( - "%s\tmFloatingRotationButtonBounds=%s", prefix, mFloatingRotationButtonBounds)); - pw.println(String.format( - "%s\tmSysuiStateFlags=%s", - prefix, - QuickStepContract.getSystemUiStateString(mSysuiStateFlags))); + pw.println(prefix + "\tmState=" + getStateString(mState)); + pw.println(prefix + "\tmLightIconColor=" + Integer.toHexString(mLightIconColor)); + pw.println(prefix + "\tmDarkIconColor=" + Integer.toHexString(mDarkIconColor)); + pw.println(prefix + "\tmFloatingRotationButtonBounds=" + mFloatingRotationButtonBounds); + pw.println(prefix + "\tmSysuiStateFlags=" + QuickStepContract.getSystemUiStateString( + mSysuiStateFlags)); } private static String getStateString(int flags) { StringJoiner str = new StringJoiner("|"); - appendFlag(str, flags, FLAG_SWITCHER_SUPPORTED, "FLAG_SWITCHER_SUPPORTED"); + appendFlag(str, flags, FLAG_SWITCHER_SHOWING, "FLAG_SWITCHER_SHOWING"); appendFlag(str, flags, FLAG_IME_VISIBLE, "FLAG_IME_VISIBLE"); appendFlag(str, flags, FLAG_ROTATION_BUTTON_VISIBLE, "FLAG_ROTATION_BUTTON_VISIBLE"); appendFlag(str, flags, FLAG_A11Y_VISIBLE, "FLAG_A11Y_VISIBLE"); @@ -750,6 +931,8 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT appendFlag(str, flags, FLAG_NOTIFICATION_SHADE_EXPANDED, "FLAG_NOTIFICATION_SHADE_EXPANDED"); appendFlag(str, flags, FLAG_SCREEN_PINNING_ACTIVE, "FLAG_SCREEN_PINNING_ACTIVE"); + appendFlag(str, flags, FLAG_VOICE_INTERACTION_WINDOW_SHOWING, + "FLAG_VOICE_INTERACTION_WINDOW_SHOWING"); return str.toString(); } @@ -866,9 +1049,9 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT 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 b7978076c1..74e73754df 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.graphics.Rect; 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; @@ -43,7 +46,8 @@ public class StashedHandleViewController implements TaskbarControllers.LoggableT public static final int ALPHA_INDEX_STASHED = 0; public static final int ALPHA_INDEX_HOME_DISABLED = 1; - private static final int NUM_ALPHA_CHANNELS = 2; + public static final int ALPHA_INDEX_ASSISTANT_INVOKED = 2; + private static final int NUM_ALPHA_CHANNELS = 3; /** * The SharedPreferences key for whether the stashed handle region is dark. @@ -54,15 +58,16 @@ public class StashedHandleViewController implements TaskbarControllers.LoggableT 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(); @@ -76,7 +81,7 @@ public class StashedHandleViewController implements TaskbarControllers.LoggableT 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); @@ -84,30 +89,30 @@ public class StashedHandleViewController implements TaskbarControllers.LoggableT 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(); @@ -134,13 +139,40 @@ public class StashedHandleViewController implements TaskbarControllers.LoggableT 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; + } + + private boolean isPhoneGestureNavMode(DeviceProfile deviceProfile) { + return TaskbarManager.isPhoneMode(deviceProfile) && !mActivity.isThreeButtonNav(); } - public MultiValueAlpha getStashedHandleAlpha() { + public MultiPropertyFactory<View> getStashedHandleAlpha() { return mTaskbarStashedHandleAlpha; } @@ -154,9 +186,17 @@ public class StashedHandleViewController implements TaskbarControllers.LoggableT * 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; @@ -193,10 +233,17 @@ public class StashedHandleViewController implements TaskbarControllers.LoggableT } /** + * 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); } @@ -208,10 +255,9 @@ public class StashedHandleViewController implements TaskbarControllers.LoggableT public void dumpLogs(String prefix, PrintWriter pw) { pw.println(prefix + "StashedHandleViewController:"); - pw.println(String.format( - "%s\tisStashedHandleVisible=%b", prefix, isStashedHandleVisible())); - pw.println(String.format("%s\tmStashedHandleWidth=%dpx", prefix, mStashedHandleWidth)); - pw.println(String.format("%s\tmStashedHandleHeight=%dpx", prefix, mStashedHandleHeight)); + pw.println(prefix + "\tisStashedHandleVisible=" + isStashedHandleVisible()); + pw.println(prefix + "\tmStashedHandleWidth=" + mStashedHandleWidth); + pw.println(prefix + "\tmStashedHandleHeight=" + mStashedHandleHeight); mRegionSamplingHelper.dump(prefix, pw); } } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java index dc2e3b624c..731eea78e9 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java @@ -23,10 +23,15 @@ import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; import static com.android.launcher3.AbstractFloatingView.TYPE_ALL; import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE; -import static com.android.launcher3.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; import android.animation.AnimatorSet; import android.animation.ValueAnimator; @@ -73,20 +78,26 @@ 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.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.TestProtocol; +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.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; @@ -117,7 +128,7 @@ public class TaskbarActivityContext extends BaseTaskbarContext { // 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(); @@ -125,34 +136,40 @@ public class TaskbarActivityContext extends BaseTaskbarContext { 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( + mIsNavBarForceVisible = settingsCache.getValue( Settings.Secure.getUriFor(Settings.Secure.NAV_BAR_KIDS_MODE), 0); - mIsNavBarKidsMode = SettingsCache.INSTANCE.get(this).getValue( + // TODO(b/244231596) For shared Taskbar window, update this value in init() instead so + // to get correct value when recreating the taskbar + mIsNavBarKidsMode = settingsCache.getValue( Settings.Secure.getUriFor(Settings.Secure.NAV_BAR_KIDS_MODE), 0); - updateIconSize(resources); - // Get display and corners first, as views might use them in constructor. Display display = windowContext.getDisplay(); Context c = display.getDisplayId() == Display.DEFAULT_DISPLAY @@ -163,8 +180,10 @@ public class TaskbarActivityContext extends BaseTaskbarContext { 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); @@ -172,13 +191,15 @@ public class TaskbarActivityContext extends BaseTaskbarContext { mAccessibilityDelegate = new TaskbarShortcutMenuAccessibilityDelegate(this); + final boolean isDesktopMode = getPackageManager().hasSystemFeature(FEATURE_PC); + // Construct controllers. mControllers = new TaskbarControllers(this, new TaskbarDragController(this), buttonController, - getPackageManager().hasSystemFeature(FEATURE_PC) - ? new DesktopNavbarButtonsViewController(this, navButtonsView) : - new NavbarButtonsViewController(this, navButtonsView), + isDesktopMode + ? new DesktopNavbarButtonsViewController(this, navButtonsView) + : new NavbarButtonsViewController(this, navButtonsView), new RotationButtonController(this, c.getColor(R.color.taskbar_nav_icon_light_color), c.getColor(R.color.taskbar_nav_icon_dark_color), @@ -191,7 +212,9 @@ public class TaskbarActivityContext extends BaseTaskbarContext { 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), @@ -199,8 +222,14 @@ public class TaskbarActivityContext extends BaseTaskbarContext { new TaskbarAutohideSuspendController(this), new TaskbarPopupController(this), new TaskbarForceVisibleImmersiveController(this), - new TaskbarAllAppsController(this, dp), - new TaskbarInsetsController(this)); + new TaskbarOverlayController(this, launcherDp), + new TaskbarAllAppsController(), + new TaskbarInsetsController(this), + new VoiceInteractionWindowController(this), + new TaskbarTranslationController(this), + isDesktopMode + ? new DesktopTaskbarRecentAppsController(this) + : TaskbarRecentAppsController.DEFAULT); } public void init(@NonNull TaskbarSharedState sharedState) { @@ -211,7 +240,12 @@ public class TaskbarActivityContext extends BaseTaskbarContext { 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 @@ -220,10 +254,10 @@ public class TaskbarActivityContext extends BaseTaskbarContext { } /** 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. @@ -232,11 +266,34 @@ public class TaskbarActivityContext extends BaseTaskbarContext { dispatchDeviceProfileChanged(); } + /** + * 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 @@ -246,29 +303,59 @@ public class TaskbarActivityContext extends BaseTaskbarContext { return super.getStatsLogManager(); } - /** Creates LayoutParams for adding a view directly to WindowManager as a new window */ + /** @see #createDefaultWindowLayoutParams(int) */ public WindowManager.LayoutParams createDefaultWindowLayoutParams() { + return createDefaultWindowLayoutParams(TYPE_NAVIGATION_BAR_PANEL); + } + + /** + * Creates LayoutParams for adding a view directly to WindowManager as a new window. + * @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, - TYPE_NAVIGATION_BAR_PANEL, - WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE - | WindowManager.LayoutParams.FLAG_SLIPPERY, + isVerticalBarLayout ? mLastRequestedNonFullscreenHeight : MATCH_PARENT, + isVerticalBarLayout ? MATCH_PARENT : mLastRequestedNonFullscreenHeight, + type, + 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() { @@ -414,7 +501,7 @@ public class TaskbarActivityContext extends BaseTaskbarContext { @Override public void onDragEnd() { - maybeSetTaskbarWindowNotFullscreen(); + onDragEndOrViewRemoved(); } @Override @@ -445,7 +532,10 @@ public class TaskbarActivityContext extends BaseTaskbarContext { 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) { @@ -453,6 +543,8 @@ public class TaskbarActivityContext extends BaseTaskbarContext { fromInit); mControllers.taskbarViewController.setImeIsVisible( mControllers.navbarButtonsViewController.isImeVisible()); + mControllers.taskbarViewController.setIsImeSwitcherVisible( + mControllers.navbarButtonsViewController.isImeSwitcherVisible()); int shadeExpandedFlags = SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED | SYSUI_STATE_QUICK_SETTINGS_EXPANDED; onNotificationShadeExpandChanged((systemUiStateFlags & shadeExpandedFlags) != 0, fromInit); @@ -468,6 +560,8 @@ public class TaskbarActivityContext extends BaseTaskbarContext { fromInit); mControllers.navButtonController.updateSysuiFlags(systemUiStateFlags); mControllers.taskbarForceVisibleImmersiveController.updateSysuiFlags(systemUiStateFlags); + mControllers.voiceInteractionWindowController.setIsVoiceInteractionWindowVisible( + (systemUiStateFlags & SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING) != 0, fromInit); } /** @@ -476,7 +570,7 @@ public class TaskbarActivityContext extends BaseTaskbarContext { 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() @@ -512,24 +606,33 @@ public class TaskbarActivityContext extends BaseTaskbarContext { } /** + * 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() { @@ -576,9 +679,32 @@ public class TaskbarActivityContext extends BaseTaskbarContext { * 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. @@ -607,12 +733,16 @@ public class TaskbarActivityContext extends BaseTaskbarContext { /** 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}. */ public void removeWindowView(View view) { - mWindowManager.removeViewImmediate(view); + if (view.isAttachedToWindow()) { + mWindowManager.removeViewImmediate(view); + } } protected void onTaskbarIconClicked(View view) { @@ -621,6 +751,7 @@ public class TaskbarActivityContext extends BaseTaskbarContext { 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(); @@ -651,44 +782,67 @@ public class TaskbarActivityContext extends BaseTaskbarContext { }); } else if (tag instanceof WorkspaceItemInfo) { 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); - - } 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); + if (!info.isDisabled() || !ItemClickHandler.handleDisabledItemClicked(info, this)) { + TaskbarUIController taskbarUIController = mControllers.uiController; + RecentsView recents = taskbarUIController.getRecentsView(); + if (recents != null + && taskbarUIController.getRecentsView().isSplitSelectionActive()) { + // If we are selecting a second app for split, launch the split tasks + taskbarUIController.triggerSecondAppForSplit(info, info.intent, view); + } else { + // Else launch the selected task + Intent intent = new Intent(info.getIntent()) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + try { + if (mIsSafeModeEnabled && !PackageManagerHelper.isSystemApp(this, intent)) { + Toast.makeText(this, R.string.safemode_shortcut_error, + Toast.LENGTH_SHORT).show(); + } else if (info.isPromise()) { + TestLogging.recordEvent( + TestProtocol.SEQUENCE_MAIN, "start: taskbarPromiseIcon"); + intent = new PackageManagerHelper(this) + .getMarketIntent(info.getTargetPackage()) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(intent); + + } else if (info.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) { + TestLogging.recordEvent( + TestProtocol.SEQUENCE_MAIN, "start: taskbarDeepShortcut"); + String id = info.getDeepShortcutId(); + String packageName = intent.getPackage(); + getSystemService(LauncherApps.class) + .startShortcut(packageName, id, null, null, info.user); + } else { + startItemInfoActivity(info); + } + + mControllers.uiController.onTaskbarIconLaunched(info); + } catch (NullPointerException + | ActivityNotFoundException + | SecurityException e) { + Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT) + .show(); + Log.e(TAG, "Unable to launch. tag=" + info + " intent=" + intent, e); } - - mControllers.uiController.onTaskbarIconLaunched(info); - } catch (NullPointerException | ActivityNotFoundException | SecurityException e) { - Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT) - .show(); - Log.e(TAG, "Unable to launch. tag=" + info + " intent=" + intent, e); } + mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true); } } else if (tag instanceof AppInfo) { - startItemInfoActivity((AppInfo) tag); - mControllers.uiController.onTaskbarIconLaunched((AppInfo) tag); + AppInfo info = (AppInfo) tag; + TaskbarUIController taskbarUIController = mControllers.uiController; + RecentsView recents = taskbarUIController.getRecentsView(); + if (recents != null + && taskbarUIController.getRecentsView().isSplitSelectionActive()) { + // If we are selecting a second app for split, launch the split tasks + taskbarUIController.triggerSecondAppForSplit(info, info.intent, view); + } else { + // Else launch the selected task + startItemInfoActivity((AppInfo) tag); + mControllers.uiController.onTaskbarIconLaunched((AppInfo) tag); + } + mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true); + } else if (tag instanceof ItemClickProxy) { + ((ItemClickProxy) tag).onItemClicked(view); } else { Log.e(TAG, "Unknown type clicked: " + tag); } @@ -724,6 +878,39 @@ public class TaskbarActivityContext extends BaseTaskbarContext { } /** + * Called when we want to unstash taskbar when user performs swipes up gesture. + */ + public void onSwipeToUnstashTaskbar() { + mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(false); + } + + /** 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. */ @@ -731,11 +918,42 @@ public class TaskbarActivityContext extends BaseTaskbarContext { 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(); } @@ -769,12 +987,13 @@ public class TaskbarActivityContext extends BaseTaskbarContext { } 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); } @@ -808,6 +1027,10 @@ public class TaskbarActivityContext extends BaseTaskbarContext { btv.post(() -> mControllers.taskbarPopupController.showForIcon(btv)); } + public boolean isInApp() { + return mControllers.taskbarStashController.isInApp(); + } + protected void dumpLogs(String prefix, PrintWriter pw) { pw.println(prefix + "TaskbarActivityContext:"); @@ -822,6 +1045,11 @@ public class TaskbarActivityContext extends BaseTaskbarContext { 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 c5615c7ba1..4350e9c280 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendController.java @@ -33,21 +33,28 @@ import java.util.StringJoiner; public class TaskbarAutohideSuspendController implements TaskbarControllers.LoggableTaskbarController { + // Taskbar window is fullscreen. public static final int FLAG_AUTOHIDE_SUSPEND_FULLSCREEN = 1 << 0; + // User is dragging item. public static final int FLAG_AUTOHIDE_SUSPEND_DRAGGING = 1 << 1; + // User has touched down but has not lifted finger. + public static final int FLAG_AUTOHIDE_SUSPEND_TOUCHING = 1 << 2; @IntDef(flag = true, value = { FLAG_AUTOHIDE_SUSPEND_FULLSCREEN, FLAG_AUTOHIDE_SUSPEND_DRAGGING, + FLAG_AUTOHIDE_SUSPEND_TOUCHING, }) @Retention(RetentionPolicy.SOURCE) public @interface AutohideSuspendFlag {} + private final TaskbarActivityContext mActivity; private final SystemUiProxy mSystemUiProxy; private @AutohideSuspendFlag int mAutohideSuspendFlags = 0; public TaskbarAutohideSuspendController(TaskbarActivityContext activity) { + mActivity = activity; mSystemUiProxy = SystemUiProxy.INSTANCE.get(activity); } @@ -59,20 +66,34 @@ public class TaskbarAutohideSuspendController implements * 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 public void dumpLogs(String prefix, PrintWriter pw) { pw.println(prefix + "TaskbarAutohideSuspendController:"); - pw.println(String.format( - "%s\tmAutohideSuspendFlags=%s", prefix, getStateString(mAutohideSuspendFlags))); + pw.println(prefix + "\tmAutohideSuspendFlags=" + getStateString(mAutohideSuspendFlags)); } private static String getStateString(int flags) { @@ -80,6 +101,7 @@ public class TaskbarAutohideSuspendController implements appendFlag(str, flags, FLAG_AUTOHIDE_SUSPEND_FULLSCREEN, "FLAG_AUTOHIDE_SUSPEND_FULLSCREEN"); appendFlag(str, flags, FLAG_AUTOHIDE_SUSPEND_DRAGGING, "FLAG_AUTOHIDE_SUSPEND_DRAGGING"); + appendFlag(str, flags, FLAG_AUTOHIDE_SUSPEND_TOUCHING, "FLAG_AUTOHIDE_SUSPEND_TOUCHING"); return str.toString(); } } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt index 1177bdb484..ff7e8e9af2 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt @@ -16,10 +16,16 @@ package com.android.launcher3.taskbar +import com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound +import com.android.launcher3.Utilities.mapToRange + import android.graphics.Canvas +import android.graphics.Color import android.graphics.Paint import android.graphics.Path import com.android.launcher3.R +import com.android.launcher3.anim.Interpolators +import com.android.launcher3.util.DisplayController /** * Helps draw the taskbar background, made up of a rectangle plus two inverted rounded corners. @@ -28,9 +34,23 @@ class TaskbarBackgroundRenderer(context: TaskbarActivityContext) { val paint: Paint = Paint() var backgroundHeight = context.deviceProfile.taskbarSize.toFloat() + var translationYForSwipe = 0f + + private var maxBackgroundHeight = context.deviceProfile.taskbarSize.toFloat() + private val transientBackgroundBounds = context.transientTaskbarBounds + + private val isTransientTaskbar = DisplayController.isTransientTaskbar(context); - private val leftCornerRadius = context.leftCornerRadius.toFloat() - private val rightCornerRadius = context.rightCornerRadius.toFloat() + private var shadowBlur = 0f + private var keyShadowDistance = 0f + private var bottomMargin = 0 + + private val fullLeftCornerRadius = context.leftCornerRadius.toFloat() + private val fullRightCornerRadius = context.rightCornerRadius.toFloat() + private var leftCornerRadius = fullLeftCornerRadius + private var rightCornerRadius = fullRightCornerRadius + private val square: Path = Path() + private val circle: Path = Path() private val invertedLeftCornerPath: Path = Path() private val invertedRightCornerPath: Path = Path() @@ -39,13 +59,38 @@ class TaskbarBackgroundRenderer(context: TaskbarActivityContext) { paint.flags = Paint.ANTI_ALIAS_FLAG paint.style = Paint.Style.FILL + if (isTransientTaskbar) { + paint.color = context.getColor(R.color.transient_taskbar_background) + + val res = context.resources + bottomMargin = res.getDimensionPixelSize(R.dimen.transient_taskbar_margin) + shadowBlur = res.getDimension(R.dimen.transient_taskbar_shadow_blur) + keyShadowDistance = res.getDimension(R.dimen.transient_taskbar_key_shadow_distance) + } + + setCornerRoundness(DEFAULT_ROUNDNESS) + } + + /** + * Sets the roundness of the round corner above Taskbar. No effect on transient Taskkbar. + * @param cornerRoundness 0 has no round corner, 1 has complete round corner. + */ + fun setCornerRoundness(cornerRoundness: Float) { + if (isTransientTaskbar && !transientBackgroundBounds.isEmpty) { + return + } + + leftCornerRadius = fullLeftCornerRadius * cornerRoundness + rightCornerRadius = fullRightCornerRadius * cornerRoundness + // Create the paths for the inverted rounded corners above the taskbar. Start with a filled // square, and then subtract out a circle from the appropriate corner. - val square = Path() + square.reset() square.addRect(0f, 0f, leftCornerRadius, leftCornerRadius, Path.Direction.CW) - val circle = Path() + circle.reset() circle.addCircle(leftCornerRadius, 0f, leftCornerRadius, Path.Direction.CW) invertedLeftCornerPath.op(square, circle, Path.Op.DIFFERENCE) + square.reset() square.addRect(0f, 0f, rightCornerRadius, rightCornerRadius, Path.Direction.CW) circle.reset() @@ -58,18 +103,49 @@ class TaskbarBackgroundRenderer(context: TaskbarActivityContext) { */ 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 449e0a7311..bc41c2b520 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.NonNull; 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; @@ -52,8 +54,13 @@ public class TaskbarControllers { public final TaskbarForceVisibleImmersiveController taskbarForceVisibleImmersiveController; public final TaskbarAllAppsController taskbarAllAppsController; public final TaskbarInsetsController taskbarInsetsController; + public final VoiceInteractionWindowController voiceInteractionWindowController; + public final TaskbarRecentAppsController taskbarRecentAppsController; + public final TaskbarTranslationController taskbarTranslationController; + public final TaskbarOverlayController taskbarOverlayController; @Nullable private LoggableTaskbarController[] mControllersToLog = null; + @Nullable private BackgroundRendererController[] mBackgroundRendererControllers = null; /** Do not store this controller, as it may change at runtime. */ @NonNull public TaskbarUIController uiController = TaskbarUIController.DEFAULT; @@ -63,6 +70,9 @@ public class TaskbarControllers { @Nullable private TaskbarSharedState mSharedState = null; + // Roundness property for round corner above taskbar . + private final AnimatedFloat mCornerRoundness = new AnimatedFloat(this::updateCornerRoundness); + public TaskbarControllers(TaskbarActivityContext taskbarActivityContext, TaskbarDragController taskbarDragController, TaskbarNavButtonController navButtonController, @@ -79,8 +89,12 @@ public class TaskbarControllers { TaskbarAutohideSuspendController taskbarAutoHideSuspendController, TaskbarPopupController taskbarPopupController, TaskbarForceVisibleImmersiveController taskbarForceVisibleImmersiveController, + TaskbarOverlayController taskbarOverlayController, TaskbarAllAppsController taskbarAllAppsController, - TaskbarInsetsController taskbarInsetsController) { + TaskbarInsetsController taskbarInsetsController, + VoiceInteractionWindowController voiceInteractionWindowController, + TaskbarTranslationController taskbarTranslationController, + TaskbarRecentAppsController taskbarRecentAppsController) { this.taskbarActivityContext = taskbarActivityContext; this.taskbarDragController = taskbarDragController; this.navButtonController = navButtonController; @@ -97,8 +111,12 @@ public class TaskbarControllers { this.taskbarAutohideSuspendController = taskbarAutoHideSuspendController; this.taskbarPopupController = taskbarPopupController; this.taskbarForceVisibleImmersiveController = taskbarForceVisibleImmersiveController; + this.taskbarOverlayController = taskbarOverlayController; this.taskbarAllAppsController = taskbarAllAppsController; this.taskbarInsetsController = taskbarInsetsController; + this.voiceInteractionWindowController = voiceInteractionWindowController; + this.taskbarTranslationController = taskbarTranslationController; + this.taskbarRecentAppsController = taskbarRecentAppsController; } /** @@ -123,17 +141,27 @@ public class TaskbarControllers { taskbarEduController.init(this); taskbarPopupController.init(this); taskbarForceVisibleImmersiveController.init(this); + taskbarOverlayController.init(this); taskbarAllAppsController.init(this, sharedState.allAppsVisible); navButtonController.init(this); taskbarInsetsController.init(this); + voiceInteractionWindowController.init(this); + taskbarRecentAppsController.init(this); + taskbarTranslationController.init(this); mControllersToLog = new LoggableTaskbarController[] { taskbarDragController, navButtonController, navbarButtonsViewController, taskbarDragLayerController, taskbarScrimViewController, taskbarViewController, taskbarUnfoldAnimationController, taskbarKeyguardController, stashedHandleViewController, taskbarStashController, taskbarEduController, - taskbarAutohideSuspendController, taskbarPopupController, taskbarInsetsController + taskbarAutohideSuspendController, taskbarPopupController, taskbarInsetsController, + voiceInteractionWindowController, taskbarTranslationController + }; + mBackgroundRendererControllers = new BackgroundRendererController[] { + taskbarDragLayerController, taskbarScrimViewController, + voiceInteractionWindowController }; + mCornerRoundness.updateValue(TaskbarBackgroundRenderer.DEFAULT_ROUNDNESS); mAreAllControllersInitialized = true; for (Runnable postInitCallback : mPostInitCallbacks) { @@ -150,12 +178,14 @@ public class TaskbarControllers { public void onConfigurationChanged(@Config int configChanges) { navbarButtonsViewController.onConfigurationChanged(configChanges); + taskbarDragLayerController.onConfigurationChanged(); } /** * Cleans up all controllers. */ public void onDestroy() { + mAreAllControllersInitialized = false; mSharedState = null; navbarButtonsViewController.onDestroy(); @@ -169,11 +199,14 @@ public class TaskbarControllers { taskbarAutohideSuspendController.onDestroy(); taskbarPopupController.onDestroy(); taskbarForceVisibleImmersiveController.onDestroy(); - taskbarAllAppsController.onDestroy(); + taskbarOverlayController.onDestroy(); navButtonController.onDestroy(); taskbarInsetsController.onDestroy(); + voiceInteractionWindowController.onDestroy(); + taskbarRecentAppsController.onDestroy(); mControllersToLog = null; + mBackgroundRendererControllers = null; } /** @@ -207,6 +240,23 @@ public class TaskbarControllers { 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 @@ -216,4 +266,12 @@ public class TaskbarControllers { 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 c522888fce..d1fea7b069 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java @@ -15,12 +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; @@ -31,6 +35,7 @@ import android.graphics.Point; 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; @@ -41,14 +46,12 @@ import android.window.SurfaceSyncer; 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; @@ -65,9 +68,11 @@ import com.android.launcher3.popup.PopupContainerWithArrow; import com.android.launcher3.shortcuts.DeepShortcutView; import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider; import com.android.launcher3.testing.TestLogging; -import com.android.launcher3.testing.TestProtocol; +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; @@ -81,7 +86,8 @@ import java.util.function.Predicate; 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]; @@ -97,6 +103,8 @@ public class TaskbarDragController extends DragController<BaseTaskbarContext> im // 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); @@ -108,6 +116,14 @@ public class TaskbarDragController extends DragController<BaseTaskbarContext> im 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. @@ -129,7 +145,7 @@ public class TaskbarDragController extends DragController<BaseTaskbarContext> im 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"); @@ -191,8 +207,13 @@ public class TaskbarDragController extends DragController<BaseTaskbarContext> im 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(); + } + }); } } @@ -286,11 +307,20 @@ public class TaskbarDragController extends DragController<BaseTaskbarContext> im 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 @@ -358,11 +388,11 @@ public class TaskbarDragController extends DragController<BaseTaskbarContext> im } 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); @@ -391,9 +421,14 @@ public class TaskbarDragController extends DragController<BaseTaskbarContext> im 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; @@ -420,6 +455,45 @@ public class TaskbarDragController extends DragController<BaseTaskbarContext> im } @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(); @@ -430,56 +504,20 @@ public class TaskbarDragController extends DragController<BaseTaskbarContext> im 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) { - // 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; @@ -515,6 +553,68 @@ public class TaskbarDragController extends DragController<BaseTaskbarContext> im 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 @@ -538,7 +638,7 @@ public class TaskbarDragController extends DragController<BaseTaskbarContext> im @Override protected void exitDrag() { - if (mDragObject != null) { + if (mDragObject != null && !mDisallowGlobalDrag) { mActivity.getDragLayer().removeView(mDragObject.dragView); } } @@ -554,17 +654,21 @@ public class TaskbarDragController extends DragController<BaseTaskbarContext> im 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:"); - pw.println(String.format("%s\tmDragIconSize=%dpx", prefix, mDragIconSize)); - pw.println(String.format("%s\tmTempXY=%s", prefix, Arrays.toString(mTempXY))); - pw.println(String.format("%s\tmRegistrationX=%d", prefix, mRegistrationX)); - pw.println(String.format("%s\tmRegistrationY=%d", prefix, mRegistrationY)); - pw.println(String.format( - "%s\tmIsSystemDragInProgress=%b", prefix, mIsSystemDragInProgress)); - pw.println(String.format( - "%s\tisInternalDragInProgess=%b", prefix, super.isDragging())); + pw.println(prefix + "\tmDragIconSize=" + mDragIconSize); + pw.println(prefix + "\tmTempXY=" + Arrays.toString(mTempXY)); + pw.println(prefix + "\tmRegistrationX=" + mRegistrationX); + 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 c1a6185d23..d0059f7207 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java @@ -24,17 +24,15 @@ import android.util.AttributeSet; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; +import android.view.ViewTreeObserver; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.testing.TestLogging; -import com.android.launcher3.testing.TestProtocol; +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 @@ import com.android.systemui.shared.system.ViewTreeObserverWrapper.OnComputeInset 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 @@ public class TaskbarDragLayer extends BaseDragLayer<TaskbarActivityContext> { 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 @@ public class TaskbarDragLayer extends BaseDragLayer<TaskbarActivityContext> { } @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 @@ public class TaskbarDragLayer extends BaseDragLayer<TaskbarActivityContext> { 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 99c59a8a98..cd27a46a00 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 @@ public class TaskbarDragLayerController implements TaskbarControllers.LoggableTa 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 @@ public class TaskbarDragLayerController implements TaskbarControllers.LoggableTa 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 @@ public class TaskbarDragLayerController implements TaskbarControllers.LoggableTa 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); @@ -144,10 +178,9 @@ public class TaskbarDragLayerController implements TaskbarControllers.LoggableTa public void dumpLogs(String prefix, PrintWriter pw) { pw.println(prefix + "TaskbarDragLayerController:"); - pw.println(String.format("%s\tmBgOffset=%.2f", prefix, mBgOffset.value)); - pw.println(String.format("%s\tmFolderMargin=%dpx", prefix, mFolderMargin)); - pw.println(String.format( - "%s\tmLastSetBackgroundAlpha=%.2f", prefix, mLastSetBackgroundAlpha)); + pw.println(prefix + "\tmBgOffset=" + mBgOffset.value); + pw.println(prefix + "\tmFolderMargin=" + mFolderMargin); + pw.println(prefix + "\tmLastSetBackgroundAlpha=" + mLastSetBackgroundAlpha); } /** @@ -155,26 +188,70 @@ public class TaskbarDragLayerController implements TaskbarControllers.LoggableTa */ 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 e29b14b76f..d3f1b2f51e 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,125 +49,59 @@ public class TaskbarEduController implements TaskbarControllers.LoggableTaskbarC } 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()); - }); - } - - void hideEdu() { - if (mTaskbarEduView != null) { - mTaskbarEduView.close(true /* animate */); - } - } - - /** - * 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(); - } - - /** - * 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(); - - 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; + TaskbarOverlayController overlayController = mControllers.taskbarOverlayController; + TaskbarOverlayContext overlayContext = overlayController.requestWindow(); + LayoutInflater layoutInflater = overlayContext.getLayoutInflater(); + + 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); + + // 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; + + mTaskbarEduView.init(new TaskbarEduCallbacks()); + + 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(String.format("%s\tisShowingEdu=%b", prefix, mTaskbarEduView != null)); - pw.println(String.format("%s\tmWaveAnimTranslationY=%.2f", prefix, mWaveAnimTranslationY)); - pw.println(String.format( - "%s\tmWaveAnimTranslationYReturnOvershoot=%.2f", - prefix, - mWaveAnimTranslationYReturnOvershoot)); + pw.println(prefix + "\tisShowingEdu=" + (mTaskbarEduView != null)); } /** * 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 */)); @@ -219,5 +117,17 @@ public class TaskbarEduController implements TaskbarControllers.LoggableTaskbarC 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 8e57ea62fc..6cd6512391 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduPagedView.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduPagedView.java @@ -48,7 +48,7 @@ public class TaskbarEduPagedView extends PagedView<PageIndicatorDots> { void setControllerCallbacks(TaskbarEduCallbacks controllerCallbacks) { mControllerCallbacks = controllerCallbacks; - mControllerCallbacks.onPageChanged(getCurrentPage(), getPageCount()); + mControllerCallbacks.onPageChanged(getCurrentPage(), getCurrentPage(), getPageCount()); } @Override @@ -67,7 +67,7 @@ public class TaskbarEduPagedView extends PagedView<PageIndicatorDots> { @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/TaskbarEduView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduView.java index 89d67be685..5702b6bb5c 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.provider.Settings; 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 @@ public class TaskbarEduView extends AbstractSlideInView<TaskbarActivityContext> 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 @@ public class TaskbarEduView extends AbstractSlideInView<TaskbarActivityContext> 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 @@ public class TaskbarEduView extends AbstractSlideInView<TaskbarActivityContext> 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 c99cebb1a7..ffaee455d9 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarForceVisibleImmersiveController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarForceVisibleImmersiveController.java @@ -18,21 +18,22 @@ package com.android.launcher3.taskbar; 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 @@ public class TaskbarForceVisibleImmersiveController implements TouchController { 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 @@ public class TaskbarForceVisibleImmersiveController implements TouchController { } 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 @@ public class TaskbarForceVisibleImmersiveController implements TouchController { } 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); + } + MultiPropertyFactory<View> ha = + mControllers.navbarButtonsViewController.getHomeButtonAlpha(); + if (ba != null) { + ha.get(ALPHA_INDEX_IMMERSIVE_MODE).setValue(mIconAlphaForDimming.value); } - return Optional.ofNullable(mControllers.navbarButtonsViewController.getHomeButtonAlpha()); } @Override diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt index 6a6a693dc3..a48b88fc19 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt @@ -17,17 +17,23 @@ package com.android.launcher3.taskbar import android.graphics.Insets import android.graphics.Region +import android.view.InsetsFrameProvider +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 /** @@ -36,9 +42,8 @@ import java.io.PrintWriter 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() } @@ -51,8 +56,7 @@ class TaskbarInsetsController(val context: TaskbarActivityContext): LoggableTask this.controllers = controllers windowLayoutParams = context.windowLayoutParams - val wmWrapper: WindowManagerWrapper = getInstance() - wmWrapper.setProvidesInsetsTypes( + setProvidesInsetsTypes( windowLayoutParams, intArrayOf( ITYPE_EXTRA_NAVIGATION_BAR, @@ -61,9 +65,6 @@ class TaskbarInsetsController(val context: TaskbarActivityContext): LoggableTask ) ) - windowLayoutParams.providedInternalInsets = arrayOfNulls<Insets>(ITYPE_SIZE) - windowLayoutParams.providedInternalImeInsets = arrayOfNulls<Insets>(ITYPE_SIZE) - onTaskbarWindowHeightOrInsetsChanged() windowLayoutParams.insetsRoundedCornerFrame = true @@ -75,39 +76,80 @@ class TaskbarInsetsController(val context: TaskbarActivityContext): LoggableTask } fun onTaskbarWindowHeightOrInsetsChanged() { - var reducingSize = getReducingInsetsForTaskbarInsetsHeight( - controllers.taskbarStashController.contentHeightToReportToApps) - - contentRegion.set(0, reducingSize.top, - context.deviceProfile.widthPx, windowLayoutParams.height) - windowLayoutParams.providedInternalInsets[ITYPE_EXTRA_NAVIGATION_BAR] = reducingSize - windowLayoutParams.providedInternalInsets[ITYPE_BOTTOM_MANDATORY_GESTURES] = reducingSize - reducingSize = getReducingInsetsForTaskbarInsetsHeight( - controllers.taskbarStashController.tappableHeightToReportToApps) - windowLayoutParams.providedInternalInsets[ITYPE_BOTTOM_TAPPABLE_ELEMENT] = reducingSize - windowLayoutParams.providedInternalInsets[ITYPE_BOTTOM_MANDATORY_GESTURES] = reducingSize - - reducingSize = getReducingInsetsForTaskbarInsetsHeight(taskbarHeightForIme) - windowLayoutParams.providedInternalImeInsets[ITYPE_EXTRA_NAVIGATION_BAR] = reducingSize - windowLayoutParams.providedInternalImeInsets[ITYPE_BOTTOM_TAPPABLE_ELEMENT] = reducingSize - windowLayoutParams.providedInternalImeInsets[ITYPE_BOTTOM_MANDATORY_GESTURES] = reducingSize + 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.type == ITYPE_BOTTOM_MANDATORY_GESTURES) { + provider.insetsSize = getInsetsByNavMode(contentHeight) + } else if (provider.type == ITYPE_BOTTOM_TAPPABLE_ELEMENT) { + provider.insetsSize = getInsetsByNavMode(tappableHeight) + } + } + + 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) { + 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) } /** - * WindowLayoutParams.providedInternal*Insets expects Insets that subtract from the window frame - * height (i.e. WindowLayoutParams#height). So for Taskbar to report bottom insets to apps, it - * actually provides insets from the top of its window frame. - * @param height The number of pixels from the bottom of the screen that Taskbar insets. + * 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. */ - private fun getReducingInsetsForTaskbarInsetsHeight(height: Int): Insets { - return Insets.of(0, windowLayoutParams.height - height, 0, 0) + 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( @@ -116,18 +158,22 @@ class TaskbarInsetsController(val context: TaskbarActivityContext): LoggableTask 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) + 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 @@ -135,15 +181,15 @@ class TaskbarInsetsController(val context: TaskbarActivityContext): LoggableTask // 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) } @@ -151,13 +197,18 @@ class TaskbarInsetsController(val context: TaskbarActivityContext): LoggableTask override fun dumpLogs(prefix: String, pw: PrintWriter) { pw.println(prefix + "TaskbarInsetsController:") pw.println("$prefix\twindowHeight=${windowLayoutParams.height}") - pw.println("$prefix\tprovidedInternalInsets[ITYPE_EXTRA_NAVIGATION_BAR]=" + - "${windowLayoutParams.providedInternalInsets[ITYPE_EXTRA_NAVIGATION_BAR]}") - pw.println("$prefix\tprovidedInternalInsets[ITYPE_BOTTOM_TAPPABLE_ELEMENT]=" + - "${windowLayoutParams.providedInternalInsets[ITYPE_BOTTOM_TAPPABLE_ELEMENT]}") - pw.println("$prefix\tprovidedInternalImeInsets[ITYPE_EXTRA_NAVIGATION_BAR]=" + - "${windowLayoutParams.providedInternalImeInsets[ITYPE_EXTRA_NAVIGATION_BAR]}") - pw.println("$prefix\tprovidedInternalImeInsets[ITYPE_BOTTOM_TAPPABLE_ELEMENT]=" + - "${windowLayoutParams.providedInternalImeInsets[ITYPE_BOTTOM_TAPPABLE_ELEMENT]}") + for (provider in windowLayoutParams.providedInsets) { + 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()) { + if (i > 0) pw.print(", ") + pw.print(overrideSize) + } + pw.print("})") + } + pw.println() + } } -}
\ No newline at end of file +} diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarKeyguardController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarKeyguardController.java index 56648eac38..0808faba1c 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarKeyguardController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarKeyguardController.java @@ -111,11 +111,9 @@ public class TaskbarKeyguardController implements TaskbarControllers.LoggableTas public void dumpLogs(String prefix, PrintWriter pw) { pw.println(prefix + "TaskbarKeyguardController:"); - pw.println(String.format( - "%s\tmKeyguardSysuiFlags=%s", - prefix, - QuickStepContract.getSystemUiStateString(mKeyguardSysuiFlags))); - pw.println(String.format("%s\tmBouncerShowing=%b", prefix, mBouncerShowing)); - pw.println(String.format("%s\tmIsScreenOff=%b", prefix, mIsScreenOff)); + pw.println(prefix + "\tmKeyguardSysuiFlags=" + QuickStepContract.getSystemUiStateString( + mKeyguardSysuiFlags)); + pw.println(prefix + "\tmBouncerShowing=" + mBouncerShowing); + pw.println(prefix + "\tmIsScreenOff=" + mIsScreenOff); } } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java index dc0ef27ba1..4ad3858de1 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java @@ -19,22 +19,28 @@ import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_APP; import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_STASHED_LAUNCHER_STATE; import static com.android.launcher3.taskbar.TaskbarStashController.TASKBAR_STASH_DURATION; import static com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_HOME; +import static com.android.systemui.animation.Interpolators.EMPHASIZED; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; +import android.util.Log; import androidx.annotation.NonNull; 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; @@ -44,8 +50,6 @@ import com.android.systemui.shared.recents.model.ThumbnailData; import java.io.PrintWriter; import java.util.HashMap; import java.util.StringJoiner; -import java.util.function.Consumer; -import java.util.function.Supplier; /** * Track LauncherState, RecentsAnimation, resumed state for task bar in one place here and animate @@ -53,24 +57,25 @@ import java.util.function.Supplier; */ public class TaskbarLauncherStateController { + private static final String TAG = TaskbarLauncherStateController.class.getSimpleName(); + private static final boolean DEBUG = false; + public static final int FLAG_RESUMED = 1 << 0; 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; @@ -78,14 +83,16 @@ import java.util.function.Supplier; 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>() { @@ -99,7 +106,11 @@ import java.util.function.Supplier; } updateStateForFlag(FLAG_TRANSITION_STATE_RUNNING, true); if (!mShouldDelayLauncherStateAnim) { - applyState(); + if (toState == LauncherState.NORMAL) { + applyState(QuickstepTransitionManager.TASKBAR_TO_HOME_DURATION); + } else { + applyState(); + } } } @@ -108,10 +119,17 @@ import java.util.function.Supplier; 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; @@ -119,13 +137,12 @@ import java.util.function.Supplier; mTaskbarBackgroundAlpha = mControllers.taskbarDragLayerController .getTaskbarBackgroundAlpha(); - MultiValueAlpha taskbarIconAlpha = mControllers.taskbarViewController.getTaskbarIconAlpha(); - mIconAlphaForHome = taskbarIconAlpha.getProperty(ALPHA_INDEX_HOME); - mIconAlphaForHome.setConsumer( - (Consumer<Float>) alpha -> mLauncher.getHotseat().setIconsAlpha(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); @@ -135,20 +152,19 @@ import java.util.function.Supplier; 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, @@ -161,23 +177,28 @@ import java.util.function.Supplier; 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(applyState(duration, false)); + if (mTaskBarRecentsAnimationListener != null) { + mTaskBarRecentsAnimationListener.endGestureStateOverride( + !mLauncher.isInState(LauncherState.OVERVIEW)); + } mTaskBarRecentsAnimationListener = new TaskBarRecentsAnimationListener(callbacks); callbacks.addListener(mTaskBarRecentsAnimationListener); - RecentsView recentsView = mLauncher.getOverviewPanel(); - recentsView.setTaskLaunchListener(() -> { - mTaskBarRecentsAnimationListener.endGestureStateOverride(true); - }); + ((RecentsView) mLauncher.getOverviewPanel()).setTaskLaunchListener(() -> + mTaskBarRecentsAnimationListener.endGestureStateOverride(true)); return animatorSet; } public boolean isAnimatingToLauncher() { - return mIsAnimatingToLauncherViaResume || mIsAnimatingToLauncherViaGesture; + return mIsAnimatingToLauncher; } public void setShouldDelayLauncherStateAnim(boolean shouldDelayLauncherStateAnim) { @@ -237,11 +258,19 @@ import java.util.function.Supplier; } private Animator onStateChangeApplied(int changedFlags, long duration, boolean start) { + boolean goingToLauncher = isInLauncher(); + final float toAlignment = isIconAlignedWithHotseat() ? 1 : 0; + if (DEBUG) { + Log.d(TAG, "onStateChangeApplied - mState: " + getStateString(mState) + + ", changedFlags: " + getStateString(changedFlags) + + ", goingToLauncher: " + goingToLauncher + + ", mLauncherState: " + mLauncherState + + ", toAlignment: " + toAlignment); + } AnimatorSet animatorSet = new AnimatorSet(); // Add the state animation first to ensure FLAG_IN_STASHED_LAUNCHER_STATE is set and we can // determine whether goingToUnstashedLauncherStateChanged. - boolean wasGoingToUnstashedLauncherState = goingToUnstashedLauncherState(); if (hasAnyFlag(changedFlags, FLAG_TRANSITION_STATE_RUNNING)) { boolean committed = !hasAnyFlag(FLAG_TRANSITION_STATE_RUNNING); playStateTransitionAnim(animatorSet, duration, committed); @@ -252,84 +281,83 @@ import java.util.function.Supplier; 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); - - resumeAlignAnim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mIsAnimatingToLauncherViaResume = false; - } - @Override - public void onAnimationStart(Animator animation) { - mIsAnimatingToLauncherViaResume = isResumed; + if (hasAnyFlag(changedFlags, FLAGS_LAUNCHER)) { + animatorSet.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mIsAnimatingToLauncher = false; + } - TaskbarStashController stashController = - mControllers.taskbarStashController; - stashController.updateStateForFlag(FLAG_IN_APP, !isResumed); - stashController.applyState(duration); + @Override + public void onAnimationStart(Animator animation) { + mIsAnimatingToLauncher = goingToLauncher; + + TaskbarStashController stashController = + mControllers.taskbarStashController; + if (DEBUG) { + Log.d(TAG, "onAnimationStart - FLAG_IN_APP: " + !goingToLauncher); } - }); - animatorSet.play(resumeAlignAnim); + stashController.updateStateForFlag(FLAG_IN_APP, !goingToLauncher); + stashController.applyState(duration); + } + }); + + 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(backgroundAlpha) + .setDuration(duration)); + } - 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); - } - gestureAlignAnim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mIsAnimatingToLauncherViaGesture = false; - } - - @Override - public void onAnimationStart(Animator animation) { - mIsAnimatingToLauncherViaGesture = isRecentsAnimationRunning(); - } - }); - animatorSet.play(gestureAlignAnim); + 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 (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); + 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(mTaskbarBackgroundAlpha.animateToValue(goingToLauncher ? 0 : 1) - .setDuration(duration)); + animatorSet.play(iconAlignAnim); } + animatorSet.setInterpolator(EMPHASIZED); + if (start) { animatorSet.start(); } @@ -337,18 +365,39 @@ import java.util.function.Supplier; } /** 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); - float toAlignment = mLauncherState.isTaskbarAlignedWithHotseat(mLauncher) ? 1 : 0; - - TaskbarStashController controller = mControllers.taskbarStashController; - controller.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE, isInStashedState); - Animator stashAnimator = controller.applyStateWithoutStart(duration); + TaskbarStashController stashController = mControllers.taskbarStashController; + stashController.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE, isInStashedState); + Animator stashAnimator = stashController.applyStateWithoutStart(duration); if (stashAnimator != null) { stashAnimator.addListener(new AnimatorListenerAdapter() { @Override @@ -362,51 +411,29 @@ import java.util.function.Supplier; @Override public void onAnimationStart(Animator animation) { if (mLauncher.getHotseat().getIconsAlpha() > 0) { - mIconAlphaForHome.setValue(mLauncher.getHotseat().getIconsAlpha()); + updateIconAlphaForHome(mLauncher.getHotseat().getIconsAlpha()); } } }); animatorSet.play(stashAnimator); } - - // If we're already animating to the value, just leave it be instead of restarting it. - if (!mIconAlignmentForLauncherState.isAnimatingToValue(toAlignment)) { - mIconAlignmentForLauncherState.finishAnimation(); - animatorSet.play(mIconAlignmentForLauncherState.animateToValue(toAlignment) - .setDuration(duration)); - } - } - - private boolean isResumed() { - return (mState & FLAG_RESUMED) != 0; } - private boolean isRecentsAnimationRunning() { - return (mState & FLAG_RECENTS_ANIMATION_RUNNING) != 0; + private boolean isInLauncher() { + return (mState & FLAGS_LAUNCHER) != 0; } - private void onIconAlignmentRatioChangedForStateTransition() { - if (!isResumed() && mTaskBarRecentsAnimationListener == null) { - return; - } - onIconAlignmentRatioChanged(this::getCurrentIconAlignmentRatioForLauncherState); - } - - private void onIconAlignmentRatioChangedForAppAndHomeTransition() { - onIconAlignmentRatioChanged(this::getCurrentIconAlignmentRatioBetweenAppAndHome); - } - - private void onIconAlignmentRatioChanged(Supplier<Float> alignmentSupplier) { - if (mControllers == null) { - return; - } - float alignment = alignmentSupplier.get(); + private void onIconAlignmentRatioChanged() { float currentValue = mIconAlphaForHome.getValue(); - boolean taskbarWillBeVisible = alignment < 1; + boolean taskbarWillBeVisible = mIconAlignment.value < 1; boolean firstFrameVisChanged = (taskbarWillBeVisible && Float.compare(currentValue, 1) != 0) || (!taskbarWillBeVisible && Float.compare(currentValue, 0) != 0); - updateIconAlignment(alignment); + 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) { @@ -416,25 +443,18 @@ import java.util.function.Supplier; } } - private void updateIconAlignment(float alignment) { - mControllers.taskbarViewController.setLauncherIconAlignment( - alignment, mLauncher.getDeviceProfile()); - - // Switch taskbar and hotseat in last frame - setTaskbarViewVisible(alignment < 1); - mControllers.navbarButtonsViewController.updateTaskbarAlignment(alignment); - } - - private float getCurrentIconAlignmentRatioBetweenAppAndHome() { - return Math.max(mIconAlignmentForResumedState.value, mIconAlignmentForGestureState.value); - } - - private float getCurrentIconAlignmentRatioForLauncherState() { - return mIconAlignmentForLauncherState.value; - } - - 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 @@ -459,17 +479,18 @@ import java.util.function.Supplier; private void endGestureStateOverride(boolean finishedToApp) { mCallbacks.removeListener(this); mTaskBarRecentsAnimationListener = null; + ((RecentsView) mLauncher.getOverviewPanel()).setTaskLaunchListener(null); // Update the resumed state immediately to ensure a seamless handoff boolean launcherResumed = !finishedToApp; 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(); } @@ -477,29 +498,24 @@ import java.util.function.Supplier; 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", - prefix, - mIconAlignmentForResumedState.value)); pw.println(String.format( - "%s\tmIconAlignmentForGestureState=%.2f", + "%s\tmIconAlignment=%.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( @@ -508,13 +524,9 @@ import java.util.function.Supplier; 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", - prefix, - mIsAnimatingToLauncherViaGesture)); - pw.println(String.format( - "%s\tmIsAnimatingToLauncherViaResume=%b", + "%s\tmIsAnimatingToLauncher=%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 06262c0a25..86e191151a 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java @@ -30,19 +30,23 @@ import android.content.res.Configuration; import android.hardware.display.DisplayManager; import android.net.Uri; import android.os.Handler; +import android.os.SystemProperties; import android.provider.Settings; +import android.util.Log; 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 +62,9 @@ import java.io.PrintWriter; */ 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 +85,7 @@ public class TaskbarManager { // 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 +136,11 @@ public class TaskbarManager { | 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,8 +157,8 @@ public class TaskbarManager { } 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)) { + mTaskbarActivityContext.updateDeviceProfile(dp, mNavMode); } mTaskbarActivityContext.onConfigurationChanged(configDiff); } @@ -159,12 +169,15 @@ public class TaskbarManager { @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 +192,9 @@ public class TaskbarManager { private void destroyExistingTaskbar() { if (mTaskbarActivityContext != null) { mTaskbarActivityContext.onDestroy(); - mTaskbarActivityContext = null; + if (!FLAG_HIDE_NAVBAR_WINDOW) { + mTaskbarActivityContext = null; + } } } @@ -210,8 +225,13 @@ public class TaskbarManager { return; } mActivity = activity; - mUnfoldProgressProvider.setSourceProvider(getUnfoldTransitionProgressProviderForActivity( - activity)); + UnfoldTransitionProgressProvider unfoldTransitionProgressProvider = + getUnfoldTransitionProgressProviderForActivity(activity); + if (unfoldTransitionProgressProvider == null) { + Log.e("b/261320823", "UnfoldTransitionProgressProvider null in setActivity. " + + "Unfold animation for launcher will not work."); + } + mUnfoldProgressProvider.setSourceProvider(unfoldTransitionProgressProvider); if (mTaskbarActivityContext != null) { mTaskbarActivityContext.setUIController( @@ -225,8 +245,8 @@ public class TaskbarManager { */ 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 +255,11 @@ public class TaskbarManager { * 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 +280,33 @@ public class TaskbarManager { } } - private void recreateTaskbar() { - destroyExistingTaskbar(); - - DeviceProfile dp = - mUserUnlocked ? LauncherAppState.getIDP(mContext).getDeviceProfile(mContext) : null; + /** + * This method is called multiple times (ex. initial init, then when user unlocks) in which case + * we fully want to destroy an existing taskbar and create a new one. + * In other case (folding/unfolding) we don't need to remove and add window. + */ + @VisibleForTesting + public void recreateTaskbar() { + DeviceProfile dp = mUserUnlocked ? + LauncherAppState.getIDP(mContext).getDeviceProfile(mContext) : null; - boolean isTaskBarEnabled = dp != null && dp.isTaskbarPresent; + destroyExistingTaskbar(); + 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 +320,12 @@ public class TaskbarManager { } } + public void onLongPressHomeEnabled(boolean assistantLongPressEnabled) { + if (mNavButtonController != null) { + mNavButtonController.setAssistantLongPressEnabled(assistantLongPressEnabled); + } + } + /** * Sets the flag indicating setup UI is visible */ @@ -301,6 +336,26 @@ public class TaskbarManager { } } + /** + * @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/TaskbarModelCallbacks.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java index 75881a31f2..5e670d2946 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java @@ -29,6 +29,7 @@ import com.android.launcher3.util.IntArray; import com.android.launcher3.util.IntSet; import com.android.launcher3.util.ItemInfoMatcher; import com.android.launcher3.util.LauncherBindableItemsContainer; +import com.android.quickstep.RecentsModel; import java.io.PrintWriter; import java.util.ArrayList; @@ -42,7 +43,7 @@ import java.util.function.Predicate; * Launcher model Callbacks for rendering taskbar. */ public class TaskbarModelCallbacks implements - BgDataModel.Callbacks, LauncherBindableItemsContainer { + BgDataModel.Callbacks, LauncherBindableItemsContainer, RecentsModel.RunningTasksListener { private final SparseArray<ItemInfo> mHotseatItems = new SparseArray<>(); private List<ItemInfo> mPredictedItems = Collections.emptyList(); @@ -61,6 +62,16 @@ public class TaskbarModelCallbacks implements public void init(TaskbarControllers controllers) { mControllers = controllers; + if (mControllers.taskbarRecentAppsController.isEnabled()) { + RecentsModel.INSTANCE.get(mContext).registerRunningTasksListener(this); + } + } + + /** + * Unregisters listeners in this class. + */ + public void unregisterListeners() { + RecentsModel.INSTANCE.get(mContext).unregisterRunningTasksListener(); } @Override @@ -185,6 +196,8 @@ public class TaskbarModelCallbacks implements isHotseatEmpty = false; } } + hotseatItemInfos = mControllers.taskbarRecentAppsController + .updateHotseatItemInfos(hotseatItemInfos); mContainer.updateHotseatItems(hotseatItemInfos); final boolean finalIsHotseatEmpty = isHotseatEmpty; @@ -196,6 +209,21 @@ public class TaskbarModelCallbacks implements } @Override + public void onRunningTasksChanged() { + updateRunningApps(); + } + + /** Called when there's a change in running apps to update the UI. */ + public void commitRunningAppsToUI() { + commitItemsToUI(); + } + + /** Call TaskbarRecentAppsController to update running apps with mHotseatItems. */ + public void updateRunningApps() { + mControllers.taskbarRecentAppsController.updateRunningApps(mHotseatItems); + } + + @Override public void bindDeepShortcutMap(HashMap<ComponentKey, Integer> deepShortcutMapCopy) { mControllers.taskbarPopupController.setDeepShortcutMap(deepShortcutMapCopy); } @@ -203,6 +231,7 @@ public class TaskbarModelCallbacks implements @Override public void bindAllApplications(AppInfo[] apps, int flags) { mControllers.taskbarAllAppsController.setApps(apps, flags); + mControllers.taskbarRecentAppsController.setApps(apps); } protected void dumpLogs(String prefix, PrintWriter pw) { diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java index 4ff0649440..5bb958a713 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java @@ -18,7 +18,6 @@ package com.android.launcher3.taskbar; import static com.android.internal.app.AssistUtils.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS; import static com.android.internal.app.AssistUtils.INVOCATION_TYPE_KEY; -import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_A11Y_BUTTON_LONGPRESS; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_A11Y_BUTTON_TAP; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_BACK_BUTTON_LONGPRESS; @@ -28,11 +27,14 @@ import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCH import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_IME_SWITCHER_BUTTON_TAP; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_OVERVIEW_BUTTON_LONGPRESS; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_OVERVIEW_BUTTON_TAP; +import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING; import android.os.Bundle; import android.os.Handler; import android.util.Log; +import android.view.HapticFeedbackConstants; +import android.view.View; import androidx.annotation.IntDef; import androidx.annotation.Nullable; @@ -41,7 +43,7 @@ import androidx.annotation.StringRes; import com.android.launcher3.R; import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.testing.TestLogging; -import com.android.launcher3.testing.TestProtocol; +import com.android.launcher3.testing.shared.TestProtocol; import com.android.quickstep.OverviewCommandHelper; import com.android.quickstep.SystemUiProxy; import com.android.quickstep.TaskUtils; @@ -65,14 +67,14 @@ public class TaskbarNavButtonController implements TaskbarControllers.LoggableTa private long mLastScreenPinLongPress; private boolean mScreenPinned; + private boolean mAssistantLongPressEnabled; @Override public void dumpLogs(String prefix, PrintWriter pw) { pw.println(prefix + "TaskbarNavButtonController:"); - pw.println(String.format( - "%s\tmLastScreenPinLongPress=%dms", prefix, mLastScreenPinLongPress)); - pw.println(String.format("%s\tmScreenPinned=%b", prefix, mScreenPinned)); + pw.println(prefix + "\tmLastScreenPinLongPress=" + mLastScreenPinLongPress); + pw.println(prefix + "\tmScreenPinned=" + mScreenPinned); } @Retention(RetentionPolicy.SOURCE) @@ -113,7 +115,9 @@ public class TaskbarNavButtonController implements TaskbarControllers.LoggableTa mHandler = handler; } - public void onButtonClick(@TaskbarButton int buttonType) { + public void onButtonClick(@TaskbarButton int buttonType, View view) { + // Provide the same haptic feedback that the system offers for virtual keys. + view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY); switch (buttonType) { case BUTTON_BACK: logEvent(LAUNCHER_TASKBAR_BACK_BUTTON_TAP); @@ -144,7 +148,9 @@ public class TaskbarNavButtonController implements TaskbarControllers.LoggableTa } } - public boolean onButtonLongClick(@TaskbarButton int buttonType) { + public boolean onButtonLongClick(@TaskbarButton int buttonType, View view) { + // Provide the same haptic feedback that the system offers for virtual keys. + view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY); switch (buttonType) { case BUTTON_HOME: logEvent(LAUNCHER_TASKBAR_HOME_BUTTON_LONGPRESS); @@ -246,6 +252,10 @@ public class TaskbarNavButtonController implements TaskbarControllers.LoggableTa 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"); @@ -284,7 +294,7 @@ public class TaskbarNavButtonController implements TaskbarControllers.LoggableTa } 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 7b4501a003..9b27c9d026 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.ShortcutUtil; 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 @@ public class TaskbarPopupController implements TaskbarControllers.LoggableTaskba // Initialized in init. private TaskbarControllers mControllers; + private boolean mHideSplitOptions; public TaskbarPopupController(TaskbarActivityContext context) { mContext = context; @@ -93,6 +101,10 @@ public class TaskbarPopupController implements TaskbarControllers.LoggableTaskba 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 @@ public class TaskbarPopupController implements TaskbarControllers.LoggableTaskba // 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 @@ public class TaskbarPopupController implements TaskbarControllers.LoggableTaskba @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 @@ public class TaskbarPopupController implements TaskbarControllers.LoggableTaskba 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 @@ public class TaskbarPopupController implements TaskbarControllers.LoggableTaskba mItemInfo.user), new Intent(), mPosition.stagePosition, - null); + null, + instanceIds.first); } } } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.java new file mode 100644 index 0000000000..8445cff0ee --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.java @@ -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.taskbar; + +import android.util.SparseArray; + +import androidx.annotation.CallSuper; + +import com.android.launcher3.model.data.AppInfo; +import com.android.launcher3.model.data.ItemInfo; + +/** + * Base class for providing recent apps functionality + */ +public class TaskbarRecentAppsController { + + public static final TaskbarRecentAppsController DEFAULT = new TaskbarRecentAppsController(); + + // Initialized in init. + protected TaskbarControllers mControllers; + + @CallSuper + protected void init(TaskbarControllers taskbarControllers) { + mControllers = taskbarControllers; + } + + @CallSuper + protected void onDestroy() { + mControllers = null; + } + + /** Stores the current {@link AppInfo} instances, no-op except in desktop environment. */ + protected void setApps(AppInfo[] apps) { } + + /** + * Indicates whether recent apps functionality is enabled, should return false except in + * desktop environment. + */ + protected boolean isEnabled() { + return false; + } + + /** Called to update hotseatItems, no-op except in desktop environment. */ + protected ItemInfo[] updateHotseatItemInfos(ItemInfo[] hotseatItems) { + return hotseatItems; + } + + /** Called to update the list of currently running apps, no-op except in desktop environment. */ + protected void updateRunningApps(SparseArray<ItemInfo> hotseatItems) { } +} diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimView.java index 1d3757fca2..cdc6d59bf1 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimView.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimView.java @@ -69,4 +69,13 @@ public class TaskbarScrimView extends View { 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 58ace17787..88767dd61d 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.animation.ObjectAnimator; 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 @@ import java.io.PrintWriter; /** * 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,9 +96,14 @@ public class TaskbarScrimViewController implements TaskbarControllers.LoggableTa } @Override + public void setCornerRoundness(float cornerRoundness) { + mScrimView.setCornerRoundness(cornerRoundness); + } + + @Override public void dumpLogs(String prefix, PrintWriter pw) { pw.println(prefix + "TaskbarScrimViewController:"); - pw.println(String.format("%s\tmScrimAlpha.value=%.2f", prefix, mScrimAlpha.value)); + pw.println(prefix + "\tmScrimAlpha.value=" + mScrimAlpha.value); } } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java index f131595126..c10b57ab87 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java @@ -17,24 +17,29 @@ package com.android.launcher3.taskbar; 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 class TaskbarShortcutMenuAccessibilityDelegate 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 @@ public class TaskbarShortcutMenuAccessibilityDelegate && (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 @@ public class TaskbarShortcutMenuAccessibilityDelegate 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 fc9f9d002b..c269648f7d 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java @@ -17,31 +17,47 @@ package com.android.launcher3.taskbar; 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.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 android.view.WindowInsets; +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.taskbar.allapps.TaskbarAllAppsSlideInView; -import com.android.launcher3.testing.TestProtocol; -import com.android.launcher3.util.MultiValueAlpha.AlphaProperty; -import com.android.quickstep.AnimatedFloat; +import com.android.launcher3.testing.shared.TestProtocol; +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.StringJoiner; @@ -53,23 +69,28 @@ import java.util.function.IntPredicate; */ 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; @@ -79,13 +100,13 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba // 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. @@ -123,6 +144,9 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba */ 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; @@ -135,11 +159,11 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba 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). */ @@ -149,8 +173,12 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba private @Nullable AnimatorSet mAnimator; private boolean mIsSystemGestureInProgress; 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( @@ -158,15 +186,32 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba 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) { @@ -177,24 +222,36 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba 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()); } @@ -204,23 +261,38 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba * 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; } /** @@ -258,7 +330,14 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba * 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) { @@ -282,18 +361,30 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba } /** + * Returns the height that taskbar will be touchable. + */ + public int getTouchableHeight() { + return mIsStashed ? mStashedHeight : mUnstashedHeight; + } + + /** * Returns the height that taskbar will inset when inside apps. * @see WindowInsets.Type#navigationBars() * @see WindowInsets.Type#systemBars() */ public int getContentHeightToReportToApps() { + if ((isPhoneMode() && !mActivity.isThreeButtonNav()) + || 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() @@ -306,6 +397,7 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba } return mStashedHeight; } + return mUnstashedHeight; } @@ -323,6 +415,20 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba } /** + * Stash or unstashes the transient taskbar. + */ + public void updateAndAnimateTransientTaskbar(boolean stash) { + if (!DisplayController.isTransientTaskbar(mActivity)) { + return; + } + + if (hasAnyFlag(FLAG_STASHED_IN_APP_AUTO) != stash) { + updateStateForFlag(FLAG_STASHED_IN_APP_AUTO, stash); + applyState(); + } + } + + /** * Should be called when long pressing the nav region when taskbar is present. * @return Whether taskbar was stashed and now is unstashed. */ @@ -384,7 +490,8 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba /* isStashed= */ false, placeholderDuration, /* startDelay= */ 0, - /* animateBg= */ false); + /* animateBg= */ false, + /* changedFlags=*/ 0); animation.play(mAnimator); } @@ -395,17 +502,25 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba * @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, long startDelay, + 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); @@ -426,10 +541,13 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba 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) { @@ -441,11 +559,17 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba 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; @@ -466,6 +590,11 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba secondHalfAnimatorSet.playTogether( mIconAlphaForStash.animateToValue(1) ); + + if (skipStashAnimation) { + fullLengthAnimatorSet.setInterpolator(FINAL_FRAME); + secondHalfAnimatorSet.setInterpolator(FINAL_FRAME); + } } fullLengthAnimatorSet.play(mControllers.stashedHandleViewController @@ -487,15 +616,37 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba 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) { + View v = mControllers.taskbarActivityContext.getDragLayer(); + int action = expanding ? InteractionJankMonitor.CUJ_TASKBAR_EXPAND : + InteractionJankMonitor.CUJ_TASKBAR_COLLAPSE; + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(@NonNull Animator animation) { + InteractionJankMonitor.getInstance().begin(v, action); + } + + @Override + public void onAnimationEnd(@NonNull Animator animation) { + InteractionJankMonitor.getInstance().end(action); + } + }); + } /** * Creates and starts a partial stash animation, hinting at the new state that will trigger when * long press is detected. @@ -571,33 +722,27 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba } // Only update the following flags when system gesture is not in progress. - maybeResetStashedInAppAllApps(hasAnyFlag(FLAG_STASHED_IN_APP_IME) == mIsImeShowing); - if (hasAnyFlag(FLAG_STASHED_IN_APP_IME) != mIsImeShowing) { - updateStateForFlag(FLAG_STASHED_IN_APP_IME, mIsImeShowing); + boolean shouldStashForIme = 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()); } } @@ -620,13 +765,20 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba 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, mIsImeShowing); + updateStateForFlag(FLAG_STASHED_IN_APP_IME, shouldStashForIme()); animDuration = TASKBAR_STASH_DURATION_FOR_IME; startDelay = getTaskbarStashStartDelayForIme(); } @@ -635,6 +787,22 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba } /** + * 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() { + if (DisplayController.isTransientTaskbar(mActivity)) { + return false; + } + return (mIsImeShowing || mIsImeSwitcherShowing) && + !(isPhoneMode() && mActivity.isThreeButtonNav() + && mActivity.getDeviceProfile().isLandscape); + } + + /** * Updates the proper flag to indicate whether the task bar should be stashed. * * Note that this only updates the flag. {@link #applyState()} needs to be called separately. @@ -686,39 +854,88 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba 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:"); - pw.println(String.format("%s\tmStashedHeight=%dpx", prefix, mStashedHeight)); - pw.println(String.format("%s\tmUnstashedHeight=%dpx", prefix, mUnstashedHeight)); - pw.println(String.format("%s\tmIsStashed=%b", prefix, mIsStashed)); - pw.println(String.format( - "%s\tappliedState=%s", prefix, getStateString(mStatePropertyHolder.mPrevFlags))); - pw.println(String.format("%s\tmState=%s", prefix, getStateString(mState))); - pw.println(String.format( - "%s\tmIsSystemGestureInProgress=%b", prefix, mIsSystemGestureInProgress)); - pw.println(String.format("%s\tmIsImeShowing=%b", prefix, mIsImeShowing)); + pw.println(prefix + "\tmStashedHeight=" + mStashedHeight); + pw.println(prefix + "\tmUnstashedHeight=" + mUnstashedHeight); + pw.println(prefix + "\tmIsStashed=" + mIsStashed); + pw.println(prefix + "\tappliedState=" + getStateString(mStatePropertyHolder.mPrevFlags)); + pw.println(prefix + "\tmState=" + getStateString(mState)); + pw.println(prefix + "\tmIsSystemGestureInProgress=" + mIsSystemGestureInProgress); + pw.println(prefix + "\tmIsImeShowing=" + mIsImeShowing); + pw.println(prefix + "\tmIsImeSwitcherShowing=" + mIsImeSwitcherShowing); } 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) { @@ -748,7 +965,13 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba 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", @@ -758,9 +981,11 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba start)); } mIsStashed = isStashed; + mIsHotseatIconOnTopWhenAligned = isHotseatIconOnTopWhenAligned; // This sets mAnimator. - createAnimToIsStashed(mIsStashed, duration, startDelay, /* animateBg= */ true); + createAnimToIsStashed( + mIsStashed, duration, startDelay, /* animateBg= */ true, changedFlags); if (start) { mAnimator.start(); } 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 0000000000..5dba444ada --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.taskbar; + +import static com.android.launcher3.anim.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) { + reset(); + } + })); + mSpringBounce.start(); + mHasSprungOnceThisGesture = true; + } + + private void reset() { + mGestureEnded = false; + mHasSprungOnceThisGesture = false; + } + + /** + * Returns a callback to help monitor the swipe gesture. + */ + public TransitionCallback getTransitionCallback() { + return mCallback; + } + + /** + * Returns an animation to reset the taskbar translation for animation back to launcher. + */ + public ObjectAnimator createAnimToLauncher(long duration) { + ObjectAnimator animator = ObjectAnimator.ofFloat(mTranslationYForSwipe, VALUE, 0); + animator.setInterpolator(Interpolators.LINEAR); + animator.setDuration(duration); + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + cancelAnimationIfExists(); + mAnimationToHomeRunning = true; + } + + @Override + public void onAnimationEnd(Animator animation) { + mAnimationToHomeRunning = false; + reset(); + } + }); + return animator; + } + + /** + * Helper class to communicate to/from the input consumer. + */ + public class TransitionCallback { + + /** + * Called when there is movement to move the taskbar. + */ + public void onActionMove(float dY) { + if (mAnimationToHomeRunning + || (mHasSprungOnceThisGesture && !mGestureEnded)) { + return; + } + + mTranslationYForSwipe.updateValue(dY); + } + + /** + * Called when swipe gesture has ended. + */ + public void onActionEnd() { + if (mHasSprungOnceThisGesture) { + reset(); + } else { + mGestureEnded = true; + startSpring(); + } + } + } + + @Override + public void dumpLogs(String prefix, PrintWriter pw) { + pw.println(prefix + "TaskbarTranslationController:"); + + pw.println(prefix + "\tmTranslationYForSwipe=" + mTranslationYForSwipe.value); + pw.println(prefix + "\tmHasSprungOnceThisGesture=" + mHasSprungOnceThisGesture); + pw.println(prefix + "\tmAnimationToHomeRunning=" + mAnimationToHomeRunning); + pw.println(prefix + "\tmGestureEnded=" + mGestureEnded); + pw.println(prefix + "\tmSpringBounce is running=" + (mSpringBounce != null + && mSpringBounce.isRunning())); + } +} + diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java index fcc34c6d99..7b0374614c 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 @@ public class TaskbarUIController { 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 @@ public class TaskbarUIController { } /** - * 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 @@ public class TaskbarUIController { } } + /** + * 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 @@ public class TaskbarUIController { 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.findLastActiveTaskAndDoSplitOperation( + 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 64a4fa79df..4c937a7f9b 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 class TaskbarUnfoldAnimationController implements 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 6f88d649fb..2fcd64b5e6 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java @@ -16,13 +16,14 @@ 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; import android.util.AttributeSet; +import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; -import android.view.ViewGroup; import android.widget.FrameLayout; import androidx.annotation.LayoutRes; @@ -31,6 +32,7 @@ import androidx.annotation.Nullable; import androidx.core.graphics.ColorUtils; import com.android.launcher3.BubbleTextView; +import com.android.launcher3.DeviceProfile; import com.android.launcher3.Insettable; import com.android.launcher3.R; import com.android.launcher3.Utilities; @@ -40,10 +42,9 @@ import com.android.launcher3.icons.ThemedIconDrawable; 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.uioverrides.ApiWrapper; +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; @@ -52,15 +53,17 @@ import java.util.function.Predicate; * 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; @@ -76,7 +79,9 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar 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; public TaskbarView(@NonNull Context context) { this(context, null); @@ -95,13 +100,16 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar 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; @@ -112,11 +120,18 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar 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) { @@ -166,6 +181,8 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar if (mAllAppsButton != null) { removeView(mAllAppsButton); } + removeView(mQsb); + for (int i = 0; i < hotseatItemInfos.length; i++) { ItemInfo hotseatItemInfo = hotseatItemInfos[i]; @@ -239,9 +256,14 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar } if (mAllAppsButton != null) { - int index = Utilities.isRtl(getResources()) ? 0 : getChildCount(); + int index = mIsRtl ? getChildCount() : 0; addView(mAllAppsButton, index); } + if (mActivityContext.getDeviceProfile().isQsbInline) { + addView(mQsb, mIsRtl ? getChildCount() : 0); + // Always set QSB to invisible after re-adding. + mQsb.setVisibility(View.INVISIBLE); + } mThemeIconsBackground = calculateThemeIconsBackground(); setThemedIconsBackgroundColor(mThemeIconsBackground); @@ -273,8 +295,9 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { int count = getChildCount(); - int spaceNeeded = count * (mItemMarginLeftRight * 2 + mIconTouchSize); - int navSpaceNeeded = ApiWrapper.getHotseatEndOffset(getContext()); + DeviceProfile deviceProfile = mActivityContext.getDeviceProfile(); + int spaceNeeded = getIconLayoutWidth(); + int navSpaceNeeded = deviceProfile.hotseatBarEndOffset; boolean layoutRtl = isLayoutRtl(); int iconEnd = right - (right - left - spaceNeeded) / 2; boolean needMoreSpaceForNav = layoutRtl ? @@ -292,10 +315,25 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar mIconLayoutBounds.bottom = mIconLayoutBounds.top + mIconTouchSize; for (int i = count; i > 0; i--) { View child = getChildAt(i - 1); - iconEnd -= mItemMarginLeftRight; - int iconStart = iconEnd - mIconTouchSize; - child.layout(iconStart, mIconLayoutBounds.top, iconEnd, mIconLayoutBounds.bottom); - iconEnd = iconStart - mItemMarginLeftRight; + if (child == mQsb) { + int qsbStart; + int qsbEnd; + if (layoutRtl) { + qsbStart = iconEnd + mItemMarginLeftRight; + qsbEnd = qsbStart + deviceProfile.hotseatQsbWidth; + } else { + qsbEnd = iconEnd - mItemMarginLeftRight; + qsbStart = qsbEnd - deviceProfile.hotseatQsbWidth; + } + int qsbTop = (bottom - top - deviceProfile.hotseatQsbHeight) / 2; + int qsbBottom = qsbTop + deviceProfile.hotseatQsbHeight; + child.layout(qsbStart, qsbTop, qsbEnd, qsbBottom); + } else { + iconEnd -= mItemMarginLeftRight; + int iconStart = iconEnd - mIconTouchSize; + child.layout(iconStart, mIconLayoutBounds.top, iconEnd, mIconLayoutBounds.bottom); + iconEnd = iconStart - mItemMarginLeftRight; + } } mIconLayoutBounds.left = iconEnd; } @@ -309,12 +347,22 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar } @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)) { @@ -331,6 +379,7 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar public void setTouchesEnabled(boolean touchEnabled) { this.mTouchEnabled = touchEnabled; + mControllerCallbacks.clearTouchInProgress(); } /** @@ -349,6 +398,18 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar } /** + * 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() { @@ -363,10 +424,18 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar /** * Returns the all apps button in the taskbar. */ + @Nullable public View getAllAppsButtonView() { return mAllAppsButton; } + /** + * Returns the QSB in the taskbar. + */ + public View getQsb() { + return mQsb; + } + // FolderIconParent implemented methods. @Override diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java index 3562f5bc3c..168c353273 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java @@ -16,10 +16,16 @@ package com.android.launcher3.taskbar; 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; @@ -27,24 +33,33 @@ import android.util.FloatProperty; 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; import com.android.launcher3.config.FeatureFlags; 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; @@ -63,7 +78,10 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar public static final int ALPHA_INDEX_STASH = 2; public static final int ALPHA_INDEX_RECENTS_DISABLED = 3; public static final int ALPHA_INDEX_NOTIFICATION_EXPANDED = 4; - private static final int NUM_ALPHA_CHANNELS = 5; + public static final int ALPHA_INDEX_ASSISTANT_INVOKED = 5; + public static final int ALPHA_INDEX_IME_BUTTON_NAV = 6; + 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; @@ -75,12 +93,18 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar 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; @@ -90,6 +114,10 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar 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; @@ -97,12 +125,42 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar 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); @@ -116,17 +174,21 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar controllers.navbarButtonsViewController.getTaskbarNavButtonTranslationY(); mTaskbarNavButtonTranslationYForInAppDisplay = controllers.navbarButtonsViewController .getTaskbarNavButtonTranslationYForInAppDisplay(); + + mActivity.addOnDeviceProfileChangeListener(mDeviceProfileChangeListener); } public void onDestroy() { LauncherAppState.getInstance(mActivity).getModel().removeCallbacks(mModelCallbacks); + mActivity.removeOnDeviceProfileChangeListener(mDeviceProfileChangeListener); + mModelCallbacks.unregisterListeners(); } public boolean areIconsVisible() { return mTaskbarView.areIconsVisible(); } - public MultiValueAlpha getTaskbarIconAlpha() { + public MultiPropertyFactory<View> getTaskbarIconAlpha() { return mTaskbarIconAlpha; } @@ -134,7 +196,16 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar * 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.get(ALPHA_INDEX_IME_BUTTON_NAV).setValue( + isImeSwitcherVisible ? 0 : 1); } /** @@ -142,7 +213,7 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar */ 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); } /** @@ -165,10 +236,15 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar return mTaskbarView.getIconLayoutBounds(); } + public int getIconLayoutWidth() { + return mTaskbarView.getIconLayoutWidth(); + } + public View[] getIconViews() { return mTaskbarView.getIconViews(); } + @Nullable public View getAllAppsButtonView() { return mTaskbarView.getAllAppsButtonView(); } @@ -190,9 +266,18 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar 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() { @@ -211,7 +296,12 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar * 1 => fully aligned */ public void setLauncherIconAlignment(float alignmentRatio, DeviceProfile launcherDp) { - if (mIconAlignControllerLazy == null) { + 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); @@ -227,44 +317,92 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar private AnimatorPlaybackController createIconAlignmentController(DeviceProfile launcherDp) { mOnControllerPreCreateCallback.run(); PendingAnimation setter = new PendingAnimation(100); + DeviceProfile taskbarDp = mActivity.getDeviceProfile(); Rect hotseatPadding = launcherDp.getHotseatLayoutPadding(mActivity); - float scaleUp = ((float) launcherDp.iconSizePx) / mActivity.getDeviceProfile().iconSizePx; + float scaleUp = ((float) launcherDp.iconSizePx) / taskbarDp.iconSizePx; int borderSpacing = launcherDp.hotseatBorderSpace; int hotseatCellSize = DeviceProfile.calculateCellWidth( launcherDp.availableWidthPx - hotseatPadding.left - hotseatPadding.right, 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); } int collapsedHeight = mActivity.getDefaultTaskbarWindowHeight(); - int expandedHeight = Math.max(collapsedHeight, - mActivity.getDeviceProfile().taskbarSize + offsetY); + int expandedHeight = Math.max(collapsedHeight, taskbarDp.taskbarSize + offsetY); setter.addOnFrameListener(anim -> mActivity.setTaskbarWindowHeight( anim.getAnimatedFraction() > 0 ? expandedHeight : collapsedHeight)); 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()) { + 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 (child == mTaskbarView.getQsb()) { + boolean isRtl = Utilities.isRtl(child.getResources()); + float hotseatIconCenter = isRtl + ? launcherDp.widthPx - hotseatPadding.right + borderSpacing + + launcherDp.hotseatQsbWidth / 2f + : hotseatPadding.left - borderSpacing - launcherDp.hotseatQsbWidth / 2f; + float childCenter = (child.getLeft() + child.getRight()) / 2f; + float halfQsbIconWidthDiff = + (launcherDp.hotseatQsbWidth - taskbarDp.iconSizePx) / 2f; + setter.addFloat(child, ICON_TRANSLATE_X, + isRtl ? -halfQsbIconWidthDiff : halfQsbIconWidthDiff, + hotseatIconCenter - childCenter, interpolator); + + float scale = ((float) taskbarDp.iconSizePx) / launcherDp.hotseatQsbVisualHeight; + setter.addFloat(child, SCALE_PROPERTY, scale, 1f, interpolator); + + 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.hotseatQsbWidth; + if (child instanceof HorizontalInsettableView) { + setter.addFloat((HorizontalInsettableView) child, + HorizontalInsettableView.HORIZONTAL_INSETS, qsbInsetFraction, 0, + 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()) - ? -1 - : mActivity.getDeviceProfile().numShownHotseatIcons; - - if (!FeatureFlags.ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT.get()) { - setter.setViewAlpha(child, 0, LINEAR); - } + ? taskbarDp.numShownHotseatIcons + : -1; } else if (child.getTag() instanceof ItemInfo) { positionInHotseat = ((ItemInfo) child.getTag()).screenId; } else { @@ -277,9 +415,11 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar + 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, VIEW_TRANSLATE_Y, mTaskbarBottomMargin, interpolator); - setter.setFloat(child, SCALE_PROPERTY, scaleUp, LINEAR); + setter.setFloat(child, SCALE_PROPERTY, scaleUp, interpolator); } AnimatorPlaybackController controller = setter.createPlaybackController(); @@ -288,8 +428,8 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar } 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( @@ -326,9 +466,33 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar @Override public void dumpLogs(String prefix, PrintWriter pw) { pw.println(prefix + "TaskbarViewController:"); + + mTaskbarIconAlpha.dump( + prefix + "\t", + pw, + "mTaskbarIconAlpha", + "ALPHA_INDEX_HOME", + "ALPHA_INDEX_KEYGUARD", + "ALPHA_INDEX_STASH", + "ALPHA_INDEX_RECENTS_DISABLED", + "ALPHA_INDEX_NOTIFICATION_EXPANDED", + "ALPHA_INDEX_ASSISTANT_INVOKED", + "ALPHA_INDEX_IME_BUTTON_NAV", + "ALPHA_INDEX_SMALL_SCREEN"); + mModelCallbacks.dumpLogs(prefix + "\t", pw); } + /** Called when there's a change in running apps to update the UI. */ + public void commitRunningAppsToUI() { + mModelCallbacks.commitRunningAppsToUI(); + } + + /** Call TaskbarModelCallbacks to update running apps. */ + public void updateRunningApps() { + mModelCallbacks.updateRunningApps(); + } + /** * Callbacks for {@link TaskbarView} to interact with its controller. */ @@ -338,6 +502,8 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar private float mDownX, mDownY; private boolean mCanceledStashHint; + private boolean mTouchInProgress; + public View.OnClickListener getIconOnClickListener() { return mActivity.getItemOnClickListener(); } @@ -359,37 +525,75 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar } /** + * 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 new file mode 100644 index 0000000000..a033507b8e --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt @@ -0,0 +1,124 @@ +package com.android.launcher3.taskbar + +import android.graphics.Canvas +import android.view.WindowManager +import android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY +import com.android.launcher3.views.BaseDragLayer +import com.android.systemui.animation.ViewRootSync +import java.io.PrintWriter + +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, + TaskbarControllers.BackgroundRendererController { + + private val taskbarBackgroundRenderer = TaskbarBackgroundRenderer(context) + + // Initialized in init. + private lateinit var controllers: TaskbarControllers + private lateinit var separateWindowForTaskbarBackground: BaseDragLayer<TaskbarActivityContext> + private lateinit var separateWindowLayoutParams: WindowManager.LayoutParams + + private var isVoiceInteractionWindowVisible: Boolean = false + + fun init(controllers: TaskbarControllers) { + this.controllers = controllers + + separateWindowForTaskbarBackground = + object : BaseDragLayer<TaskbarActivityContext>(context, null, 0) { + override fun recreateControllers() { + mControllers = emptyArray() + } + + override fun draw(canvas: Canvas) { + super.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.isSystemApplicationOverlay = true + } + + fun onDestroy() { + setIsVoiceInteractionWindowVisible(visible = false, skipAnim = true) + } + + fun setIsVoiceInteractionWindowVisible(visible: Boolean, skipAnim: Boolean) { + if (isVoiceInteractionWindowVisible == visible) { + return + } + isVoiceInteractionWindowVisible = visible + + // Fade out taskbar icons and stashed handle. + val taskbarIconAlpha = if (isVoiceInteractionWindowVisible) 0f else 1f + 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) { + fadeTaskbarIcons.end() + fadeStashedHandle.end() + } + + 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 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 { + 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 51fa4d9f3a..eeca329e80 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 @@ package com.android.launcher3.taskbar.allapps; 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 @@ public class TaskbarAllAppsContainerView extends } @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 e2f752212e..0000000000 --- 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.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 6fd98db19a..7a34869e2a 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 @@ import java.util.Optional; */ 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 @@ public final class TaskbarAllAppsController { 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 @@ public final class TaskbarAllAppsController { } mPredictedApps = predictedApps; - if (mAllAppsContext != null) { - mAllAppsContext.getAppsView().getFloatingHeaderView() + if (mAppsView != null) { + mAppsView.getFloatingHeaderView() .findFixedRowByType(PredictionRowView.class) .setPredictedApps(mPredictedApps); } @@ -135,123 +108,49 @@ public final class TaskbarAllAppsController { 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); - } - - /** 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 (AbstractFloatingView.getOpenView(mAllAppsContext, TYPE_ALL) != null - || 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(); + // 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); } - /** 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.removeView(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, - 0, - 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 c4837a0c51..8502752652 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 android.view.animation.Interpolator; 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 @@ public class TaskbarAllAppsSlideInView extends AbstractSlideInView<TaskbarAllApp 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 @@ public class TaskbarAllAppsSlideInView extends AbstractSlideInView<TaskbarAllApp 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 @@ public class TaskbarAllAppsSlideInView extends AbstractSlideInView<TaskbarAllApp 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 a39e872a8d..7a3b3e8c7b 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 @@ import com.android.launcher3.taskbar.TaskbarStashController; */ 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 @@ final class TaskbarAllAppsViewController { } 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 0000000000..68ea27a8ec --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.launcher3.taskbar.navbutton + +import android.content.res.Resources +import android.view.ViewGroup +import android.widget.ImageView +import android.widget.LinearLayout +import com.android.launcher3.R +import com.android.launcher3.taskbar.navbutton.NavButtonLayoutFactory.NavButtonLayoutter + +/** + * Meant to be a simple container for data subclasses will need + * + * Assumes that the 3 navigation buttons (back/home/recents) have already been added to + * [navButtonContainer] + * + * @property navButtonContainer ViewGroup that holds the 3 navigation buttons. + * @property endContextualContainer ViewGroup that holds the end contextual button (ex, IME dismiss). + * @property startContextualContainer ViewGroup that holds the start contextual button (ex, A11y). + */ +abstract class AbstractNavButtonLayoutter( + val resources: Resources, + val navButtonContainer: LinearLayout, + protected val endContextualContainer: ViewGroup, + protected val startContextualContainer: ViewGroup +) : NavButtonLayoutter { + protected val homeButton: ImageView = navButtonContainer + .findViewById(R.id.home) + protected val recentsButton: ImageView = navButtonContainer + .findViewById(R.id.recent_apps) + protected val backButton: ImageView = navButtonContainer + .findViewById(R.id.back) +} + diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/KidsNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/KidsNavLayoutter.kt new file mode 100644 index 0000000000..c67ab7964e --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/KidsNavLayoutter.kt @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.launcher3.taskbar.navbutton + +import android.content.res.Resources +import android.graphics.Color +import android.graphics.drawable.PaintDrawable +import android.view.Gravity +import android.view.ViewGroup +import android.widget.FrameLayout +import android.widget.ImageView +import android.widget.LinearLayout +import com.android.launcher3.DeviceProfile +import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.DIMEN_TASKBAR_BACK_BUTTON_LEFT_MARGIN_KIDS +import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.DIMEN_TASKBAR_HOME_BUTTON_LEFT_MARGIN_KIDS +import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.DIMEN_TASKBAR_ICON_SIZE_KIDS +import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.DIMEN_TASKBAR_NAV_BUTTONS_CORNER_RADIUS_KIDS +import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.DIMEN_TASKBAR_NAV_BUTTONS_HEIGHT_KIDS +import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.DIMEN_TASKBAR_NAV_BUTTONS_WIDTH_KIDS +import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.DRAWABLE_SYSBAR_BACK_KIDS +import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.DRAWABLE_SYSBAR_HOME_KIDS + +class KidsNavLayoutter( + resources: Resources, + navBarContainer: LinearLayout, + endContextualContainer: ViewGroup, + startContextualContainer: ViewGroup +) : AbstractNavButtonLayoutter( + resources, + navBarContainer, + endContextualContainer, + startContextualContainer +) { + + override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) { + val iconSize: Int = resources.getDimensionPixelSize( + DIMEN_TASKBAR_ICON_SIZE_KIDS) + val buttonWidth: Int = resources.getDimensionPixelSize( + DIMEN_TASKBAR_NAV_BUTTONS_WIDTH_KIDS) + val buttonHeight: Int = resources.getDimensionPixelSize( + DIMEN_TASKBAR_NAV_BUTTONS_HEIGHT_KIDS) + val buttonRadius: Int = resources.getDimensionPixelSize( + DIMEN_TASKBAR_NAV_BUTTONS_CORNER_RADIUS_KIDS) + val paddingLeft = (buttonWidth - iconSize) / 2 + val paddingTop = (buttonHeight - iconSize) / 2 + + // Update icons + backButton.setImageDrawable( + backButton.context.getDrawable(DRAWABLE_SYSBAR_BACK_KIDS)) + backButton.scaleType = ImageView.ScaleType.FIT_CENTER + backButton.setPadding(paddingLeft, paddingTop, paddingLeft, paddingTop) + homeButton.setImageDrawable( + homeButton.getContext().getDrawable(DRAWABLE_SYSBAR_HOME_KIDS)) + homeButton.scaleType = ImageView.ScaleType.FIT_CENTER + homeButton.setPadding(paddingLeft, paddingTop, paddingLeft, paddingTop) + + // Home button layout + val homeLayoutparams = LinearLayout.LayoutParams( + buttonWidth, + buttonHeight + ) + val homeButtonLeftMargin: Int = resources.getDimensionPixelSize( + DIMEN_TASKBAR_HOME_BUTTON_LEFT_MARGIN_KIDS) + homeLayoutparams.setMargins(homeButtonLeftMargin, 0, 0, 0) + homeButton.layoutParams = homeLayoutparams + + // Back button layout + val backLayoutParams = LinearLayout.LayoutParams( + buttonWidth, + buttonHeight + ) + val backButtonLeftMargin: Int = resources.getDimensionPixelSize( + DIMEN_TASKBAR_BACK_BUTTON_LEFT_MARGIN_KIDS) + backLayoutParams.setMargins(backButtonLeftMargin, 0, 0, 0) + backButton.layoutParams = backLayoutParams + + // Button backgrounds + val whiteWith10PctAlpha = Color.argb(0.1f, 1f, 1f, 1f) + val buttonBackground = PaintDrawable(whiteWith10PctAlpha) + buttonBackground.setCornerRadius(buttonRadius.toFloat()) + homeButton.background = buttonBackground + backButton.background = buttonBackground + + // Update alignment within taskbar + val navButtonsLayoutParams = navButtonContainer.layoutParams as FrameLayout.LayoutParams + navButtonsLayoutParams.apply { + marginStart = navButtonsLayoutParams.marginEnd / 2 + marginEnd = navButtonsLayoutParams.marginStart + gravity = Gravity.CENTER + } + navButtonContainer.requestLayout() + + homeButton.onLongClickListener = null + } +}
\ No newline at end of file diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/LayoutResourceHelper.java b/quickstep/src/com/android/launcher3/taskbar/navbutton/LayoutResourceHelper.java new file mode 100644 index 0000000000..0d9b855c91 --- /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 0000000000..db0a2d8d44 --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.launcher3.taskbar.navbutton + +import android.content.res.Resources +import android.view.ViewGroup +import android.widget.FrameLayout +import android.widget.LinearLayout +import com.android.launcher3.DeviceProfile +import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.ID_END_CONTEXTUAL_BUTTONS +import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.ID_END_NAV_BUTTONS +import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.ID_START_CONTEXTUAL_BUTTONS +import com.android.launcher3.taskbar.navbutton.NavButtonLayoutFactory.Companion +import com.android.launcher3.taskbar.navbutton.NavButtonLayoutFactory.NavButtonLayoutter + +/** + * Select the correct layout for nav buttons + * + * Since layouts are done dynamically for the nav buttons on Taskbar, this + * class returns a corresponding [NavButtonLayoutter] via + * [Companion.getUiLayoutter] + * that can help position the buttons based on the current [DeviceProfile] + */ +class NavButtonLayoutFactory { + companion object { + /** + * Get the correct instance of [NavButtonLayoutter] + * + * No layouts supported for configurations where: + * * taskbar isn't showing AND + * * the device is not in [phoneMode] + * OR + * * phone is showing + * * device is using gesture navigation + * + * @param navButtonsView ViewGroup that contains start, end, nav button ViewGroups + * @param isKidsMode no-op when taskbar is hidden/not showing + * @param isInSetup no-op when taskbar is hidden/not showing + * @param phoneMode refers to the device using the taskbar window on phones + * @param isThreeButtonNav are no-ops when taskbar is present/showing + */ + fun getUiLayoutter(deviceProfile: DeviceProfile, + navButtonsView: FrameLayout, + resources: Resources, + isKidsMode: Boolean, + isInSetup: Boolean, + isThreeButtonNav: Boolean, + phoneMode: Boolean): + NavButtonLayoutter { + val navButtonContainer = + navButtonsView.findViewById<LinearLayout>(ID_END_NAV_BUTTONS) + val endContextualContainer = + navButtonsView.findViewById<ViewGroup>(ID_END_CONTEXTUAL_BUTTONS) + val startContextualContainer = + navButtonsView.findViewById<ViewGroup>(ID_START_CONTEXTUAL_BUTTONS) + val isPhoneNavMode = phoneMode && isThreeButtonNav + return when { + isPhoneNavMode -> { + if (!deviceProfile.isLandscape) { + PhonePortraitNavLayoutter(resources, navButtonContainer, + endContextualContainer, startContextualContainer) + } else { + PhoneLandscapeNavLayoutter(resources, navButtonContainer, + endContextualContainer, startContextualContainer) + } + } + deviceProfile.isTaskbarPresent -> { + return when { + isInSetup -> { + SetupNavLayoutter(resources, navButtonContainer, endContextualContainer, + startContextualContainer) + } + isKidsMode -> { + KidsNavLayoutter(resources, navButtonContainer, endContextualContainer, + startContextualContainer) + } + else -> + TaskbarNavLayoutter(resources, navButtonContainer, endContextualContainer, + startContextualContainer) + } + } + else -> error("No layoutter found") + } + } + } + + /** Lays out and provides access to the home, recents, and back buttons for various mischief */ + interface NavButtonLayoutter { + fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) + } +}
\ No newline at end of file diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt new file mode 100644 index 0000000000..a89476e15a --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.launcher3.taskbar.navbutton + +import android.content.res.Resources +import android.view.Gravity +import android.view.ViewGroup +import android.widget.FrameLayout +import android.widget.LinearLayout +import androidx.core.view.children +import com.android.launcher3.DeviceProfile +import com.android.launcher3.R +import com.android.launcher3.taskbar.TaskbarManager +import com.android.launcher3.util.DimensionUtils + +class PhoneLandscapeNavLayoutter( + resources: Resources, + navBarContainer: LinearLayout, + endContextualContainer: ViewGroup, + startContextualContainer: ViewGroup +) : AbstractNavButtonLayoutter( + resources, + navBarContainer, + endContextualContainer, + startContextualContainer +) { + + override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) { + // TODO(b/230395757): Polish pending, this is just to make it usable + val navContainerParams = navButtonContainer.layoutParams as FrameLayout.LayoutParams + val endStartMargins = + resources.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size) + val taskbarDimensions = DimensionUtils.getTaskbarPhoneDimensions(dp, resources, + TaskbarManager.isPhoneMode(dp)) + navButtonContainer.removeAllViews() + navButtonContainer.orientation = LinearLayout.VERTICAL + + navContainerParams.apply { + width = taskbarDimensions.x + height = ViewGroup.LayoutParams.MATCH_PARENT + gravity = Gravity.CENTER + topMargin = endStartMargins + bottomMargin = endStartMargins + marginEnd = 0 + marginStart = 0 + } + + // Swap recents and back button + navButtonContainer.addView(recentsButton) + navButtonContainer.addView(homeButton) + navButtonContainer.addView(backButton) + + navButtonContainer.layoutParams = navContainerParams + + // Add the spaces in between the nav buttons + val spaceInBetween: Int = + resources.getDimensionPixelSize(R.dimen.taskbar_button_space_inbetween_phone) + navButtonContainer.children.forEachIndexed { i, navButton -> + val buttonLayoutParams = navButton.layoutParams as LinearLayout.LayoutParams + buttonLayoutParams.weight = 1f + when (i) { + 0 -> { + buttonLayoutParams.bottomMargin = spaceInBetween / 2 + } + navButtonContainer.childCount - 1 -> { + buttonLayoutParams.topMargin = spaceInBetween / 2 + } + else -> { + buttonLayoutParams.bottomMargin = spaceInBetween / 2 + buttonLayoutParams.topMargin = spaceInBetween / 2 + } + } + } + } +}
\ No newline at end of file diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt new file mode 100644 index 0000000000..275f59faef --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.launcher3.taskbar.navbutton + +import android.content.res.Resources +import android.view.Gravity +import android.view.ViewGroup +import android.widget.FrameLayout +import android.widget.LinearLayout +import com.android.launcher3.DeviceProfile +import com.android.launcher3.R +import com.android.launcher3.taskbar.TaskbarManager +import com.android.launcher3.util.DimensionUtils + +class PhonePortraitNavLayoutter(resources: Resources, navBarContainer: LinearLayout, + endContextualContainer: ViewGroup, + startContextualContainer: ViewGroup) : + AbstractNavButtonLayoutter(resources, navBarContainer, endContextualContainer, + startContextualContainer) { + + override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) { + // TODO(b/230395757): Polish pending, this is just to make it usable + val navContainerParams = navButtonContainer.layoutParams as FrameLayout.LayoutParams + val taskbarDimensions = DimensionUtils.getTaskbarPhoneDimensions(dp, resources, + TaskbarManager.isPhoneMode(dp)) + val endStartMargins = resources.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size) + navContainerParams.width = taskbarDimensions.x + navContainerParams.height = ViewGroup.LayoutParams.MATCH_PARENT + navContainerParams.gravity = Gravity.CENTER_VERTICAL + + // Ensure order of buttons is correct + navButtonContainer.removeAllViews() + navButtonContainer.orientation = LinearLayout.HORIZONTAL + navContainerParams.topMargin = 0 + navContainerParams.bottomMargin = 0 + navContainerParams.marginEnd = endStartMargins + navContainerParams.marginStart = endStartMargins + // Swap recents and back button in case we were landscape prior to this + navButtonContainer.addView(backButton) + navButtonContainer.addView(homeButton) + navButtonContainer.addView(recentsButton) + + navButtonContainer.layoutParams = navContainerParams + + // Add the spaces in between the nav buttons + val spaceInBetween = + resources.getDimensionPixelSize(R.dimen.taskbar_button_space_inbetween_phone) + for (i in 0 until navButtonContainer.childCount) { + val navButton = navButtonContainer.getChildAt(i) + val buttonLayoutParams = navButton.layoutParams as LinearLayout.LayoutParams + buttonLayoutParams.weight = 1f + when (i) { + 0 -> { + // First button + buttonLayoutParams.marginEnd = spaceInBetween / 2 + } + navButtonContainer.childCount - 1 -> { + // Last button + buttonLayoutParams.marginStart = spaceInBetween / 2 + } + else -> { + // other buttons + buttonLayoutParams.marginStart = spaceInBetween / 2 + buttonLayoutParams.marginEnd = spaceInBetween / 2 + } + } + } + } +}
\ No newline at end of file diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt new file mode 100644 index 0000000000..afe70d6c21 --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.launcher3.taskbar.navbutton + +import android.content.res.Resources +import android.view.Gravity +import android.view.ViewGroup +import android.widget.FrameLayout +import android.widget.LinearLayout +import com.android.launcher3.DeviceProfile + +class SetupNavLayoutter( + resources: Resources, + navButtonContainer: LinearLayout, + endContextualContainer: ViewGroup, + startContextualContainer: ViewGroup +) : AbstractNavButtonLayoutter( + resources, + navButtonContainer, + endContextualContainer, + startContextualContainer +) { + + override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) { + // Since setup wizard only has back button enabled, it looks strange to be + // end-aligned, so start-align instead. + val navButtonsLayoutParams = navButtonContainer.layoutParams as FrameLayout.LayoutParams + navButtonsLayoutParams.apply { + marginStart = navButtonsLayoutParams.marginEnd + marginEnd = 0 + gravity = Gravity.START + } + navButtonContainer.requestLayout() + } +}
\ No newline at end of file diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt new file mode 100644 index 0000000000..b2ca2afa04 --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.launcher3.taskbar.navbutton + +import android.content.res.Resources +import android.view.Gravity +import android.view.ViewGroup +import android.widget.FrameLayout +import android.widget.LinearLayout +import com.android.launcher3.DeviceProfile +import com.android.launcher3.R + +/** + * Layoutter for showing 3 button navigation on large screen + */ +class TaskbarNavLayoutter( + resources: Resources, + navBarContainer: LinearLayout, + endContextualContainer: ViewGroup, + startContextualContainer: ViewGroup +) : AbstractNavButtonLayoutter( + resources, + navBarContainer, + endContextualContainer, + startContextualContainer +) { + + override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) { + // Add spacing after the end of the last nav button + val navButtonParams = navButtonContainer.layoutParams as FrameLayout.LayoutParams + var navMarginEnd = resources.getDimension(dp.inv.inlineNavButtonsEndSpacing).toInt() + val contextualWidth = endContextualContainer.width + // If contextual buttons are showing, we check if the end margin is enough for the + // contextual button to be showing - if not, move the nav buttons over a smidge + if (isContextualButtonShowing && navMarginEnd < contextualWidth) { + // Additional spacing, eat up half of space between last icon and nav button + navMarginEnd += resources.getDimensionPixelSize(R.dimen.taskbar_hotseat_nav_spacing) / 2 + } + + navButtonParams.apply { + gravity = Gravity.END + width = FrameLayout.LayoutParams.WRAP_CONTENT + height = ViewGroup.LayoutParams.MATCH_PARENT + marginEnd = navMarginEnd + } + navButtonContainer.orientation = LinearLayout.HORIZONTAL + navButtonContainer.layoutParams = navButtonParams + + // Add the spaces in between the nav buttons + val spaceInBetween = resources.getDimensionPixelSize(R.dimen.taskbar_button_space_inbetween) + for (i in 0 until navButtonContainer.childCount) { + val navButton = navButtonContainer.getChildAt(i) + val buttonLayoutParams = navButton.layoutParams as LinearLayout.LayoutParams + buttonLayoutParams.weight = 0f + when (i) { + 0 -> { + buttonLayoutParams.marginEnd = spaceInBetween / 2 + } + navButtonContainer.childCount - 1 -> { + buttonLayoutParams.marginStart = spaceInBetween / 2 + } + else -> { + buttonLayoutParams.marginStart = spaceInBetween / 2 + buttonLayoutParams.marginEnd = spaceInBetween / 2 + } + } + } + } +}
\ No newline at end of file diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java new file mode 100644 index 0000000000..38b6dfd39b --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java @@ -0,0 +1,137 @@ +/* + * 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.LauncherPrefs; +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; +import com.android.launcher3.util.OnboardingPrefs; + +/** + * Window context for the taskbar overlays such as All Apps and EDU. + * <p> + * Overlays have their own window and need a window context. Some properties are delegated to the + * {@link TaskbarActivityContext} such as {@link PopupDataProvider}. + */ +public class TaskbarOverlayContext extends BaseTaskbarContext { + private final TaskbarActivityContext mTaskbarContext; + private final OnboardingPrefs<TaskbarOverlayContext> mOnboardingPrefs; + + private final TaskbarOverlayController mOverlayController; + private final TaskbarDragController mDragController; + private final TaskbarOverlayDragLayer mDragLayer; + + // We automatically stash taskbar when All Apps is opened in gesture navigation mode. + private final boolean mWillTaskbarBeVisuallyStashed; + private final int mStashedTaskbarHeight; + + public TaskbarOverlayContext( + Context windowContext, + TaskbarActivityContext taskbarContext, + TaskbarControllers controllers) { + super(windowContext); + mTaskbarContext = taskbarContext; + mOverlayController = controllers.taskbarOverlayController; + mDragController = new TaskbarDragController(this); + mDragController.init(controllers); + mOnboardingPrefs = new OnboardingPrefs<>(this, LauncherPrefs.getPrefs(this)); + mDragLayer = new TaskbarOverlayDragLayer(this); + + TaskbarStashController taskbarStashController = controllers.taskbarStashController; + mWillTaskbarBeVisuallyStashed = taskbarStashController.supportsVisualStashing(); + mStashedTaskbarHeight = taskbarStashController.getStashedHeight(); + } + + boolean willTaskbarBeVisuallyStashed() { + return mWillTaskbarBeVisuallyStashed; + } + + int getStashedTaskbarHeight() { + return mStashedTaskbarHeight; + } + + public TaskbarOverlayController getOverlayController() { + return mOverlayController; + } + + @Override + public DeviceProfile getDeviceProfile() { + return mOverlayController.getLauncherDeviceProfile(); + } + + @Override + public TaskbarDragController getDragController() { + return mDragController; + } + + @Override + public TaskbarOverlayDragLayer getDragLayer() { + return mDragLayer; + } + + @Override + public TaskbarAllAppsContainerView getAppsView() { + return mDragLayer.findViewById(R.id.apps_view); + } + + @Override + public OnboardingPrefs<TaskbarOverlayContext> getOnboardingPrefs() { + return mOnboardingPrefs; + } + + @Override + public boolean isBindingItems() { + return mTaskbarContext.isBindingItems(); + } + + @Override + public View.OnClickListener getItemOnClickListener() { + return mTaskbarContext.getItemOnClickListener(); + } + + @Override + public PopupDataProvider getPopupDataProvider() { + return mTaskbarContext.getPopupDataProvider(); + } + + @Override + public DotInfo getDotInfoForItem(ItemInfo info) { + return mTaskbarContext.getDotInfoForItem(info); + } + + @Override + public void onDragStart() {} + + @Override + public void onDragEnd() { + mOverlayController.maybeCloseWindow(); + } + + @Override + public void onPopupVisibilityChanged(boolean isVisible) {} +} 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 0000000000..476e0a8bab --- /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 0000000000..044afd6725 --- /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 2f8e4d9dde..ca7ce746b6 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java +++ b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java @@ -17,15 +17,13 @@ package com.android.launcher3.uioverrides; import android.app.Person; -import android.content.Context; import android.content.pm.ShortcutInfo; -import android.content.res.Resources; -import com.android.launcher3.R; import com.android.launcher3.Utilities; -import com.android.launcher3.util.DisplayController; -import com.android.launcher3.util.DisplayController.NavigationMode; +/** + * A wrapper for the hidden API calls + */ public class ApiWrapper { public static final boolean TASKBAR_DRAWN_IN_PROCESS = true; @@ -34,24 +32,4 @@ public class ApiWrapper { Person[] persons = si.getPersons(); return persons == null ? Utilities.EMPTY_PERSON_ARRAY : persons; } - - /** - * Returns the minimum space that should be left empty at the end of hotseat - */ - public static int getHotseatEndOffset(Context context) { - if (DisplayController.getNavigationMode(context) == NavigationMode.THREE_BUTTONS) { - Resources res = context.getResources(); - /* - * 3 nav buttons + - * Little space at the end for contextual buttons + - * Little space between icons and nav buttons - */ - return 3 * res.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size) - + res.getDimensionPixelSize(R.dimen.taskbar_contextual_button_margin) - + res.getDimensionPixelSize(R.dimen.taskbar_hotseat_nav_spacing); - } else { - return 0; - } - - } } diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java index 84b3839825..8fb70300f6 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java @@ -23,25 +23,32 @@ import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_MODAL; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE; +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.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.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; /** @@ -53,9 +60,9 @@ import com.android.quickstep.views.RecentsView; 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(); } @@ -67,24 +74,25 @@ public abstract class BaseRecentsViewStateController<T extends RecentsView> 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); + 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(); + } + }); } /** @@ -104,19 +112,70 @@ public abstract class BaseRecentsViewStateController<T extends RecentsView> 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)); + + 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, - showAsGrid ? INSTANT : FINAL_FRAME); + 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 5f3a990750..bf0f8f72e9 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.Launcher; 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 @@ public class PredictedAppIcon extends DoubleShadowBubbleTextView { mIsPinned = true; applyFromWorkspaceItem(info); setOnLongClickListener(ItemLongClickListener.INSTANCE_WORKSPACE); - ((CellLayout.LayoutParams) getLayoutParams()).canReorder = true; + ((CellLayoutLayoutParams) getLayoutParams()).canReorder = true; invalidate(); } @@ -280,7 +281,7 @@ public class PredictedAppIcon extends DoubleShadowBubbleTextView { */ 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 0000000000..6659fa0ee7 --- /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 4bb43431bc..9be569e895 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,41 +23,89 @@ import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SH 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.testing.TestProtocol.HINT_STATE_ORDINAL; -import static com.android.launcher3.testing.TestProtocol.HINT_STATE_TWO_BUTTON_ORDINAL; -import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL; -import static com.android.launcher3.testing.TestProtocol.QUICK_SWITCH_STATE_ORDINAL; +import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID; +import static com.android.launcher3.popup.QuickstepSystemShortcut.getSplitSelectShortcutByPosition; +import static com.android.launcher3.taskbar.LauncherTaskbarUIController.ALL_APPS_PAGE_PROGRESS_INDEX; +import static com.android.launcher3.taskbar.LauncherTaskbarUIController.MINUS_ONE_PAGE_PROGRESS_INDEX; +import static com.android.launcher3.taskbar.LauncherTaskbarUIController.WIDGETS_PAGE_PROGRESS_INDEX; +import static com.android.launcher3.testing.shared.TestProtocol.HINT_STATE_ORDINAL; +import static com.android.launcher3.testing.shared.TestProtocol.HINT_STATE_TWO_BUTTON_ORDINAL; +import static com.android.launcher3.testing.shared.TestProtocol.OVERVIEW_STATE_ORDINAL; +import static com.android.launcher3.testing.shared.TestProtocol.QUICK_SWITCH_STATE_ORDINAL; +import static com.android.launcher3.util.DisplayController.CHANGE_ACTIVE_SCREEN; +import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE; +import static com.android.launcher3.util.Executors.THREAD_POOL_EXECUTOR; +import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY; +import android.animation.AnimatorSet; +import android.animation.ValueAnimator; +import android.app.ActivityManager; +import android.app.ActivityOptions; +import android.content.Context; import android.content.Intent; +import android.content.IntentSender; import android.content.SharedPreferences; import android.content.res.Configuration; +import android.hardware.SensorManager; +import android.hardware.devicestate.DeviceStateManager; +import android.media.permission.SafeCloseable; +import android.os.Bundle; +import android.os.CancellationSignal; +import android.os.IBinder; +import android.os.SystemProperties; +import android.util.Log; +import android.view.Display; import android.view.HapticFeedbackConstants; +import android.view.RemoteAnimationTarget; import android.view.View; +import android.view.WindowManagerGlobal; +import android.window.SplashScreen; -import com.android.launcher3.BaseQuickstepLauncher; +import androidx.annotation.BinderThread; +import androidx.annotation.Nullable; + +import com.android.app.viewcapture.ViewCapture; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.LauncherState; import com.android.launcher3.QuickstepAccessibilityDelegate; +import com.android.launcher3.QuickstepTransitionManager; +import com.android.launcher3.R; +import com.android.launcher3.Utilities; import com.android.launcher3.Workspace; import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.appprediction.PredictionRowView; +import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.dragndrop.DragOptions; import com.android.launcher3.hybridhotseat.HotseatPredictionController; import com.android.launcher3.logging.InstanceId; import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.logging.StatsLogManager.StatsLogger; import com.android.launcher3.model.BgDataModel.FixedContainerItems; +import com.android.launcher3.model.WellbeingModel; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.popup.SystemShortcut; +import com.android.launcher3.proxy.ProxyActivityStarter; +import com.android.launcher3.proxy.StartActivityParams; +import com.android.launcher3.statehandlers.DepthController; +import com.android.launcher3.statehandlers.DesktopVisibilityController; import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory; +import com.android.launcher3.statemanager.StateManager.StateHandler; +import com.android.launcher3.taskbar.LauncherTaskbarUIController; +import com.android.launcher3.taskbar.TaskbarManager; +import com.android.launcher3.uioverrides.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 +116,120 @@ import com.android.launcher3.uioverrides.touchcontrollers.StatusBarTouchControll 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 +310,16 @@ public class QuickstepLauncher extends BaseQuickstepLauncher { @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 +338,35 @@ public class QuickstepLauncher extends BaseQuickstepLauncher { 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 +374,17 @@ public class QuickstepLauncher extends BaseQuickstepLauncher { */ 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 +414,30 @@ public class QuickstepLauncher extends BaseQuickstepLauncher { @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 +516,489 @@ public class QuickstepLauncher extends BaseQuickstepLauncher { 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)); + } + addMultiWindowModeChangedListener(mDepthController); + initUnfoldTransitionProgressProvider(); + if (FeatureFlags.CONTINUOUS_VIEW_TREE_CAPTURE.get()) { + mViewCapture = ViewCapture.getInstance().startCapture(getWindow()); + } + } + + @Override + protected void onResume() { + super.onResume(); + + if (mLauncherUnfoldAnimationController != null) { + mLauncherUnfoldAnimationController.onResume(); + } + } + + @Override + protected void onPause() { + if (mLauncherUnfoldAnimationController != null) { + mLauncherUnfoldAnimationController.onPause(); + } + + super.onPause(); + } + + @Override + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + + if (mOverviewCommandHelper != null) { + mOverviewCommandHelper.clearPendingCommands(); + } + } + + public QuickstepTransitionManager getAppTransitionManager() { + return mAppTransitionManager; + } + + @Override + public void onEnterAnimationComplete() { + super.onEnterAnimationComplete(); + // After the transition to home, enable the high-res thumbnail loader if it wasn't enabled + // as a part of quickstep, so that high-res thumbnails can load the next time we enter + // overview + RecentsModel.INSTANCE.get(this).getThumbnailCache() + .getHighResLoadingState().setVisible(true); + } + + @Override + protected void handleGestureContract(Intent intent) { + if (FeatureFlags.SEPARATE_RECENTS_ACTIVITY.get()) { + super.handleGestureContract(intent); + } + } + + @Override + public void onTrimMemory(int level) { + super.onTrimMemory(level); + RecentsModel.INSTANCE.get(this).onTrimMemory(level); + } + + @Override + public void onUiChangedWhileSleeping() { + // Remove the snapshot because the content view may have obvious changes. + UI_HELPER_EXECUTOR.execute( + () -> ActivityManagerWrapper.getInstance().invalidateHomeTaskSnapshot(this)); + } + + @Override + protected void onScreenOff() { + super.onScreenOff(); + RecentsView recentsView = getOverviewPanel(); + recentsView.finishRecentsAnimation(true /* toRecents */, null); + } + + @Override + public void onAllAppsTransition(float progress) { + super.onAllAppsTransition(progress); + onTaskbarInAppDisplayProgressUpdate(progress, ALL_APPS_PAGE_PROGRESS_INDEX); + } + + @Override + public void onWidgetsTransition(float progress) { + super.onWidgetsTransition(progress); + onTaskbarInAppDisplayProgressUpdate(progress, WIDGETS_PAGE_PROGRESS_INDEX); + if (mEnableWidgetDepth) { + getDepthController().widgetDepth.setValue(Utilities.mapToRange( + progress, 0f, 1f, 0f, getDeviceProfile().bottomSheetDepth, EMPHASIZED)); + } + } + + private void onTaskbarInAppDisplayProgressUpdate(float progress, int flag) { + if (mTaskbarManager == null + || mTaskbarManager.getCurrentActivityContext() == null + || mTaskbarUIController == null) { + return; + } + mTaskbarUIController.onTaskbarInAppDisplayProgressUpdate(progress, flag); + } + + @Override + public void startIntentSenderForResult(IntentSender intent, int requestCode, + Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags, Bundle options) { + if (requestCode != -1) { + mPendingActivityRequestCode = requestCode; + StartActivityParams params = new StartActivityParams(this, requestCode); + params.intentSender = intent; + params.fillInIntent = fillInIntent; + params.flagsMask = flagsMask; + params.flagsValues = flagsValues; + params.extraFlags = extraFlags; + params.options = options; + startActivity(ProxyActivityStarter.getLaunchIntent(this, params)); + } else { + super.startIntentSenderForResult(intent, requestCode, fillInIntent, flagsMask, + flagsValues, extraFlags, options); + } + } + + @Override + public void startActivityForResult(Intent intent, int requestCode, Bundle options) { + if (requestCode != -1) { + mPendingActivityRequestCode = requestCode; + StartActivityParams params = new StartActivityParams(this, requestCode); + params.intent = intent; + params.options = options; + startActivity(ProxyActivityStarter.getLaunchIntent(this, params)); + } else { + super.startActivityForResult(intent, requestCode, options); + } + } + + @Override + public void setResumed() { + if (DesktopTaskView.DESKTOP_IS_PROTO2_ENABLED) { + DesktopVisibilityController controller = mDesktopVisibilityController; + if (controller != null && controller.areFreeformTasksVisible()) { + // 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= */ THREAD_POOL_EXECUTOR, + /* tracingTagPrefix= */ "launcher", + WindowManagerGlobal.getWindowManagerService() + ); + + mUnfoldTransitionProgressProvider = unfoldComponent.getUnfoldTransitionProvider() + .orElseThrow(() -> new IllegalStateException( + "Trying to create UnfoldTransitionProgressProvider when the " + + "transition is disabled")); + + mRotationChangeProvider = unfoldComponent.getRotationChangeProvider(); + mLauncherUnfoldAnimationController = new LauncherUnfoldAnimationController( + /* launcher= */ this, + getWindowManager(), + mUnfoldTransitionProgressProvider, + mRotationChangeProvider + ); + Log.d("b/261320823", "initUnfoldTransitionProgressProvider completed"); } - return appWidgetHost; + } + + 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; } private static final class LauncherTaskViewController extends @@ -371,6 +1027,9 @@ public class QuickstepLauncher extends BaseQuickstepLauncher { @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 0000000000..a8edd511f6 --- /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 00a98c0647..e8e83288cd 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_ACTIONS; import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT; import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_ACTIONS_FADE; +import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE; import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA; 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_PRIMARY_SPLIT_TRANSLA 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 @@ import com.android.quickstep.views.RecentsView; public final class RecentsViewStateController extends BaseRecentsViewStateController<LauncherRecentsView> { - public RecentsViewStateController(BaseQuickstepLauncher launcher) { + public RecentsViewStateController(QuickstepLauncher launcher) { super(launcher); } @@ -72,7 +75,10 @@ public final class RecentsViewStateController extends // 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 @@ public final class RecentsViewStateController extends // 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 @@ public final class RecentsViewStateController extends 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 @@ public final class RecentsViewStateController extends * 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 @@ public final class RecentsViewStateController extends 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); - - if (!animate) { - splitSelectInitAnimation.buildAnim().start(); - } else { - builder.add(splitSelectInitAnimation.buildAnim()); - } - - mRecentsView.applySplitPrimaryScrollOffset(); - } else { - mRecentsView.resetSplitPrimaryScrollOffset(); + SplitAnimationTimings timings = + AnimUtils.getDeviceOverviewToSplitTimings(mLauncher.getDeviceProfile().isTablet); + + 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()); + + if (!animate) { + AnimatorSet as = builder.buildAnim(); + as.start(); + as.end(); } + + mRecentsView.applySplitPrimaryScrollOffset(); } private void setAlphas(PropertySetter propertySetter, StateAnimationConfig config, @@ -143,7 +164,7 @@ public final class RecentsViewStateController extends 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 5afeca7e3b..faa900b714 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.ComponentName; 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 @@ public class PluginEnablerImpl extends PreferenceDataStore implements PluginEnab 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 fe0bca6646..e3d386cae6 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.Utilities; 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 a74774c62b..2a42175b5b 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java +++ b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java @@ -20,19 +20,17 @@ import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_ALLAP 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 */ public class AllAppsState extends LauncherState { - private static final float WORKSPACE_SCALE_FACTOR = 0.97f; - private static final int STATE_FLAGS = FLAG_WORKSPACE_INACCESSIBLE | FLAG_CLOSE_POPUPS | FLAG_HOTSEAT_INACCESSIBLE; @@ -41,11 +39,11 @@ public class AllAppsState extends LauncherState { } @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 +58,8 @@ public class AllAppsState extends LauncherState { @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,17 +70,22 @@ public class AllAppsState extends LauncherState { ScaleAndTranslation overviewScaleAndTranslation = LauncherState.OVERVIEW .getWorkspaceScaleAndTranslation(launcher); return new ScaleAndTranslation( - WORKSPACE_SCALE_FACTOR, + launcher.getDeviceProfile().workspaceContentScale, overviewScaleAndTranslation.translationX, overviewScaleAndTranslation.translationY); } } @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 3e737bb6f3..0e998f5a7f 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java +++ b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java @@ -16,6 +16,7 @@ package com.android.launcher3.uioverrides.states; import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND; +import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS; import android.content.Context; import android.graphics.Color; @@ -25,6 +26,7 @@ import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; import com.android.launcher3.R; import com.android.launcher3.allapps.AllAppsTransitionController; +import com.android.launcher3.config.FeatureFlags; import com.android.quickstep.util.LayoutUtils; import com.android.quickstep.views.RecentsView; @@ -82,6 +84,11 @@ public class BackgroundAppState extends OverviewState { } @Override + public boolean showTaskThumbnailSplash() { + return true; + } + + @Override protected float getDepthUnchecked(Context context) { return 1; } @@ -89,12 +96,18 @@ public class BackgroundAppState extends OverviewState { @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 43179aaaab..6c1ef56feb 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java +++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java @@ -22,12 +22,11 @@ import android.content.Context; import android.graphics.Rect; import android.os.SystemProperties; -import com.android.launcher3.BaseQuickstepLauncher; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; import com.android.launcher3.R; -import com.android.launcher3.taskbar.LauncherTaskbarUIController; +import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.Themes; import com.android.quickstep.util.LayoutUtils; @@ -39,6 +38,10 @@ import com.android.quickstep.views.TaskView; */ 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 @@ -59,8 +62,15 @@ public class OverviewState extends LauncherState { @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 @@ -95,13 +105,12 @@ public class OverviewState extends LauncherState { @Override public boolean isTaskbarStashed(Launcher launcher) { - if (launcher instanceof BaseQuickstepLauncher) { - LauncherTaskbarUIController uiController = - ((BaseQuickstepLauncher) launcher).getTaskbarUIController(); + return !FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get(); + } - return uiController != null && uiController.supportsVisualStashing(); - } - return super.isTaskbarStashed(launcher); + @Override + public boolean isTaskbarAlignedWithHotseat(Launcher launcher) { + return !FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get(); } @Override diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java index 2d7fe69d3b..5eeeb36981 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; 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,20 +42,15 @@ import static com.android.launcher3.states.StateAnimationConfig.ANIM_DEPTH; 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; -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_SCALE; import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_TRANSLATE; -import static com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController.ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD; -import static com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController.ALL_APPS_CONTENT_FADE_MIN_CLAMPING_THRESHOLD; -import static com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController.ALL_APPS_SCRIM_OPAQUE_THRESHOLD; -import static com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController.ALL_APPS_SCRIM_VISIBLE_THRESHOLD; import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY; -import static com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE; -import static com.android.systemui.animation.Interpolators.EMPHASIZED_DECELERATE; import android.animation.ValueAnimator; @@ -60,11 +58,12 @@ import com.android.launcher3.CellLayout; import com.android.launcher3.Hotseat; import com.android.launcher3.LauncherState; import com.android.launcher3.Workspace; -import com.android.launcher3.anim.Interpolators; import com.android.launcher3.states.StateAnimationConfig; +import com.android.launcher3.touch.AllAppsSwipeController; 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; /** @@ -94,9 +93,16 @@ public class QuickstepAtomicAnimationFactory extends 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); @@ -105,22 +111,21 @@ public class QuickstepAtomicAnimationFactory extends // 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; @@ -182,23 +187,25 @@ public class QuickstepAtomicAnimationFactory extends } config.duration = Math.max(config.duration, mHintToNormalDuration); } else if (fromState == ALL_APPS && toState == NORMAL) { - boolean isTablet = mActivity.getDeviceProfile().isTablet; - config.setInterpolator(ANIM_ALL_APPS_FADE, - isTablet ? FINAL_FRAME : Interpolators.clampToProgress(LINEAR, - 1 - ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD, - 1 - ALL_APPS_CONTENT_FADE_MIN_CLAMPING_THRESHOLD)); - config.setInterpolator(ANIM_SCRIM_FADE, Interpolators.clampToProgress(LINEAR, - 1 - ALL_APPS_SCRIM_OPAQUE_THRESHOLD, - 1 - ALL_APPS_SCRIM_VISIBLE_THRESHOLD)); - config.setInterpolator(ANIM_VERTICAL_PROGRESS, EMPHASIZED_ACCELERATE); - if (!isTablet) { - config.setInterpolator(ANIM_WORKSPACE_FADE, INSTANT); - } + AllAppsSwipeController.applyAllAppsToNormalConfig(mActivity, config); } else if (fromState == NORMAL && toState == ALL_APPS) { - if (mActivity.getDeviceProfile().isTablet) { - config.setInterpolator(ANIM_VERTICAL_PROGRESS, EMPHASIZED_DECELERATE); - } - // TODO(b/231682175): centralize this setup in AllAppsSwipeController + 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 e79d56b631..8babd3470b 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 @@ public class SplitScreenSelectState extends OverviewState { 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 34a6821ac5..40dfd82f5d 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 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 @@ public class NavBarToHomeTouchController implements TouchController, 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 @@ public class NavBarToHomeTouchController implements TouchController, 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 @@ public class NavBarToHomeTouchController implements TouchController, 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 8faabc9561..b5afda388a 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.LauncherState.OVERVIEW; 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.graphics.PointF; 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 @@ public class NoButtonNavbarToOverviewTouchController extends PortraitStatesTouch @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 class NoButtonNavbarToOverviewTouchController extends PortraitStatesTouch 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 @@ public class NoButtonNavbarToOverviewTouchController extends PortraitStatesTouch @Override public void onDragEnd(float velocity) { LauncherTaskbarUIController controller = - ((BaseQuickstepLauncher) mLauncher).getTaskbarUIController(); + ((QuickstepLauncher) mLauncher).getTaskbarUIController(); if (controller != null) { controller.setShouldDelayLauncherStateAnim(false); } @@ -277,14 +283,4 @@ public class NoButtonNavbarToOverviewTouchController extends PortraitStatesTouch 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 53dc9dd873..e0cb0b4ddb 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.ANIM_WORKSPACE_S 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.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.Animator.AnimatorListener; 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 @@ public class NoButtonQuickSwitchTouchController implements TouchController, 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 @@ public class NoButtonQuickSwitchTouchController implements TouchController, 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,11 +226,12 @@ public class NoButtonQuickSwitchTouchController implements TouchController, // 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( (fromState.getVisibleElements(mLauncher) & OVERVIEW_ACTIONS) != 0 ? 1f : 0f); + mRecentsView.setTaskIconScaledDown(true); float[] scaleAndOffset = toState.getOverviewScaleAndOffset(mLauncher); // As we drag right, animate the following properties: @@ -246,24 +246,6 @@ public class NoButtonQuickSwitchTouchController implements TouchController, 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(); @@ -321,6 +303,7 @@ public class NoButtonQuickSwitchTouchController implements TouchController, boolean verticalFling = mSwipeDetector.isFling(velocity.y); boolean noFling = !horizontalFling && !verticalFling; if (mMotionPauseDetector.isPaused() && noFling) { + // Going to Overview. cancelAnimations(); StateAnimationConfig config = new StateAnimationConfig(); @@ -331,6 +314,8 @@ public class NoButtonQuickSwitchTouchController implements TouchController, @Override public void onAnimationEnd(Animator animation) { onAnimationToStateCompleted(OVERVIEW); + // Animate the icon after onAnimationToStateCompleted() so it doesn't clobber. + mRecentsView.animateUpTaskIconScale(); } }); overviewAnim.start(); diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java index e56c90c20c..8368f9ca03 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java @@ -21,21 +21,8 @@ import static com.android.launcher3.AbstractFloatingView.getTopOpenViewWithType; import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.OVERVIEW; -import static com.android.launcher3.anim.Interpolators.FINAL_FRAME; -import static com.android.launcher3.anim.Interpolators.INSTANT; -import static com.android.launcher3.anim.Interpolators.LINEAR; -import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE; -import static com.android.launcher3.states.StateAnimationConfig.ANIM_DEPTH; -import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_FADE; -import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_SCALE; -import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_TRANSLATE; -import static com.android.launcher3.states.StateAnimationConfig.ANIM_SCRIM_FADE; -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_SCALE; import android.view.MotionEvent; -import android.view.animation.Interpolator; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; @@ -44,6 +31,7 @@ import com.android.launcher3.allapps.AllAppsTransitionController; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.states.StateAnimationConfig; import com.android.launcher3.touch.AbstractStateChangeTouchController; +import com.android.launcher3.touch.AllAppsSwipeController; import com.android.launcher3.touch.SingleAxisSwipeDetector; import com.android.launcher3.uioverrides.states.OverviewState; import com.android.quickstep.SystemUiProxy; @@ -58,53 +46,6 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr private static final String TAG = "PortraitStatesTouchCtrl"; - /** - * The progress at which all apps content will be fully visible. - */ - public static final float ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD = 0.8f; - - /** - * Minimum clamping progress for fading in all apps content - */ - public static final float ALL_APPS_CONTENT_FADE_MIN_CLAMPING_THRESHOLD = 0.5f; - - /** - * Minimum clamping progress for fading in all apps scrim - */ - public static final float ALL_APPS_SCRIM_VISIBLE_THRESHOLD = .1f; - - /** - * Maximum clamping progress for opaque all apps scrim - */ - public static final float ALL_APPS_SCRIM_OPAQUE_THRESHOLD = .5f; - - // Custom timing for NORMAL -> ALL_APPS on phones only. - private static final float ALL_APPS_STATE_TRANSITION = 0.4f; - private static final float ALL_APPS_FULL_DEPTH_PROGRESS = 0.5f; - - // Custom interpolators for NORMAL -> ALL_APPS on phones only. - private static final Interpolator LINEAR_EARLY = - Interpolators.clampToProgress(LINEAR, 0f, ALL_APPS_STATE_TRANSITION); - private static final Interpolator STEP_TRANSITION = - Interpolators.clampToProgress(FINAL_FRAME, 0f, ALL_APPS_STATE_TRANSITION); - // The blur to and from All Apps is set to be complete when the interpolator is at 0.5. - public static final Interpolator BLUR = - Interpolators.clampToProgress( - Interpolators.mapToProgress(LINEAR, 0f, ALL_APPS_FULL_DEPTH_PROGRESS), - 0f, ALL_APPS_STATE_TRANSITION); - public static final Interpolator WORKSPACE_FADE = STEP_TRANSITION; - public static final Interpolator WORKSPACE_SCALE = LINEAR_EARLY; - public static final Interpolator HOTSEAT_FADE = STEP_TRANSITION; - public static final Interpolator HOTSEAT_SCALE = LINEAR_EARLY; - public static final Interpolator HOTSEAT_TRANSLATE = STEP_TRANSITION; - public static final Interpolator SCRIM_FADE = LINEAR_EARLY; - public static final Interpolator ALL_APPS_FADE = - Interpolators.clampToProgress(LINEAR, ALL_APPS_STATE_TRANSITION, 1f); - public static final Interpolator ALL_APPS_VERTICAL_PROGRESS = - Interpolators.clampToProgress( - Interpolators.mapToProgress(LINEAR, ALL_APPS_STATE_TRANSITION, 1f), - ALL_APPS_STATE_TRANSITION, 1f); - private final PortraitOverviewStateTouchHelper mOverviewPortraitStateTouchHelper; public PortraitStatesTouchController(Launcher l) { @@ -160,66 +101,15 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr return fromState; } - private StateAnimationConfig getNormalToAllAppsAnimation() { - StateAnimationConfig builder = new StateAnimationConfig(); - if (mLauncher.getDeviceProfile().isTablet) { - builder.setInterpolator(ANIM_ALL_APPS_FADE, INSTANT); - builder.setInterpolator(ANIM_SCRIM_FADE, - Interpolators.clampToProgress(LINEAR, - ALL_APPS_SCRIM_VISIBLE_THRESHOLD, - ALL_APPS_SCRIM_OPAQUE_THRESHOLD)); - } else { - // TODO(b/231682175): centralize this setup in AllAppsSwipeController. - builder.setInterpolator(ANIM_DEPTH, BLUR); - builder.setInterpolator(ANIM_WORKSPACE_FADE, WORKSPACE_FADE); - builder.setInterpolator(ANIM_WORKSPACE_SCALE, WORKSPACE_SCALE); - builder.setInterpolator(ANIM_HOTSEAT_FADE, HOTSEAT_FADE); - builder.setInterpolator(ANIM_HOTSEAT_SCALE, HOTSEAT_SCALE); - builder.setInterpolator(ANIM_HOTSEAT_TRANSLATE, HOTSEAT_TRANSLATE); - builder.setInterpolator(ANIM_SCRIM_FADE, SCRIM_FADE); - builder.setInterpolator(ANIM_ALL_APPS_FADE, ALL_APPS_FADE); - builder.setInterpolator(ANIM_VERTICAL_PROGRESS, ALL_APPS_VERTICAL_PROGRESS); - } - return builder; - } - - private StateAnimationConfig getAllAppsToNormalAnimation() { - StateAnimationConfig builder = new StateAnimationConfig(); - if (mLauncher.getDeviceProfile().isTablet) { - builder.setInterpolator(ANIM_ALL_APPS_FADE, FINAL_FRAME); - builder.setInterpolator(ANIM_SCRIM_FADE, - Interpolators.clampToProgress(LINEAR, - 1 - ALL_APPS_SCRIM_OPAQUE_THRESHOLD, - 1 - ALL_APPS_SCRIM_VISIBLE_THRESHOLD)); - } else { - // These interpolators are the reverse of the ones used above, so swiping out of All - // Apps feels the same as swiping into it. - // TODO(b/231682175): centralize this setup in AllAppsSwipeController. - builder.setInterpolator(ANIM_DEPTH, Interpolators.reverse(BLUR)); - builder.setInterpolator(ANIM_WORKSPACE_FADE, Interpolators.reverse(WORKSPACE_FADE)); - builder.setInterpolator(ANIM_WORKSPACE_SCALE, Interpolators.reverse(WORKSPACE_SCALE)); - builder.setInterpolator(ANIM_HOTSEAT_FADE, Interpolators.reverse(HOTSEAT_FADE)); - builder.setInterpolator(ANIM_HOTSEAT_SCALE, Interpolators.reverse(HOTSEAT_SCALE)); - builder.setInterpolator(ANIM_HOTSEAT_TRANSLATE, - Interpolators.reverse(HOTSEAT_TRANSLATE)); - builder.setInterpolator(ANIM_SCRIM_FADE, Interpolators.reverse(SCRIM_FADE)); - builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.reverse(ALL_APPS_FADE)); - builder.setInterpolator(ANIM_VERTICAL_PROGRESS, - Interpolators.reverse(ALL_APPS_VERTICAL_PROGRESS)); - } - return builder; - } - @Override protected StateAnimationConfig getConfigForStates( LauncherState fromState, LauncherState toState) { - final StateAnimationConfig config; + final StateAnimationConfig config = new StateAnimationConfig(); + config.userControlled = true; if (fromState == NORMAL && toState == ALL_APPS) { - config = getNormalToAllAppsAnimation(); + AllAppsSwipeController.applyNormalToAllAppsAnimConfig(mLauncher, config); } else if (fromState == ALL_APPS && toState == NORMAL) { - config = getAllAppsToNormalAnimation(); - } else { - config = new StateAnimationConfig(); + AllAppsSwipeController.applyAllAppsToNormalConfig(mLauncher, config); } return config; } @@ -296,12 +186,17 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr 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); @@ -314,6 +209,10 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr if (newToState != ALL_APPS) { InteractionJankMonitorWrapper.cancel(InteractionJankMonitorWrapper.CUJ_OPEN_ALL_APPS); } + if (newToState != NORMAL) { + InteractionJankMonitorWrapper.cancel( + InteractionJankMonitorWrapper.CUJ_CLOSE_ALL_APPS_SWIPE); + } } @Override @@ -321,6 +220,9 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr 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); } } @@ -328,5 +230,7 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr 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 d1b0a9c4ba..56ac4c5b3a 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_OVERVIEW_TR 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.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.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHO 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.states.StateAnimationConfig; 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 @@ public class QuickSwitchTouchController extends AbstractStateChangeTouchControll 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/TaskViewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java index ca7f633bbc..eddc50c64f 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.touch.SingleAxisSwipeDetector; 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 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity> 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 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity> 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/launcher3/uioverrides/touchcontrollers/TwoButtonNavbarTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TwoButtonNavbarTouchController.java index e2747dfcbb..9f2c1d479f 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TwoButtonNavbarTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TwoButtonNavbarTouchController.java @@ -24,13 +24,11 @@ import static com.android.launcher3.Utilities.EDGE_NAV_BAR; import android.animation.ValueAnimator; import android.os.SystemClock; -import android.util.Log; import android.view.MotionEvent; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; -import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.touch.AbstractStateChangeTouchController; import com.android.launcher3.touch.SingleAxisSwipeDetector; import com.android.quickstep.SystemUiProxy; diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java index 9bcb809af4..1ac357f889 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.PagedView.INVALID_PAGE; import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL; import static com.android.launcher3.anim.Interpolators.DEACCEL; import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2; -import static com.android.launcher3.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,11 @@ import static com.android.quickstep.GestureState.STATE_END_TARGET_SET; 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.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; @@ -58,8 +62,10 @@ import android.animation.ValueAnimator; import android.annotation.TargetApi; import android.app.Activity; import android.app.ActivityManager; +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; @@ -69,8 +75,10 @@ import android.os.IBinder; 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; @@ -81,24 +89,30 @@ import android.window.PictureInPictureSurfaceTransaction; 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; @@ -109,10 +123,10 @@ import com.android.quickstep.util.ProtoTracer; 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.RecentsView; import com.android.quickstep.views.TaskView; import com.android.systemui.shared.recents.model.Task; @@ -120,14 +134,15 @@ import com.android.systemui.shared.recents.model.ThumbnailData; 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; /** @@ -140,7 +155,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, RecentsAnimationCallbacks.RecentsAnimationListener { private static final String TAG = "AbsSwipeUpHandler"; - private static final String[] STATE_NAMES = DEBUG_STATES ? new String[17] : null; + private static final ArrayList<String> STATE_NAMES = new ArrayList<>(); protected final BaseActivityInterface<S, T> mActivityInterface; protected final InputConsumerProxy mInputConsumerProxy; @@ -171,66 +186,68 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, } }; - private static int getFlagForIndex(int index, String name) { + private static int FLAG_COUNT = 0; + private static int getNextStateFlag(String name) { if (DEBUG_STATES) { - STATE_NAMES[index] = name; + STATE_NAMES.add(name); } - return 1 << index; + int index = 1 << FLAG_COUNT; + FLAG_COUNT++; + return index; } // Launcher UI related states protected static final int STATE_LAUNCHER_PRESENT = - getFlagForIndex(0, "STATE_LAUNCHER_PRESENT"); + getNextStateFlag("STATE_LAUNCHER_PRESENT"); protected static final int STATE_LAUNCHER_STARTED = - getFlagForIndex(1, "STATE_LAUNCHER_STARTED"); + getNextStateFlag("STATE_LAUNCHER_STARTED"); protected static final int STATE_LAUNCHER_DRAWN = - getFlagForIndex(2, "STATE_LAUNCHER_DRAWN"); + getNextStateFlag("STATE_LAUNCHER_DRAWN"); // Called when the Launcher has connected to the touch interaction service (and the taskbar // ui controller is initialized) protected static final int STATE_LAUNCHER_BIND_TO_SERVICE = - getFlagForIndex(3, "STATE_LAUNCHER_BIND_TO_SERVICE"); + getNextStateFlag("STATE_LAUNCHER_BIND_TO_SERVICE"); // Internal initialization states private static final int STATE_APP_CONTROLLER_RECEIVED = - getFlagForIndex(4, "STATE_APP_CONTROLLER_RECEIVED"); + getNextStateFlag("STATE_APP_CONTROLLER_RECEIVED"); // Interaction finish states private static final int STATE_SCALED_CONTROLLER_HOME = - getFlagForIndex(5, "STATE_SCALED_CONTROLLER_HOME"); + getNextStateFlag("STATE_SCALED_CONTROLLER_HOME"); private static final int STATE_SCALED_CONTROLLER_RECENTS = - getFlagForIndex(6, "STATE_SCALED_CONTROLLER_RECENTS"); + getNextStateFlag("STATE_SCALED_CONTROLLER_RECENTS"); protected static final int STATE_HANDLER_INVALIDATED = - getFlagForIndex(7, "STATE_HANDLER_INVALIDATED"); + getNextStateFlag("STATE_HANDLER_INVALIDATED"); private static final int STATE_GESTURE_STARTED = - getFlagForIndex(8, "STATE_GESTURE_STARTED"); + getNextStateFlag("STATE_GESTURE_STARTED"); private static final int STATE_GESTURE_CANCELLED = - getFlagForIndex(9, "STATE_GESTURE_CANCELLED"); + getNextStateFlag("STATE_GESTURE_CANCELLED"); private static final int STATE_GESTURE_COMPLETED = - getFlagForIndex(10, "STATE_GESTURE_COMPLETED"); + getNextStateFlag("STATE_GESTURE_COMPLETED"); private static final int STATE_CAPTURE_SCREENSHOT = - getFlagForIndex(11, "STATE_CAPTURE_SCREENSHOT"); + getNextStateFlag("STATE_CAPTURE_SCREENSHOT"); protected static final int STATE_SCREENSHOT_CAPTURED = - getFlagForIndex(12, "STATE_SCREENSHOT_CAPTURED"); + getNextStateFlag("STATE_SCREENSHOT_CAPTURED"); private static final int STATE_SCREENSHOT_VIEW_SHOWN = - getFlagForIndex(13, "STATE_SCREENSHOT_VIEW_SHOWN"); + getNextStateFlag("STATE_SCREENSHOT_VIEW_SHOWN"); private static final int STATE_RESUME_LAST_TASK = - getFlagForIndex(14, "STATE_RESUME_LAST_TASK"); + getNextStateFlag("STATE_RESUME_LAST_TASK"); private static final int STATE_START_NEW_TASK = - getFlagForIndex(15, "STATE_START_NEW_TASK"); + getNextStateFlag("STATE_START_NEW_TASK"); private static final int STATE_CURRENT_TASK_FINISHED = - getFlagForIndex(16, "STATE_CURRENT_TASK_FINISHED"); + getNextStateFlag("STATE_CURRENT_TASK_FINISHED"); private static final int STATE_FINISH_WITH_NO_END = - getFlagForIndex(17, "STATE_FINISH_WITH_NO_END"); + getNextStateFlag("STATE_FINISH_WITH_NO_END"); private static final int LAUNCHER_UI_STATES = STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN | STATE_LAUNCHER_STARTED | STATE_LAUNCHER_BIND_TO_SERVICE; public static final long MAX_SWIPE_DURATION = 350; - public static final long HOME_DURATION = StaggeredWorkspaceAnim.DURATION_MS; public static final float MIN_PROGRESS_FOR_OVERVIEW = 0.7f; private static final float SWIPE_DURATION_MULTIPLIER = @@ -241,6 +258,13 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, 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. */ @@ -269,7 +293,6 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, private boolean mWasLauncherAlreadyVisible; - private boolean mPassedOverviewThreshold; private boolean mGestureStarted; private boolean mLogDirectionUpOrLeft = true; private PointF mDownPos; @@ -278,6 +301,8 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, private final long mTouchTimeMs; private long mLauncherFrameDrawnTime; + private final int mSplashMainWindowShiftLength; + private final Runnable mOnDeferredActivityLaunch = this::onDeferredActivityLaunch; private SwipePipToHomeAnimator mSwipePipToHomeAnimator; @@ -290,6 +315,18 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, // 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; + + @Nullable + private RemoteAnimationTargets.ReleaseCheck mSwipePipToHomeReleaseCheck = null; + public AbsSwipeUpHandler(Context context, RecentsAnimationDeviceState deviceState, TaskAnimationManager taskAnimationManager, GestureState gestureState, long touchTimeMs, boolean continuingLastGesture, @@ -298,24 +335,69 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, mActivityInterface = gestureState.getActivityInterface(); mActivityInitListener = mActivityInterface.createActivityInitListener(this::onActivityInit); mInputConsumerProxy = - new InputConsumerProxy(context, - () -> mRecentsView.getPagedViewOrientedState().getRecentsActivityRotation(), - inputConsumer, () -> { + new InputConsumerProxy(context, /* rotationSupplier = */ () -> { + if (mRecentsView == null) { + return ROTATION_0; + } + return mRecentsView.getPagedViewOrientedState().getRecentsActivityRotation(); + }, inputConsumer, /* callback = */ () -> { endRunningWindowAnim(mGestureState.getEndTarget() == HOME /* cancel */); endLauncherTransitionController(); }, new InputProxyHandlerFactory(mActivityInterface, mGestureState)); 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); + mTaskbarHomeOverviewThreshold = 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); + mStateCallback = new MultiStateCallback( + STATE_NAMES.toArray(new String[0]), AbsSwipeUpHandler::getTrackedEventForState); mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_GESTURE_STARTED, this::onLauncherPresentAndGestureStarted); @@ -368,12 +450,6 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, 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) { @@ -440,7 +516,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, }); setupRecentsViewUi(); - linkRecentsViewScroll(); + mRecentsView.runOnPageScrollsInitialized(this::linkRecentsViewScroll); activity.runOnBindToTouchInteractionService(this::onLauncherBindToService); mActivity.registerActivityLifecycleCallbacks(mLifecycleCallbacks); @@ -549,14 +625,10 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, } 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() { @@ -569,7 +641,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, protected void notifyGestureAnimationStartToRecents() { Task[] runningTasks; - if (mIsSwipeForStagedSplit) { + if (mIsSwipeForSplit) { int[] splitTaskIds = TopTaskTracker.INSTANCE.get(mContext).getRunningSplitTaskIds(); runningTasks = mGestureState.getRunningTask().getPlaceholderTasks(splitTaskIds); } else { @@ -587,8 +659,8 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, 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 @@ -603,6 +675,8 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, public void onMotionPauseDetected() { mHasMotionEverBeenPaused = true; maybeUpdateRecentsAttachedState(true/* animate */, true/* moveFocusedTask */); + Optional.ofNullable(mActivityInterface.getTaskbarController()) + .ifPresent(TaskbarUIController::startTranslationSpring); performHapticFeedback(); } @@ -634,7 +708,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, if (!mDeviceState.isFullyGesturalNavMode() || mRecentsView == null) { return; } - RemoteAnimationTargetCompat runningTaskTarget = mRecentsAnimationTargets != null + RemoteAnimationTarget runningTaskTarget = mRecentsAnimationTargets != null ? mRecentsAnimationTargets.findTask(mGestureState.getRunningTaskId()) : null; final boolean recentsAttachedToAppWindow; @@ -674,6 +748,15 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, } } + /** + * 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 */); } @@ -736,14 +819,6 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, @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(); @@ -792,15 +867,17 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, public void onRecentsAnimationStart(RecentsAnimationController controller, RecentsAnimationTargets targets) { super.onRecentsAnimationStart(controller, targets); - ActiveGestureLog.INSTANCE.addLog("startRecentsAnimationCallback", targets.apps.length); mRemoteTargetHandles = mTargetGluer.assignTargetsForSplitScreen(mContext, targets); mRecentsAnimationController = controller; mRecentsAnimationTargets = targets; + 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 @@ -829,23 +906,18 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, 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; @@ -860,6 +932,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS); if (mRecentsView != null) { + final View rv = mRecentsView; mRecentsView.getViewTreeObserver().addOnDrawListener(new OnDrawListener() { boolean mHandled = false; @@ -874,20 +947,43 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, 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); - mRecentsView.post(() -> - mRecentsView.getViewTreeObserver().removeOnDrawListener(this)); + rv.post(() -> rv.getViewTreeObserver().removeOnDrawListener(this)); } }); } 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 @@ -911,26 +1007,27 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, } /** - * @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 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. * @param downPos The x and y value of where the gesture started. */ @UiThread - public void onGestureEnded(float endVelocity, PointF velocity, PointF downPos) { + public void onGestureEnded(float endVelocityPxPerMs, PointF velocityPxPerMs, PointF downPos) { 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 { @@ -976,12 +1073,16 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, 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 @@ -1002,11 +1103,13 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, } 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; } @@ -1019,84 +1122,106 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, 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; - } + + GestureEndTarget endTarget; + if (isCancel) { + endTarget = LAST_TASK; + } else if (isFlingY) { + endTarget = calculateEndTargetForFlingY(velocityPxPerMs, endVelocityPxPerMs); } else { - goingToNewTask = false; + endTarget = calculateEndTargetForNonFling(velocityPxPerMs); + } + + if (mDeviceState.isOverviewDisabled() && endTarget == RECENTS) { + return LAST_TASK; } - final boolean reachedOverviewThreshold = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW; + + 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 (!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; - } + 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; + } - if (mDeviceState.isOverviewDisabled() && (endTarget == RECENTS || endTarget == LAST_TASK)) { - return LAST_TASK; + private boolean isScrollingToNewTask() { + if (mRecentsView == null) { + return false; } - return endTarget; + 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); @@ -1109,16 +1234,16 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, 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; @@ -1135,7 +1260,9 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, mInputConsumerProxy.enable(); } if (endTarget == HOME) { - duration = HOME_DURATION; + duration = mActivity != null && mActivity.getDeviceProfile().isTaskbarPresent + ? StaggeredWorkspaceAnim.DURATION_TASKBAR_MS + : StaggeredWorkspaceAnim.DURATION_MS; // Early detach the nav bar once the endTarget is determined as HOME if (mRecentsAnimationController != null) { mRecentsAnimationController.detachNavigationBarFromApp(true); @@ -1165,18 +1292,26 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, 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(). + Runnable onPageTransitionEnd = () -> { + mGestureState.setState(STATE_RECENTS_SCROLLING_FINISHED); + setClampScrollOffset(false); + }; if (mRecentsView != null) { - mRecentsView.setOnPageTransitionEndCallback( - () -> mGestureState.setState(STATE_RECENTS_SCROLLING_FINISHED)); + ActiveGestureLog.INSTANCE.trackEvent(ActiveGestureErrorDetector.GestureEvent + .SET_ON_PAGE_TRANSITION_END_CALLBACK); + mRecentsView.setOnPageTransitionEndCallback(onPageTransitionEnd); } else { - mGestureState.setState(STATE_RECENTS_SCROLLING_FINISHED); + onPageTransitionEnd.run(); } - animateToProgress(startShift, endShift, duration, interpolator, endTarget, velocity); + animateToProgress(startShift, endShift, duration, interpolator, endTarget, velocityPxPerMs); } private void doLogGesture(GestureEndTarget endTarget, @Nullable TaskView targetTask) { @@ -1225,7 +1360,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, 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 @@ -1252,6 +1387,10 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, // 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); @@ -1271,30 +1410,40 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, 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 ? runningTaskTarget.taskInfo.launchCookies : new ArrayList<>(); boolean isTranslucent = runningTaskTarget != null && runningTaskTarget.isTranslucent; + boolean hasValidLeash = runningTaskTarget != null + && runningTaskTarget.leash != null + && runningTaskTarget.leash.isValid(); boolean appCanEnterPip = !mDeviceState.isPipActive() - && runningTaskTarget != null + && hasValidLeash && runningTaskTarget.allowEnterPip && runningTaskTarget.taskInfo.pictureInPictureParams != null && runningTaskTarget.taskInfo.pictureInPictureParams.isAutoEnterEnabled(); HomeAnimationFactory homeAnimFactory = createHomeAnimationFactory(cookies, duration, isTranslucent, appCanEnterPip, runningTaskTarget); - mIsSwipingPipToHome = !mIsSwipeForStagedSplit && appCanEnterPip; + mIsSwipingPipToHome = !mIsSwipeForSplit && appCanEnterPip; final RectFSpringAnim[] windowAnim; if (mIsSwipingPipToHome) { 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() { @@ -1317,7 +1466,8 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, 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); @@ -1377,7 +1527,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, } } - private int calculateWindowRotation(RemoteAnimationTargetCompat runningTaskTarget, + private int calculateWindowRotation(RemoteAnimationTarget runningTaskTarget, RecentsOrientedState orientationState) { if (runningTaskTarget.rotationChange != 0 && TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) { @@ -1388,11 +1538,9 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, } } - /** - * TODO(b/195473090) handle multiple task simulators (if needed) for PIP - */ + @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() @@ -1409,12 +1557,27 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, 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 + // from TaskViewSimulator and fallback to the bounds in TaskInfo when it's originated + // from windowing modes other than full-screen. + if (winConfig.getWindowingMode() == WindowConfiguration.WINDOWING_MODE_FULLSCREEN) { + mRemoteTargetHandles[0].getTaskViewSimulator().getCurrentCropRect().round(appBounds); + } else { + appBounds.set(winConfig.getBounds()); + } final SwipePipToHomeAnimator.Builder builder = new SwipePipToHomeAnimator.Builder() .setContext(mContext) .setTaskId(runningTaskTarget.taskId) @@ -1422,7 +1585,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, .setLeash(runningTaskTarget.leash) .setSourceRectHint( runningTaskTarget.taskInfo.pictureInPictureParams.getSourceRectHint()) - .setAppBounds(taskInfo.configuration.windowConfiguration.getBounds()) + .setAppBounds(appBounds) .setHomeToWindowPositionMap(homeToWindowPositionMap) .setStartBounds(startRect) .setDestinationBounds(destinationBounds) @@ -1467,6 +1630,35 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, 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; @@ -1554,7 +1746,6 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, private void resumeLastTask() { if (mRecentsAnimationController != null) { mRecentsAnimationController.finish(false /* toRecents */, null); - ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", false); } doLogGesture(LAST_TASK, null); reset(); @@ -1603,19 +1794,22 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, * handler (in case of quick switch). */ private void cancelCurrentAnimation() { + ActiveGestureLog.INSTANCE.addLog( + "AbsSwipeUpHandler.cancelCurrentAnimation", + ActiveGestureErrorDetector.GestureEvent.CANCEL_CURRENT_ANIMATION); mCanceled = true; mCurrentShift.cancelAnimation(); // Cleanup when switching handlers mInputConsumerProxy.unregisterCallback(); mActivityInitListener.unregister(); - ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mActivityRestartListener); + TaskStackChangeListeners.getInstance().unregisterTaskStackListener( + mActivityRestartListener); mTaskSnapshot = null; } 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); } @@ -1660,10 +1854,6 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, * 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); @@ -1673,10 +1863,6 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, boolean wasVisible = mWasLauncherAlreadyVisible || mGestureStarted; mActivityInterface.onTransitionCancelled(wasVisible, mGestureState.getEndTarget()); - if (mRecentsAnimationTargets != null) { - 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); @@ -1689,7 +1875,6 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, 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 @@ -1698,16 +1883,27 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, 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(); @@ -1745,19 +1941,19 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, } 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() { @@ -1769,7 +1965,10 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, 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()); } @@ -1790,12 +1989,13 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, mSwipePipToHomeAnimator.getFinishTransaction(), mSwipePipToHomeAnimator.getContentOverlay()); mIsSwipingPipToHome = false; - } else if (mIsSwipeForStagedSplit) { + } else if (mIsSwipeForSplit) { // Transaction to hide the task to avoid flicker for entering PiP from split-screen. PictureInPictureSurfaceTransaction tx = new PictureInPictureSurfaceTransaction.Builder() .setAlpha(0f) .build(); + tx.setShouldDisableCanAffectSystemUiFlags(false); int[] taskIds = TopTaskTracker.INSTANCE.get(mContext).getRunningSplitTaskIds(); for (int taskId : taskIds) { mRecentsAnimationController.setFinishTaskTransaction(taskId, @@ -1812,31 +2012,20 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, } 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() { @@ -1881,6 +2070,9 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, 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) { @@ -1950,17 +2142,59 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, } @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. @@ -2047,6 +2281,35 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, 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) { + return Utilities.mapToRange(displacement, mTaskbarAppWindowThreshold, + mTaskbarCatchUpThreshold, 0, mTaskbarCatchUpThreshold, ACCEL_DEACCEL); + } + + return displacement; + } + private void setDividerShown(boolean shown, boolean immediate) { if (mDividerAnimator != null) { mDividerAnimator.cancel(); diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java index 2fcd286345..274b686ea4 100644 --- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java +++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java @@ -30,6 +30,7 @@ import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION 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.graphics.Rect; 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,21 +52,18 @@ import com.android.launcher3.R; 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.WindowBounds; +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.util.SplitScreenBounds; import com.android.quickstep.views.RecentsView; -import com.android.quickstep.views.TaskView; import com.android.systemui.shared.recents.model.ThumbnailData; -import com.android.systemui.shared.system.RemoteAnimationTargetCompat; import java.util.HashMap; import java.util.Optional; @@ -121,9 +120,6 @@ public abstract class BaseActivityInterface<STATE_TYPE extends BaseState<STATE_T 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); @@ -144,6 +140,11 @@ public abstract class BaseActivityInterface<STATE_TYPE extends BaseState<STATE_T } @Nullable + public DesktopVisibilityController getDesktopVisibilityController() { + return null; + } + + @Nullable public abstract TaskbarUIController getTaskbarController(); public final boolean isResumed() { @@ -164,7 +165,7 @@ public abstract class BaseActivityInterface<STATE_TYPE extends BaseState<STATE_T public abstract boolean switchToRecentsIfVisible(Runnable onCompleteCallback); public abstract Rect getOverviewWindowBounds( - Rect homeBounds, RemoteAnimationTargetCompat target); + Rect homeBounds, RemoteAnimationTarget target); public abstract boolean allowMinimizeSplitScreen(); @@ -186,19 +187,12 @@ public abstract class BaseActivityInterface<STATE_TYPE extends BaseState<STATE_T 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, @@ -258,7 +252,7 @@ public abstract class BaseActivityInterface<STATE_TYPE extends BaseState<STATE_T private void calculateTaskSizeInternal(Context context, DeviceProfile dp, Rect potentialTaskRect, float maxScale, int gravity, Rect outRect) { - PointF taskDimension = getTaskDimension(context, dp); + PointF taskDimension = getTaskDimension(dp); float scale = Math.min( potentialTaskRect.width() / taskDimension.x, @@ -270,47 +264,20 @@ public abstract class BaseActivityInterface<STATE_TYPE extends BaseState<STATE_T Gravity.apply(gravity, outWidth, outHeight, potentialTaskRect, outRect); } - private static PointF getTaskDimension(Context context, DeviceProfile dp) { + private static PointF getTaskDimension(DeviceProfile dp) { PointF dimension = new PointF(); - getTaskDimension(context, dp, dimension); + getTaskDimension(dp, dimension); return dimension; } /** * Gets the dimension of the task in the current system state. */ - public static void getTaskDimension(Context context, DeviceProfile dp, PointF out) { - if (dp.isMultiWindowMode) { - WindowBounds bounds = SplitScreenBounds.INSTANCE.getSecondaryWindowBounds(context); - out.x = bounds.availableSize.x; - out.y = bounds.availableSize.y; - if (!TaskView.clipLeft(dp)) { - out.x += bounds.insets.left; - } - if (!TaskView.clipRight(dp)) { - out.x += bounds.insets.right; - } - if (!TaskView.clipTop(dp)) { - out.y += bounds.insets.top; - } - if (!TaskView.clipBottom(dp)) { - out.y += bounds.insets.bottom; - } - } else { - out.x = dp.widthPx; - out.y = dp.heightPx; - if (TaskView.clipLeft(dp)) { - out.x -= dp.getInsets().left; - } - if (TaskView.clipRight(dp)) { - out.x -= dp.getInsets().right; - } - if (TaskView.clipTop(dp)) { - out.y -= dp.getInsets().top; - } - if (TaskView.clipBottom(dp)) { - out.y -= Math.max(dp.getInsets().bottom, dp.taskbarSize); - } + public static void getTaskDimension(DeviceProfile dp, PointF out) { + out.x = dp.widthPx; + out.y = dp.heightPx; + if (dp.isTablet) { + out.y -= dp.taskbarSize; } } @@ -341,7 +308,7 @@ public abstract class BaseActivityInterface<STATE_TYPE extends BaseState<STATE_T (taskRect.height() + dp.overviewTaskThumbnailTopMarginPx - dp.overviewRowSpacing) / 2f; - PointF taskDimension = getTaskDimension(context, dp); + PointF taskDimension = getTaskDimension(dp); float scale = (rowHeight - dp.overviewTaskThumbnailTopMarginPx) / taskDimension.y; int outWidth = Math.round(scale * taskDimension.x); int outHeight = Math.round(scale * taskDimension.y); @@ -512,33 +479,38 @@ public abstract class BaseActivityInterface<STATE_TYPE extends BaseState<STATE_T if (mIsAttachedToWindow == attached && animate) { return; } - mIsAttachedToWindow = attached; - RecentsView recentsView = mActivity.getOverviewPanel(); - if (attached) { - mHasEverAttachedToWindow = true; - } + mActivity.getStateManager() + .cancelStateElementAnimation(INDEX_RECENTS_FADE_ANIM); + mActivity.getStateManager() + .cancelStateElementAnimation(INDEX_RECENTS_TRANSLATE_X_ANIM); + + 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(animationDuration); + animatorSet.play(fadeAnim); - float fromTranslation = attached ? 1 : 0; + float fromTranslation = ADJACENT_PAGE_HORIZONTAL_OFFSET.get( + mActivity.getOverviewPanel()); float toTranslation = attached ? 0 : 1; - 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(); - } - fadeAnim.setInterpolator(attached ? INSTANT : ACCEL_2); - fadeAnim.setDuration(animate ? RECENTS_ATTACH_DURATION : 0).start(); + 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 ba61574f03..ae9fb0b385 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.animation.AnimatorSet; 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.fallback.RecentsState; 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 @@ public final class FallbackActivityInterface extends // set to zero prior to this class becoming active. } - @Override - public void onOneHandedModeStateChanged(boolean activated) { - // Do nothing for FallbackActivityInterface - } - /** 6 */ @Override public AnimationFactory prepareRecentsUI(RecentsAnimationDeviceState deviceState, @@ -120,8 +114,7 @@ public final class FallbackActivityInterface extends public RecentsView getVisibleRecentsView() { RecentsActivity activity = getCreatedActivity(); if (activity != null) { - if (activity.hasBeenResumed() - || (ENABLE_QUICKSTEP_LIVE_TILE.get() && isInLiveTileMode())) { + if (activity.hasBeenResumed() || isInLiveTileMode()) { return activity.getOverviewPanel(); } } @@ -134,7 +127,7 @@ public final class FallbackActivityInterface extends } @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 ee5bb44040..d4bebea55f 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; @@ -23,16 +24,14 @@ import static com.android.launcher3.GestureNavContract.EXTRA_ICON_POSITION; import static com.android.launcher3.GestureNavContract.EXTRA_ICON_SURFACE; import static com.android.launcher3.GestureNavContract.EXTRA_ON_FINISH_CALLBACK; import static com.android.launcher3.GestureNavContract.EXTRA_REMOTE_CALLBACK; -import static com.android.launcher3.Utilities.createHomeIntent; import static com.android.launcher3.anim.AnimatorListeners.forEndCallback; import static com.android.launcher3.anim.Interpolators.ACCEL; -import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME; +import static com.android.quickstep.OverviewComponentObserver.startHomeIntentSafely; import android.animation.ObjectAnimator; import android.annotation.TargetApi; import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityOptions; -import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; import android.graphics.Matrix; @@ -49,6 +48,7 @@ import android.os.ParcelUuid; 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; @@ -58,6 +58,7 @@ import androidx.annotation.Nullable; 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; @@ -66,12 +67,11 @@ import com.android.launcher3.util.DisplayController; 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; @@ -126,24 +126,24 @@ public class FallbackSwipeHandler extends } } - 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(); @@ -155,21 +155,17 @@ public class FallbackSwipeHandler extends 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) { gestureContractAnimationFactory.addGestureContract(intent, runningTaskTarget.taskInfo); } - try { - mContext.startActivity(intent, options.toBundle()); - } catch (NullPointerException | ActivityNotFoundException | SecurityException e) { - mContext.startActivity(createHomeIntent()); - } + startHomeIntentSafely(mContext, intent, options.toBundle()); } @Override - protected boolean handleTaskAppeared(RemoteAnimationTargetCompat[] appearedTaskTarget) { + protected boolean handleTaskAppeared(RemoteAnimationTarget[] appearedTaskTarget) { if (mActiveAnimationFactory != null && mActiveAnimationFactory.handleHomeTaskAppeared(appearedTaskTarget)) { mActiveAnimationFactory = null; @@ -284,13 +280,13 @@ public class FallbackSwipeHandler extends 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); } @@ -309,12 +305,12 @@ public class FallbackSwipeHandler extends } } - 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 3b52e9178e..31b78b3ec3 100644 --- a/quickstep/src/com/android/quickstep/GestureState.java +++ b/quickstep/src/com/android/quickstep/GestureState.java @@ -19,25 +19,30 @@ import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKG 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; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Set; /** @@ -82,11 +87,11 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL private static final String TAG = "GestureState"; - private static final ArrayList<String> STATE_NAMES = new ArrayList<>(); + private static final List<String> STATE_NAMES = new ArrayList<>(); public static final GestureState DEFAULT_STATE = new GestureState(); private static int FLAG_COUNT = 0; - private static int getFlagForIndex(String name) { + private static int getNextStateFlag(String name) { if (DEBUG_STATES) { STATE_NAMES.add(name); } @@ -97,36 +102,36 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL // Called when the end target as been set public static final int STATE_END_TARGET_SET = - getFlagForIndex("STATE_END_TARGET_SET"); + getNextStateFlag("STATE_END_TARGET_SET"); // Called when the end target animation has finished public static final int STATE_END_TARGET_ANIMATION_FINISHED = - getFlagForIndex("STATE_END_TARGET_ANIMATION_FINISHED"); + getNextStateFlag("STATE_END_TARGET_ANIMATION_FINISHED"); // Called when the recents animation has been requested to start public static final int STATE_RECENTS_ANIMATION_INITIALIZED = - getFlagForIndex("STATE_RECENTS_ANIMATION_INITIALIZED"); + getNextStateFlag("STATE_RECENTS_ANIMATION_INITIALIZED"); // Called when the recents animation is started and the TaskAnimationManager has been updated // with the controller and targets public static final int STATE_RECENTS_ANIMATION_STARTED = - getFlagForIndex("STATE_RECENTS_ANIMATION_STARTED"); + getNextStateFlag("STATE_RECENTS_ANIMATION_STARTED"); // Called when the recents animation is canceled public static final int STATE_RECENTS_ANIMATION_CANCELED = - getFlagForIndex("STATE_RECENTS_ANIMATION_CANCELED"); + getNextStateFlag("STATE_RECENTS_ANIMATION_CANCELED"); // Called when the recents animation finishes public static final int STATE_RECENTS_ANIMATION_FINISHED = - getFlagForIndex("STATE_RECENTS_ANIMATION_FINISHED"); + getNextStateFlag("STATE_RECENTS_ANIMATION_FINISHED"); // Always called when the recents animation ends (regardless of cancel or finish) public static final int STATE_RECENTS_ANIMATION_ENDED = - getFlagForIndex("STATE_RECENTS_ANIMATION_ENDED"); + getNextStateFlag("STATE_RECENTS_ANIMATION_ENDED"); // Called when RecentsView stops scrolling and settles on a TaskView. public static final int STATE_RECENTS_SCROLLING_FINISHED = - getFlagForIndex("STATE_RECENTS_SCROLLING_FINISHED"); + getNextStateFlag("STATE_RECENTS_SCROLLING_FINISHED"); // Needed to interact with the current activity private final Intent mHomeIntent; @@ -137,7 +142,7 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL 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; @@ -152,7 +157,8 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL 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; } @@ -174,10 +180,23 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL 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. */ @@ -252,7 +271,7 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL /** * 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); @@ -310,7 +329,21 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL 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/ImageActionsApi.java b/quickstep/src/com/android/quickstep/ImageActionsApi.java index 154848d224..227380664c 100644 --- a/quickstep/src/com/android/quickstep/ImageActionsApi.java +++ b/quickstep/src/com/android/quickstep/ImageActionsApi.java @@ -78,16 +78,17 @@ public class ImageActionsApi { addImageAndSendIntent(crop, intent, true, exceptionCallback); } - @UiThread private void addImageAndSendIntent(@Nullable Rect crop, Intent intent, boolean setData, @Nullable Runnable exceptionCallback) { - if (mBitmapSupplier.get() == null) { - Log.e(TAG, "No snapshot available, not starting share."); - return; - } - UI_HELPER_EXECUTOR.execute(() -> persistBitmapAndStartActivity(mContext, - mBitmapSupplier.get(), crop, intent, (uri, intentForUri) -> { + UI_HELPER_EXECUTOR.execute(() -> { + Bitmap bitmap = mBitmapSupplier.get(); + if (bitmap == null) { + Log.e(TAG, "No snapshot available, not starting share."); + return; + } + persistBitmapAndStartActivity(mContext, + bitmap, crop, intent, (uri, intentForUri) -> { intentForUri.addFlags(FLAG_GRANT_READ_URI_PERMISSION); if (setData) { intentForUri.setData(uri); @@ -95,7 +96,8 @@ public class ImageActionsApi { intentForUri.putExtra(EXTRA_STREAM, uri); } return new Intent[]{intentForUri}; - }, TAG, exceptionCallback)); + }, TAG, exceptionCallback); + }); } /** diff --git a/quickstep/src/com/android/quickstep/KtR.java b/quickstep/src/com/android/quickstep/KtR.java deleted file mode 100644 index 758c6e08ef..0000000000 --- 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 c92fea5a14..e4824e166d 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.OVERVIEW; 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 @@ import java.util.function.Predicate; * {@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 @@ public final class LauncherActivityInterface extends } @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 @@ public final class LauncherActivityInterface extends @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 @@ public final class LauncherActivityInterface extends @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 @@ public final class LauncherActivityInterface extends 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 @@ public final class LauncherActivityInterface extends 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 @@ public final class LauncherActivityInterface extends } @Override - public Rect getOverviewWindowBounds(Rect homeBounds, RemoteAnimationTargetCompat target) { + public Rect getOverviewWindowBounds(Rect homeBounds, RemoteAnimationTarget target) { return homeBounds; } @@ -291,10 +293,6 @@ public final class LauncherActivityInterface extends } else { om.hideOverlay(150); } - LauncherTaskbarUIController taskbarController = getTaskbarController(); - if (taskbarController != null) { - taskbarController.hideEdu(); - } } @Override @@ -318,7 +316,7 @@ public final class LauncherActivityInterface extends } @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 fd9f922005..27417516a4 100644 --- a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java +++ b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java @@ -36,17 +36,16 @@ import android.view.SurfaceControl; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; import android.window.BackEvent; +import android.window.BackProgressAnimator; import android.window.IOnBackInvokedCallback; import com.android.launcher3.AbstractFloatingView; -import com.android.launcher3.BaseQuickstepLauncher; import com.android.launcher3.QuickstepTransitionManager; import com.android.launcher3.R; import com.android.launcher3.Utilities; +import com.android.launcher3.uioverrides.QuickstepLauncher; import com.android.quickstep.util.RectFSpringAnim; import com.android.systemui.shared.system.QuickStepContract; -import com.android.systemui.shared.system.RemoteAnimationTargetCompat; -import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat; /** * Controls the animation of swiping back and returning to launcher. @@ -75,7 +74,7 @@ public class LauncherBackAnimationController { private final RectF mCancelRect = new RectF(); /** The current window position. */ private final RectF mCurrentRect = new RectF(); - private final BaseQuickstepLauncher mLauncher; + private final QuickstepLauncher mLauncher; private final int mWindowScaleMarginX; /** Max window translation in the Y axis. */ private final int mWindowMaxDeltaY; @@ -84,16 +83,17 @@ public class LauncherBackAnimationController { private final Interpolator mCancelInterpolator; private final PointF mInitialTouchPos = new PointF(); - private RemoteAnimationTargetCompat mBackTarget; + private RemoteAnimationTarget mBackTarget; private SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction(); private boolean mSpringAnimationInProgress = false; private boolean mAnimatorSetInProgress = false; private float mBackProgress = 0; private boolean mBackInProgress = false; private IOnBackInvokedCallback mBackCallback; + private BackProgressAnimator mProgressAnimator = new BackProgressAnimator(); public LauncherBackAnimationController( - BaseQuickstepLauncher launcher, + QuickstepLauncher launcher, QuickstepTransitionManager quickstepTransitionManager) { mLauncher = launcher; mQuickstepTransitionManager = quickstepTransitionManager; @@ -119,30 +119,41 @@ public class LauncherBackAnimationController { mBackCallback = new IOnBackInvokedCallback.Stub() { @Override public void onBackCancelled() { - handler.post(() -> resetPositionAnimated()); + handler.post(() -> { + resetPositionAnimated(); + mProgressAnimator.reset(); + }); } @Override public void onBackInvoked() { - handler.post(() -> startTransition()); + handler.post(() -> { + startTransition(); + mProgressAnimator.reset(); + }); } @Override public void onBackProgressed(BackEvent backEvent) { - mBackProgress = backEvent.getProgress(); - // TODO: Update once the interpolation curve spec is finalized. - mBackProgress = - 1 - (1 - mBackProgress) * (1 - mBackProgress) * (1 - - mBackProgress); - if (!mBackInProgress) { - startBack(backEvent); - } else { - updateBackProgress(mBackProgress, backEvent); - } + handler.post(() -> { + mProgressAnimator.onBackProgressed(backEvent); + }); } @Override - public void onBackStarted() { } + public void onBackStarted(BackEvent backEvent) { + handler.post(() -> { + startBack(backEvent); + mProgressAnimator.onBackStarted(backEvent, event -> { + mBackProgress = event.getProgress(); + // TODO: Update once the interpolation curve spec is finalized. + mBackProgress = + 1 - (1 - mBackProgress) * (1 - mBackProgress) * (1 + - mBackProgress); + updateBackProgress(mBackProgress, event); + }); + }); + } }; SystemUiProxy.INSTANCE.get(mLauncher).setBackToLauncherCallback(mBackCallback); } @@ -170,6 +181,7 @@ public class LauncherBackAnimationController { if (mBackCallback != null) { SystemUiProxy.INSTANCE.get(mLauncher).clearBackToLauncherCallback(mBackCallback); } + mProgressAnimator.reset(); mBackCallback = null; } @@ -183,33 +195,25 @@ public class LauncherBackAnimationController { mTransaction.show(appTarget.leash).apply(); mTransaction.setAnimationTransaction(); - mBackTarget = new RemoteAnimationTargetCompat(appTarget); + mBackTarget = appTarget; mInitialTouchPos.set(backEvent.getTouchX(), backEvent.getTouchY()); // TODO(b/218916755): Offset start rectangle in multiwindow mode. mStartRect.set(mBackTarget.windowConfiguration.getMaxBounds()); + mCurrentRect.set(mStartRect); } private void updateBackProgress(float progress, BackEvent event) { - if (mBackTarget == null) { + if (!mBackInProgress || mBackTarget == null) { return; } float screenWidth = mStartRect.width(); float screenHeight = mStartRect.height(); - float dX = Math.abs(event.getTouchX() - mInitialTouchPos.x); - // The 'follow width' is the width of the window if it completely matches - // the gesture displacement. - float followWidth = screenWidth - dX; - // The 'progress width' is the width of the window if it strictly linearly interpolates - // to minimum scale base on progress. - float progressWidth = Utilities.mapRange(progress, 1, MIN_WINDOW_SCALE) * screenWidth; - // The final width is derived from interpolating between the follow with and progress width - // using gesture progress. - float width = Utilities.mapRange(progress, followWidth, progressWidth); + float width = Utilities.mapRange(progress, 1, MIN_WINDOW_SCALE) * screenWidth; float height = screenHeight / screenWidth * width; float deltaYRatio = (event.getTouchY() - mInitialTouchPos.y) / screenHeight; // Base the window movement in the Y axis on the touch movement in the Y axis. - float deltaY = (float) Math.sin(deltaYRatio * Math.PI * 0.5f) * mWindowMaxDeltaY; + float deltaY = (float) Math.sin(deltaYRatio * Math.PI * 0.5f) * mWindowMaxDeltaY * progress; // Move the window along the Y axis. float top = (screenHeight - height) * 0.5f + deltaY; // Move the window along the X axis. @@ -242,20 +246,17 @@ public class LauncherBackAnimationController { /** Transform the target window to match the target rect. */ private void applyTransform(RectF targetRect, float cornerRadius) { - SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder builder = - new SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder(mBackTarget.leash); final float scale = targetRect.width() / mStartRect.width(); mTransformMatrix.reset(); mTransformMatrix.setScale(scale, scale); mTransformMatrix.postTranslate(targetRect.left, targetRect.top); - builder.withMatrix(mTransformMatrix) - .withWindowCrop(mStartRect) - .withCornerRadius(cornerRadius); - SyncRtSurfaceTransactionApplierCompat.SurfaceParams surfaceParams = builder.build(); - if (surfaceParams.surface.isValid()) { - surfaceParams.applyTo(mTransaction); + if (mBackTarget.leash.isValid()) { + mTransaction.setMatrix(mBackTarget.leash, mTransformMatrix, new float[9]); + mTransaction.setWindowCrop(mBackTarget.leash, mStartRect); + mTransaction.setCornerRadius(mBackTarget.leash, cornerRadius); } + mTransaction.apply(); } @@ -284,8 +285,8 @@ public class LauncherBackAnimationController { mBackProgress, mWindowScaleStartCornerRadius, mWindowScaleEndCornerRadius); Pair<RectFSpringAnim, AnimatorSet> pair = mQuickstepTransitionManager.createWallpaperOpenAnimations( - new RemoteAnimationTargetCompat[]{mBackTarget}, - new RemoteAnimationTargetCompat[]{}, + new RemoteAnimationTarget[]{mBackTarget}, + new RemoteAnimationTarget[0], false /* fromUnlock */, mCurrentRect, cornerRadius); diff --git a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java index 50d1244c81..bb781c82b7 100644 --- a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java +++ b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java @@ -30,15 +30,16 @@ import android.graphics.RectF; 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.FloatingWidgetView; 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 @@ import java.util.ArrayList; * 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 @@ public class LauncherSwipeHandlerV2 extends @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,14 +84,15 @@ public class LauncherSwipeHandlerV2 extends 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) { mActivity.setHintUserWillBeActive(); } - if (!canUseWorkspaceView || appCanEnterPip || mIsSwipeForStagedSplit) { + if (!canUseWorkspaceView || appCanEnterPip || mIsSwipeForSplit) { return new LauncherHomeAnimationFactory(); } if (workspaceView instanceof LauncherAppWidgetHostView) { @@ -143,7 +144,7 @@ public class LauncherSwipeHandlerV2 extends 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 b3875aef52..a68bea2cc0 100644 --- a/quickstep/src/com/android/quickstep/MultiStateCallback.java +++ b/quickstep/src/com/android/quickstep/MultiStateCallback.java @@ -22,7 +22,12 @@ import android.os.Looper; import android.util.Log; import android.util.SparseArray; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + import com.android.launcher3.config.FeatureFlags; +import com.android.quickstep.util.ActiveGestureErrorDetector; +import com.android.quickstep.util.ActiveGestureLog; import java.util.ArrayList; import java.util.LinkedList; @@ -41,17 +46,29 @@ public class MultiStateCallback { private final SparseArray<ArrayList<Consumer<Boolean>>> mStateChangeListeners = new SparseArray<>(); + @NonNull private final TrackedEventsMapper mTrackedEventsMapper; + private final String[] mStateNames; private int mState = 0; public MultiStateCallback(String[] stateNames) { + this(stateNames, stateFlag -> null); + } + + public MultiStateCallback( + String[] stateNames, + @NonNull TrackedEventsMapper trackedEventsMapper) { mStateNames = DEBUG_STATES ? stateNames : null; + mTrackedEventsMapper = trackedEventsMapper; } /** * Adds the provided state flags to the global state on the UI thread and executes any callbacks * as a result. + * + * Also tracks the provided gesture events for error detection. Each provided event must be + * associated with one provided state flag. */ public void setStateOnUiThread(int stateFlag) { if (Looper.myLooper() == Looper.getMainLooper()) { @@ -69,7 +86,9 @@ public class MultiStateCallback { Log.d(TAG, "[" + System.identityHashCode(this) + "] Adding " + convertToFlagNames(stateFlag) + " to " + convertToFlagNames(mState)); } - + if (FeatureFlags.ENABLE_GESTURE_ERROR_DETECTION.get()) { + trackGestureEvents(stateFlag); + } final int oldState = mState; mState = mState | stateFlag; @@ -87,6 +106,26 @@ public class MultiStateCallback { notifyStateChangeListeners(oldState); } + private void trackGestureEvents(int stateFlags) { + for (int index = 0; (stateFlags >> index) != 0; index++) { + if ((stateFlags & (1 << index)) == 0) { + continue; + } + ActiveGestureErrorDetector.GestureEvent gestureEvent = + mTrackedEventsMapper.getTrackedEventForState(1 << index); + if (gestureEvent == null) { + continue; + } + if (gestureEvent.mLogEvent && gestureEvent.mTrackEvent) { + ActiveGestureLog.INSTANCE.addLog(gestureEvent.name(), gestureEvent); + } else if (gestureEvent.mLogEvent) { + ActiveGestureLog.INSTANCE.addLog(gestureEvent.name()); + } else if (gestureEvent.mTrackEvent) { + ActiveGestureLog.INSTANCE.trackEvent(gestureEvent); + } + } + } + /** * Adds the provided state flags to the global state and executes any change handlers * as a result. @@ -174,4 +213,7 @@ public class MultiStateCallback { 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 895cf89382..1b05fd272d 100644 --- a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java +++ b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java @@ -32,9 +32,9 @@ import android.view.MotionEvent; import android.view.Surface; import com.android.launcher3.R; -import com.android.launcher3.ResourceUtils; +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 dffdc5a641..5a09e021b2 100644 --- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java +++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java @@ -116,7 +116,7 @@ public class OverviewCommandHelper { */ @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 @@ public class OverviewCommandHelper { RunnableList callbackList = null; if (taskView != null) { taskView.setEndQuickswitchCuj(true); - callbackList = taskView.launchTaskAnimated(); + callbackList = taskView.launchTasks(); } if (callbackList != null) { @@ -193,7 +193,20 @@ public class OverviewCommandHelper { } } - if (activityInterface.switchToRecentsIfVisible(() -> scheduleNextTask(cmd))) { + final Runnable completeCallback = () -> { + if (cmd.type == TYPE_SHOW_NEXT_FOCUS) { + RecentsView rv = activityInterface.getVisibleRecentsView(); + // When the overview is launched via alt tab (cmd type is TYPE_SHOW_NEXT_FOCUS), + // the touch mode somehow is not change to false by the Android framework. + // The subsequent tab to go through tasks in overview can only be dispatched to + // focuses views, while focus can only be requested in + // {@link View#requestFocusNoSearch(int, Rect)} when touch mode is false. To note, + // here we launch overview from home. + rv.getViewRootImpl().touchModeChanged(false); + } + scheduleNextTask(cmd); + }; + if (activityInterface.switchToRecentsIfVisible(completeCallback)) { // If successfully switched, wait until animation finishes return false; } @@ -227,14 +240,21 @@ public class OverviewCommandHelper { 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(); } } }; + RecentsView<?, ?> visibleRecentsView = activityInterface.getVisibleRecentsView(); + if (visibleRecentsView != null) { + visibleRecentsView.moveFocusedTaskToFront(); + } if (mTaskAnimationManager.isRecentsAnimationRunning()) { cmd.mActiveCallbacks = mTaskAnimationManager.continueRecentsAnimation(gestureState); cmd.mActiveCallbacks.addListener(interactionHandler); @@ -264,6 +284,13 @@ public class OverviewCommandHelper { 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 0efe6666a8..83f2a0a114 100644 --- a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java +++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java @@ -20,11 +20,11 @@ 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 com.android.launcher3.Utilities.createHomeIntent; 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; @@ -33,8 +33,13 @@ 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; +import androidx.annotation.Nullable; + import com.android.launcher3.tracing.OverviewComponentObserverProto; import com.android.launcher3.tracing.TouchInteractionServiceProto; import com.android.launcher3.util.SimpleBroadcastReceiver; @@ -50,6 +55,8 @@ import java.util.function.Consumer; * and provide callers the relevant classes. */ public final class OverviewComponentObserver { + private static final String TAG = "OverviewComponentObserver"; + private final BroadcastReceiver mUserPreferenceChangeReceiver = new SimpleBroadcastReceiver(this::updateOverviewTargets); private final BroadcastReceiver mOtherHomeAppUpdateReceiver = @@ -111,11 +118,6 @@ public final class OverviewComponentObserver { 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) { @@ -147,7 +149,12 @@ public final class OverviewComponentObserver { } } - 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; @@ -276,4 +283,34 @@ public final class OverviewComponentObserver { overviewComponentObserver.setOverviewActivityResumed(mActivityInterface.isResumed()); serviceProto.setOverviewComponentObvserver(overviewComponentObserver); } + + /** + * Starts the intent for the current home activity. + */ + public static void startHomeIntentSafely(@NonNull Context context, @Nullable Bundle options) { + RecentsAnimationDeviceState deviceState = new RecentsAnimationDeviceState(context); + OverviewComponentObserver observer = new OverviewComponentObserver(context, deviceState); + Intent intent = observer.getHomeIntent(); + observer.onDestroy(); + deviceState.destroy(); + startHomeIntentSafely(context, intent, options); + } + + /** + * Starts the intent for the current home activity. + */ + public static void startHomeIntentSafely( + @NonNull Context context, @NonNull Intent homeIntent, @Nullable Bundle options) { + try { + context.startActivity(homeIntent, options); + } catch (NullPointerException | ActivityNotFoundException | SecurityException e) { + context.startActivity(createHomeIntent(), options); + } + } + + private static Intent createHomeIntent() { + return new Intent(Intent.ACTION_MAIN) + .addCategory(Intent.CATEGORY_HOME) + .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + } } diff --git a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java index 528fb97983..54e4a0d3f1 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.TestProtocol; +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 @@ public class QuickstepTestInformationHandler extends TestInformationHandler { 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 @@ public class QuickstepTestInformationHandler extends TestInformationHandler { 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 097850fd6f..b33ceca838 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,19 +32,21 @@ import android.util.SparseBooleanArray; 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.StagedSplitBounds; +import com.android.wm.shell.util.SplitBounds; import java.io.PrintWriter; 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 @@ public class RecentTasksList { 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; @@ -62,8 +69,12 @@ public class RecentTasksList { private TaskLoadResult mResultsBg = INVALID_RESULT; private TaskLoadResult mResultsUi = INVALID_RESULT; - public RecentTasksList(LooperExecutor mainThreadExecutor, - KeyguardManagerCompat keyguardManager, SystemUiProxy sysUiProxy) { + private RecentsModel.RunningTasksListener mRunningTasksListener; + // Tasks are stored in order of least recently launched to most recently launched. + private ArrayList<ActivityManager.RunningTaskInfo> mRunningTasks; + + public RecentTasksList(LooperExecutor mainThreadExecutor, KeyguardManager keyguardManager, + SystemUiProxy sysUiProxy) { mMainThreadExecutor = mainThreadExecutor; mKeyguardManager = keyguardManager; mChangeId = 1; @@ -73,7 +84,26 @@ public class RecentTasksList { public void onRecentTasksChanged() throws RemoteException { mMainThreadExecutor.execute(RecentTasksList.this::onRecentTasksChanged); } + + @Override + public void onRunningTaskAppeared(ActivityManager.RunningTaskInfo taskInfo) { + mMainThreadExecutor.execute(() -> { + RecentTasksList.this.onRunningTaskAppeared(taskInfo); + }); + } + + @Override + public void onRunningTaskVanished(ActivityManager.RunningTaskInfo taskInfo) { + mMainThreadExecutor.execute(() -> { + RecentTasksList.this.onRunningTaskVanished(taskInfo); + }); + } }); + // We may receive onRunningTaskAppeared events later for tasks which have already been + // included in the list returned by mSysUiProxy.getRunningTasks(), or may receive + // onRunningTaskVanished for tasks not included in the returned list. These cases will be + // addressed when the tasks are added to/removed from mRunningTasks. + initRunningTasks(mSysUiProxy.getRunningTasks(Integer.MAX_VALUE)); } @VisibleForTesting @@ -101,14 +131,18 @@ public class RecentTasksList { * @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); }); @@ -128,7 +162,11 @@ public class RecentTasksList { 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); } }); @@ -154,6 +192,59 @@ public class RecentTasksList { mChangeId++; } + /** + * Registers a listener for running tasks + */ + public void registerRunningTasksListener(RecentsModel.RunningTasksListener listener) { + mRunningTasksListener = listener; + } + + /** + * Removes the previously registered running tasks listener + */ + public void unregisterRunningTasksListener() { + mRunningTasksListener = null; + } + + private void initRunningTasks(ArrayList<ActivityManager.RunningTaskInfo> runningTasks) { + // Tasks are retrieved in order of most recently launched/used to least recently launched. + mRunningTasks = new ArrayList<>(runningTasks); + Collections.reverse(mRunningTasks); + } + + /** + * Gets the set of running tasks. + */ + public ArrayList<ActivityManager.RunningTaskInfo> getRunningTasks() { + return mRunningTasks; + } + + private void onRunningTaskAppeared(ActivityManager.RunningTaskInfo taskInfo) { + // Make sure this task is not already in the list + for (ActivityManager.RunningTaskInfo existingTask : mRunningTasks) { + if (taskInfo.taskId == existingTask.taskId) { + return; + } + } + mRunningTasks.add(taskInfo); + if (mRunningTasksListener != null) { + mRunningTasksListener.onRunningTasksChanged(); + } + } + + private void onRunningTaskVanished(ActivityManager.RunningTaskInfo taskInfo) { + // Find the task from the list of running tasks, if it exists + for (ActivityManager.RunningTaskInfo existingTask : mRunningTasks) { + if (existingTask.taskId != taskInfo.taskId) continue; + + mRunningTasks.remove(existingTask); + if (mRunningTasksListener != null) { + mRunningTasksListener.onRunningTasksChanged(); + } + return; + } + } + /** * Loads and creates a list of all the recent tasks. */ @@ -177,9 +268,15 @@ public class RecentTasksList { }; 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) @@ -195,19 +292,33 @@ public class RecentTasksList { tmpLockedUsers.get(task2Key.userId) /* isLocked */); task2.setLastSnapshotData(taskInfo2); } - final SplitConfigurationOptions.StagedSplitBounds launcherSplitBounds = - convertSplitBounds(rawTask.mStagedSplitBounds); + final SplitConfigurationOptions.SplitBounds launcherSplitBounds = + convertSplitBounds(rawTask.getSplitBounds()); allTasks.add(new GroupTask(task1, task2, launcherSplitBounds)); } return allTasks; } - private SplitConfigurationOptions.StagedSplitBounds convertSplitBounds( - StagedSplitBounds shellSplitBounds) { + 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 ? null : - new SplitConfigurationOptions.StagedSplitBounds( + new SplitConfigurationOptions.SplitBounds( shellSplitBounds.leftTopBounds, shellSplitBounds.rightBottomBounds, shellSplitBounds.leftTopTaskId, shellSplitBounds.rightBottomTaskId); } @@ -215,7 +326,7 @@ public class RecentTasksList { 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; } @@ -225,8 +336,14 @@ public class RecentTasksList { 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(); @@ -234,8 +351,14 @@ public class RecentTasksList { 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 4f0b9767f9..dc405ff8ba 100644 --- a/quickstep/src/com/android/quickstep/RecentsActivity.java +++ b/quickstep/src/com/android/quickstep/RecentsActivity.java @@ -15,34 +15,33 @@ */ package com.android.quickstep; -import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION; -import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE; +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.Utilities.createHomeIntent; -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.TestProtocol.BAD_STATE; -import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL; +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.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 +81,6 @@ import com.android.quickstep.util.TISBindHelper; import com.android.quickstep.views.OverviewActionsView; import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskView; -import com.android.systemui.shared.system.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 +108,6 @@ public final class RecentsActivity extends StatefulActivity<RecentsState> { 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 +132,7 @@ public final class RecentsActivity extends StatefulActivity<RecentsState> { SplitSelectStateController controller = new SplitSelectStateController(this, mHandler, getStateManager(), - null /* depthController */); + /* depthController */ null, getStatsLogManager()); mDragLayer.recreateControllers(); mFallbackRecentsView.init(mActionsView, controller); @@ -165,7 +159,7 @@ public final class RecentsActivity extends StatefulActivity<RecentsState> { @Override public void onMultiWindowModeChanged(boolean isInMultiWindowMode, Configuration newConfig) { - onHandleConfigChanged(); + onHandleConfigurationChanged(); super.onMultiWindowModeChanged(isInMultiWindowMode, newConfig); } @@ -175,11 +169,8 @@ public final class RecentsActivity extends StatefulActivity<RecentsState> { 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 +240,9 @@ public final class RecentsActivity extends StatefulActivity<RecentsState> { mActivityLaunchAnimationRunner = new RemoteAnimationFactory() { @Override - public void onCreateAnimation(int transit, RemoteAnimationTargetCompat[] appTargets, - RemoteAnimationTargetCompat[] wallpaperTargets, - RemoteAnimationTargetCompat[] nonAppTargets, AnimationResult result) { + public void onCreateAnimation(int transit, RemoteAnimationTarget[] appTargets, + RemoteAnimationTarget[] wallpaperTargets, + RemoteAnimationTarget[] nonAppTargets, AnimationResult result) { mHandler.removeCallbacks(mAnimationStartTimeoutRunnable); AnimatorSet anim = composeRecentsLaunchAnimator(taskView, appTargets, wallpaperTargets, nonAppTargets); @@ -269,12 +260,12 @@ public final class RecentsActivity extends StatefulActivity<RecentsState> { 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 +279,9 @@ public final class RecentsActivity extends StatefulActivity<RecentsState> { * 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 +305,6 @@ public final class RecentsActivity extends StatefulActivity<RecentsState> { 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 +331,6 @@ public final class RecentsActivity extends StatefulActivity<RecentsState> { mStateManager = new StateManager<>(this, RecentsState.BG_LAUNCHER); - mOldConfig = new Configuration(getResources().getConfiguration()); initDeviceProfile(); setupViews(); @@ -351,16 +340,6 @@ public final class RecentsActivity extends StatefulActivity<RecentsState> { } @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,38 +392,33 @@ public final class RecentsActivity extends StatefulActivity<RecentsState> { } 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()); - startActivity(createHomeIntent(), - ActivityOptionsCompat.makeRemoteAnimation(adapterCompat).toBundle()); + ActivityOptions options = ActivityOptions.makeRemoteAnimation( + new RemoteAnimationAdapter(runner, HOME_APPEAR_DURATION, 0), + new RemoteTransition(runner.toRemoteTransition(), getIApplicationThread())); + startHomeIntentSafely(this, options.toBundle()); } private final RemoteAnimationFactory mAnimationToHomeFactory = new RemoteAnimationFactory() { @Override - public void onCreateAnimation(int transit, RemoteAnimationTargetCompat[] appTargets, - RemoteAnimationTargetCompat[] wallpaperTargets, - RemoteAnimationTargetCompat[] nonAppTargets, AnimationResult result) { + public void onCreateAnimation(int transit, RemoteAnimationTarget[] appTargets, + RemoteAnimationTarget[] wallpaperTargets, + RemoteAnimationTarget[] nonAppTargets, AnimationResult result) { AnimatorPlaybackController controller = getStateManager() .createAnimationToNewWorkspace(RecentsState.BG_LAUNCHER, HOME_APPEAR_DURATION); controller.dispatchOnStart(); RemoteAnimationTargets targets = new RemoteAnimationTargets( appTargets, wallpaperTargets, nonAppTargets, MODE_OPENING); - for (RemoteAnimationTargetCompat app : targets.apps) { + for (RemoteAnimationTarget app : targets.apps) { new Transaction().setAlpha(app.leash, 1).apply(); } AnimatorSet anim = new AnimatorSet(); diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java index 51ae56b96d..b82ff03b34 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 androidx.annotation.UiThread; 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 @@ public class RecentsAnimationCallbacks implements @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 @@ public class RecentsAnimationCallbacks implements 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 @@ public class RecentsAnimationCallbacks implements @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 @@ public class RecentsAnimationCallbacks implements @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 @@ public class RecentsAnimationCallbacks implements 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 @@ public class RecentsAnimationCallbacks implements /** * 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 2007ee1ebd..4adfae5ee8 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java @@ -18,11 +18,13 @@ package com.android.quickstep; 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 androidx.annotation.UiThread; 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; @@ -104,8 +107,6 @@ public class RecentsAnimationController { } if (mSplitScreenMinimized != splitScreenMinimized) { mSplitScreenMinimized = splitScreenMinimized; - UI_HELPER_EXECUTOR.execute(() -> SystemUiProxy.INSTANCE.get(context) - .setSplitScreenMinimized(splitScreenMinimized)); } } @@ -114,7 +115,7 @@ public class RecentsAnimationController { * {@link RecentsAnimationCallbacks#onTasksAppeared}}. */ @UiThread - public void removeTaskTarget(@NonNull RemoteAnimationTargetCompat target) { + public void removeTaskTarget(@NonNull RemoteAnimationTarget target) { UI_HELPER_EXECUTOR.execute(() -> mController.removeTask(target.taskId)); } @@ -155,6 +156,10 @@ public class RecentsAnimationController { mPendingFinishCallbacks.add(callback); return; } + ActiveGestureLog.INSTANCE.addLog( + /* event= */ "finishRecentsAnimation", + /* extras= */ toRecents, + /* gestureEvent= */ FINISH_RECENTS_ANIMATION); // Finish not yet requested mFinishRequested = true; @@ -165,6 +170,8 @@ public class RecentsAnimationController { 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); }); } @@ -174,7 +181,12 @@ public class RecentsAnimationController { */ @UiThread public void cleanupScreenshot() { - UI_HELPER_EXECUTOR.execute(() -> mController.cleanupScreenshot()); + UI_HELPER_EXECUTOR.execute(() -> { + ActiveGestureLog.INSTANCE.addLog( + "cleanupScreenshot", + ActiveGestureErrorDetector.GestureEvent.CLEANUP_SCREENSHOT); + mController.cleanupScreenshot(); + }); } /** @@ -222,7 +234,6 @@ public class RecentsAnimationController { */ public void enableInputConsumer() { UI_HELPER_EXECUTOR.submit(() -> { - mController.hideCurrentInputMethod(); mController.setInputConsumerEnabled(true); }); } diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java index 4fb7e6ba06..9e25555eec 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java @@ -23,9 +23,9 @@ 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; @@ -46,10 +46,7 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_S 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; @@ -61,13 +58,14 @@ 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.NavigationMode; import com.android.launcher3.util.SettingsCache; +import com.android.launcher3.util.SimpleBroadcastReceiver; import com.android.quickstep.TopTaskTracker.CachedTaskInfo; import com.android.quickstep.util.NavBarPosition; import com.android.systemui.shared.system.ActivityManagerWrapper; @@ -113,18 +111,15 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener { private boolean mIsUserUnlocked; private final ArrayList<Runnable> mUserUnlockedActions = new ArrayList<>(); - private final BroadcastReceiver mUserUnlockedReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (ACTION_USER_UNLOCKED.equals(intent.getAction())) { - mIsUserUnlocked = true; - notifyUserUnlocked(); - } + private final SimpleBroadcastReceiver mUserUnlockedReceiver = new SimpleBroadcastReceiver(i -> { + if (ACTION_USER_UNLOCKED.equals(i.getAction())) { + mIsUserUnlocked = true; + notifyUserUnlocked(); } - }; + }); private int mGestureBlockingTaskId = -1; - private Region mExclusionRegion; + private @NonNull Region mExclusionRegion = new Region(); private SystemGestureExclusionListenerCompat mExclusionListener; public RecentsAnimationDeviceState(Context context) { @@ -152,16 +147,19 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener { mIsUserUnlocked = context.getSystemService(UserManager.class) .isUserUnlocked(Process.myUserHandle()); if (!mIsUserUnlocked) { - mContext.registerReceiver(mUserUnlockedReceiver, - new IntentFilter(ACTION_USER_UNLOCKED)); + mUserUnlockedReceiver.register(mContext, ACTION_USER_UNLOCKED); } - runOnDestroy(() -> Utilities.unregisterReceiverSafely(mContext, mUserUnlockedReceiver)); + runOnDestroy(() -> mUserUnlockedReceiver.unregisterReceiverSafely(mContext)); // Register for exclusion updates mExclusionListener = new SystemGestureExclusionListenerCompat(mDisplayId) { @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; } @@ -342,7 +340,7 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener { action.run(); } mUserUnlockedActions.clear(); - Utilities.unregisterReceiverSafely(mContext, mUserUnlockedReceiver); + mUserUnlockedReceiver.unregisterReceiverSafely(mContext); } /** @@ -498,7 +496,7 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener { 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 +573,23 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener { && ((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(" 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 b6d9016727..388e1256d8 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java @@ -15,11 +15,10 @@ */ package com.android.quickstep; -import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING; +import static android.view.RemoteAnimationTarget.MODE_CLOSING; import android.graphics.Rect; - -import com.android.systemui.shared.system.RemoteAnimationTargetCompat; +import android.view.RemoteAnimationTarget; /** * Extension of {@link RemoteAnimationTargets} with additional information about swipe @@ -30,8 +29,8 @@ public class RecentsAnimationTargets extends RemoteAnimationTargets { public final Rect homeContentInsets; public final Rect minimizedHomeBounds; - public RecentsAnimationTargets(RemoteAnimationTargetCompat[] apps, - RemoteAnimationTargetCompat[] wallpapers, RemoteAnimationTargetCompat[] nonApps, + public RecentsAnimationTargets(RemoteAnimationTarget[] apps, + RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps, Rect homeContentInsets, Rect minimizedHomeBounds) { super(apps, wallpapers, nonApps, MODE_CLOSING); this.homeContentInsets = homeContentInsets; diff --git a/quickstep/src/com/android/quickstep/RecentsFilterState.java b/quickstep/src/com/android/quickstep/RecentsFilterState.java new file mode 100644 index 0000000000..ff6951d33f --- /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 1634c0839f..913f08f41d 100644 --- a/quickstep/src/com/android/quickstep/RecentsModel.java +++ b/quickstep/src/com/android/quickstep/RecentsModel.java @@ -22,6 +22,7 @@ import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId; import android.annotation.TargetApi; import android.app.ActivityManager; +import android.app.KeyguardManager; import android.content.ComponentCallbacks2; import android.content.Context; import android.content.Intent; @@ -36,10 +37,10 @@ import com.android.launcher3.icons.IconProvider.IconChangeListener; import com.android.launcher3.util.Executors.SimpleThreadFactory; import com.android.launcher3.util.MainThreadInitializedObject; import com.android.quickstep.util.GroupTask; +import com.android.quickstep.util.TaskVisualsChangeListener; 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; @@ -49,12 +50,14 @@ import java.util.List; 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. */ @TargetApi(Build.VERSION_CODES.O) -public class RecentsModel implements IconChangeListener, TaskStackChangeListener { +public class RecentsModel implements IconChangeListener, TaskStackChangeListener, + TaskVisualsChangeListener { // We do not need any synchronization for this variable as its only written on UI thread. public static final MainThreadInitializedObject<RecentsModel> INSTANCE = @@ -73,10 +76,12 @@ public class RecentsModel implements IconChangeListener, TaskStackChangeListener 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); + mIconCache.registerTaskVisualsChangeListener(this); mThumbnailCache = new TaskThumbnailCache(context, RECENTS_MODEL_EXECUTOR); TaskStackChangeListeners.getInstance().registerTaskStackListener(this); @@ -92,14 +97,30 @@ public class RecentsModel implements IconChangeListener, TaskStackChangeListener } /** - * 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); } /** @@ -121,8 +142,9 @@ public class RecentsModel implements IconChangeListener, TaskStackChangeListener * 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)) { @@ -131,7 +153,7 @@ public class RecentsModel implements IconChangeListener, TaskStackChangeListener } } callback.accept(true); - }); + }, filter); } @Override @@ -204,6 +226,13 @@ public class RecentsModel implements IconChangeListener, TaskStackChangeListener } @Override + public void onTaskIconChanged(int taskId) { + for (TaskVisualsChangeListener listener : mThumbnailChangeListeners) { + listener.onTaskIconChanged(taskId); + } + } + + @Override public void onSystemIconStateChanged(String iconState) { mIconCache.clearCache(); } @@ -228,18 +257,33 @@ public class RecentsModel implements IconChangeListener, TaskStackChangeListener } /** - * Listener for receiving various task properties changes + * Registers a listener for running tasks */ - public interface TaskVisualsChangeListener { + public void registerRunningTasksListener(RunningTasksListener listener) { + mTaskList.registerRunningTasksListener(listener); + } - /** - * Called whn the task thumbnail changes - */ - Task onTaskThumbnailChanged(int taskId, ThumbnailData thumbnailData); + /** + * Removes the previously registered running tasks listener + */ + public void unregisterRunningTasksListener() { + mTaskList.unregisterRunningTasksListener(); + } + /** + * Gets the set of running tasks. + */ + public ArrayList<ActivityManager.RunningTaskInfo> getRunningTasks() { + return mTaskList.getRunningTasks(); + } + + /** + * Listener for receiving running tasks changes + */ + public interface RunningTasksListener { /** - * Called when the icon for a task changes + * Called when there's a change to running tasks */ - void onTaskIconChanged(String pkg, UserHandle user); + void onRunningTasksChanged(); } } diff --git a/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java b/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java index b20d48806a..80aaad0dd9 100644 --- a/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java +++ b/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java @@ -15,11 +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 static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS; - -import com.android.systemui.shared.system.RemoteAnimationTargetCompat; +import android.view.RemoteAnimationTarget; import java.util.ArrayList; import java.util.concurrent.CopyOnWriteArrayList; @@ -31,41 +31,40 @@ public class RemoteAnimationTargets { 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; } @@ -76,12 +75,12 @@ public class RemoteAnimationTargets { /** * 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; } @@ -90,19 +89,19 @@ public class RemoteAnimationTargets { } /** 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; } } @@ -114,10 +113,6 @@ public class RemoteAnimationTargets { } public void release() { - if (ENABLE_SHELL_TRANSITIONS) { - mReleaseChecks.clear(); - return; - } if (mReleased) { return; } @@ -129,15 +124,19 @@ public class RemoteAnimationTargets { } 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 c3ea25683d..4c41bef26a 100644 --- a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java +++ b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java @@ -17,14 +17,15 @@ package com.android.quickstep; import android.content.Context; +import android.graphics.Rect; +import android.view.RemoteAnimationTarget; import androidx.annotation.Nullable; -import com.android.launcher3.util.SplitConfigurationOptions.StagedSplitBounds; +import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds; import com.android.quickstep.util.AnimatorControllerWithResistance; import com.android.quickstep.util.TaskViewSimulator; import com.android.quickstep.util.TransformParams; -import com.android.systemui.shared.system.RemoteAnimationTargetCompat; import java.util.ArrayList; @@ -34,7 +35,7 @@ import java.util.ArrayList; */ public class RemoteTargetGluer { private RemoteTargetHandle[] mRemoteTargetHandles; - private StagedSplitBounds mStagedSplitBounds; + private SplitBounds mSplitBounds; /** * Use this constructor if remote targets are split-screen independent @@ -75,7 +76,7 @@ public class RemoteTargetGluer { */ public RemoteTargetHandle[] assignTargets(RemoteAnimationTargets targets) { for (int i = 0; i < mRemoteTargetHandles.length; i++) { - RemoteAnimationTargetCompat primaryTaskTarget = targets.apps[i]; + RemoteAnimationTarget primaryTaskTarget = targets.apps[i]; mRemoteTargetHandles[i].mTransformParams.setTargetSet( createRemoteAnimationTargetsForTarget(targets, null)); mRemoteTargetHandles[i].mTaskViewSimulator.setPreview(primaryTaskTarget, null); @@ -100,8 +101,8 @@ public class RemoteTargetGluer { */ 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. @@ -118,22 +119,26 @@ public class RemoteTargetGluer { // remoteTargetHandle[0] denotes topLeft task, so we pass in the bottomRight to exclude, // vice versa - mStagedSplitBounds = new StagedSplitBounds( - topLeftTarget.startScreenSpaceBounds, - bottomRightTarget.startScreenSpaceBounds, splitIds[0], splitIds[1]); + mSplitBounds = new SplitBounds( + getStartBounds(topLeftTarget), + getStartBounds(bottomRightTarget), splitIds[0], splitIds[1]); mRemoteTargetHandles[0].mTransformParams.setTargetSet( createRemoteAnimationTargetsForTarget(targets, bottomRightTarget)); mRemoteTargetHandles[0].mTaskViewSimulator.setPreview(topLeftTarget, - mStagedSplitBounds); + mSplitBounds); mRemoteTargetHandles[1].mTransformParams.setTargetSet( createRemoteAnimationTargetsForTarget(targets, topLeftTarget)); mRemoteTargetHandles[1].mTaskViewSimulator.setPreview(bottomRightTarget, - mStagedSplitBounds); + mSplitBounds); } return mRemoteTargetHandles; } + private Rect getStartBounds(RemoteAnimationTarget target) { + return target.startBounds == null ? target.screenSpaceBounds : target.startBounds; + } + /** * Ensures that we aren't excluding ancillary targets such as home/recents * @@ -144,11 +149,10 @@ public class RemoteTargetGluer { */ private RemoteAnimationTargets createRemoteAnimationTargetsForTarget( RemoteAnimationTargets targets, - RemoteAnimationTargetCompat targetToExclude) { - ArrayList<RemoteAnimationTargetCompat> targetsWithoutExcluded = - new ArrayList<RemoteAnimationTargetCompat>(); + RemoteAnimationTarget targetToExclude) { + ArrayList<RemoteAnimationTarget> targetsWithoutExcluded = new ArrayList<>(); - for (RemoteAnimationTargetCompat targetCompat : targets.unfilteredApps) { + for (RemoteAnimationTarget targetCompat : targets.unfilteredApps) { if (targetCompat == targetToExclude) { continue; } @@ -162,9 +166,8 @@ public class RemoteTargetGluer { 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); } @@ -173,8 +176,8 @@ public class RemoteTargetGluer { return mRemoteTargetHandles; } - public StagedSplitBounds getStagedSplitBounds() { - return mStagedSplitBounds; + public SplitBounds getSplitBounds() { + return mSplitBounds; } /** diff --git a/quickstep/src/com/android/quickstep/RotationTouchHelper.java b/quickstep/src/com/android/quickstep/RotationTouchHelper.java index f1e20dbb04..f8b69666fe 100644 --- a/quickstep/src/com/android/quickstep/RotationTouchHelper.java +++ b/quickstep/src/com/android/quickstep/RotationTouchHelper.java @@ -23,20 +23,20 @@ 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; import android.view.MotionEvent; import android.view.OrientationEventListener; -import com.android.launcher3.testing.TestProtocol; +import com.android.launcher3.testing.shared.TestProtocol; import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener; import com.android.launcher3.util.DisplayController.Info; -import com.android.launcher3.util.DisplayController.NavigationMode; import com.android.launcher3.util.MainThreadInitializedObject; +import com.android.launcher3.util.NavigationMode; import com.android.quickstep.util.RecentsOrientedState; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.shared.system.TaskStackChangeListener; diff --git a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java index 966710854f..291f8354d2 100644 --- a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java +++ b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java @@ -17,7 +17,6 @@ package com.android.quickstep; import static com.android.launcher3.anim.Interpolators.ACCEL_1_5; import static com.android.launcher3.anim.Interpolators.LINEAR; -import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_SELECT; import android.animation.Animator; import android.content.Context; @@ -25,12 +24,14 @@ import android.graphics.Matrix; 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; @@ -38,11 +39,10 @@ import com.android.launcher3.touch.PagedOrientationHandler; 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; @@ -66,22 +66,21 @@ public abstract class SwipeUpAnimationLogic implements // 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; // How much further we can drag past recents, as a factor of mTransitionDragLength. protected float mDragLengthFactor = 1; - protected boolean mIsSwipeForStagedSplit; + protected boolean mIsSwipeForSplit; public SwipeUpAnimationLogic(Context context, RecentsAnimationDeviceState deviceState, GestureState gestureState) { mContext = context; mDeviceState = deviceState; mGestureState = gestureState; - - mIsSwipeForStagedSplit = ENABLE_SPLIT_SELECT.get() && - TopTaskTracker.INSTANCE.get(context).getRunningSplitTaskIds().length > 1; + mIsSwipeForSplit = TopTaskTracker.INSTANCE.get(context).getRunningSplitTaskIds().length > 1; mTargetGluer = new RemoteTargetGluer(mContext, mGestureState.getActivityInterface()); mRemoteTargetHandles = mTargetGluer.getRemoteTargetHandles(); @@ -118,7 +117,9 @@ public abstract class SwipeUpAnimationLogic implements @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; @@ -131,6 +132,17 @@ public abstract class SwipeUpAnimationLogic implements } /** + * 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 @@ -338,11 +350,11 @@ public abstract class SwipeUpAnimationLogic implements } @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 39d8b5447d..bb973342d5 100644 --- a/quickstep/src/com/android/quickstep/SystemUiProxy.java +++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java @@ -17,21 +17,25 @@ package com.android.quickstep; 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; import android.app.PictureInPictureParams; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; -import android.graphics.Bitmap; +import android.content.pm.PackageManager; +import android.content.pm.ShortcutInfo; import android.graphics.Insets; import android.graphics.Rect; import android.os.Bundle; +import android.os.Handler; import android.os.IBinder; import android.os.IBinder.DeathRecipient; +import android.os.Message; import android.os.RemoteException; import android.os.UserHandle; import android.util.Log; @@ -40,18 +44,22 @@ 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.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; @@ -66,16 +74,19 @@ import com.android.wm.shell.util.GroupedRecentTaskInfo; 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; @@ -85,6 +96,7 @@ public class SystemUiProxy implements ISystemUiProxy, DisplayController.DisplayI private IStartingWindow mStartingWindow; private IRecentTasks mRecentTasks; private IBackAnimation mBackAnimation; + private IDesktopMode mDesktopMode; private final DeathRecipient mSystemUiProxyDeathRecipient = () -> { MAIN_EXECUTOR.execute(() -> clearProxy()); }; @@ -96,32 +108,25 @@ public class SystemUiProxy implements ISystemUiProxy, DisplayController.DisplayI 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 IOnBackInvokedCallback mBackToLauncherCallback; // Used to dedupe calls to SystemUI private int mLastShelfHeight; private boolean mLastShelfVisible; - private float mLastNavButtonAlpha; - private boolean mLastNavButtonAnimate; - private boolean mHasNavButtonAlphaBeenSet = false; - private Runnable mPendingSetNavButtonAlpha = null; + + private 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); - } - - @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); - } + mContext = context; + mAsyncHandler = new Handler(UI_HELPER_EXECUTOR.getLooper(), this::handleMessageAsync); } @Override @@ -167,7 +172,7 @@ public class SystemUiProxy implements ISystemUiProxy, DisplayController.DisplayI IOneHanded oneHanded, IShellTransitions shellTransitions, IStartingWindow startingWindow, IRecentTasks recentTasks, ISysuiUnlockAnimationController sysuiUnlockAnimationController, - IBackAnimation backAnimation) { + IBackAnimation backAnimation, IDesktopMode desktopMode) { unlinkToDeath(); mSystemUiProxy = proxy; mPip = pip; @@ -178,10 +183,11 @@ public class SystemUiProxy implements ISystemUiProxy, DisplayController.DisplayI 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); @@ -189,29 +195,20 @@ public class SystemUiProxy implements ISystemUiProxy, DisplayController.DisplayI if (mStartingWindowListener != null && mStartingWindow != null) { setStartingWindowListener(mStartingWindowListener); } - if (mPendingLauncherUnlockAnimationController != null - && mSysuiUnlockAnimationController != null) { - setLauncherUnlockAnimationController(mPendingLauncherUnlockAnimationController); - mPendingLauncherUnlockAnimationController = null; - } - for (int i = mRemoteTransitions.size() - 1; i >= 0; --i) { - registerRemoteTransition(mRemoteTransitions.get(i)); + if (mSysuiUnlockAnimationController != null && mLauncherUnlockAnimationController != null) { + setLauncherUnlockAnimationController(mLauncherUnlockAnimationController); } + new LinkedHashMap<>(mRemoteTransitions).forEach(this::registerRemoteTransition); if (mRecentTasksListener != null && mRecentTasks != null) { registerRecentTasksListener(mRecentTasksListener); } if (mBackAnimation != null && mBackToLauncherCallback != null) { setBackToLauncherCallback(mBackToLauncherCallback); } - - if (mPendingSetNavButtonAlpha != null) { - mPendingSetNavButtonAlpha.run(); - mPendingSetNavButtonAlpha = null; - } } public void clearProxy() { - setProxy(null, null, null, null, null, null, null, null, null); + setProxy(null, null, null, null, null, null, null, null, null, null); } // TODO(141886704): Find a way to remove this @@ -271,43 +268,6 @@ public class SystemUiProxy implements ISystemUiProxy, DisplayController.DisplayI } @Override - public Rect getNonMinimizedSplitScreenSecondaryBounds() { - if (mSystemUiProxy != null) { - try { - return mSystemUiProxy.getNonMinimizedSplitScreenSecondaryBounds(); - } catch (RemoteException e) { - Log.w(TAG, "Failed call getNonMinimizedSplitScreenSecondaryBounds", e); - } - } - return null; - } - - 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) { try { @@ -385,53 +345,6 @@ public class SystemUiProxy implements ISystemUiProxy, DisplayController.DisplayI } @Override - public void handleImageAsScreenshot(Bitmap bitmap, Rect rect, Insets insets, int i) { - if (mSystemUiProxy != null) { - try { - mSystemUiProxy.handleImageAsScreenshot(bitmap, rect, insets, i); - } catch (RemoteException e) { - Log.w(TAG, "Failed call handleImageAsScreenshot", e); - } - } - } - - @Override - public void setSplitScreenMinimized(boolean minimized) { - if (mSystemUiProxy != null) { - try { - mSystemUiProxy.setSplitScreenMinimized(minimized); - } catch (RemoteException e) { - Log.w(TAG, "Failed call setSplitScreenMinimized", e); - } - } - } - - @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 { @@ -513,12 +426,20 @@ public class SystemUiProxy implements ISystemUiProxy, DisplayController.DisplayI * 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); @@ -527,12 +448,12 @@ public class SystemUiProxy implements ISystemUiProxy, DisplayController.DisplayI } /** - * 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); } @@ -540,12 +461,17 @@ public class SystemUiProxy implements ISystemUiProxy, DisplayController.DisplayI 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); } @@ -569,6 +495,19 @@ public class SystemUiProxy implements ISystemUiProxy, DisplayController.DisplayI } } + /** + * 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 // @@ -596,15 +535,55 @@ public class SystemUiProxy implements ISystemUiProxy, DisplayController.DisplayI } /** 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(taskId1, options1, taskId2, options2, splitPosition, + splitRatio, remoteTransition, instanceId); + } catch (RemoteException e) { + 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.startTasks(mainTaskId, mainOptions, sideTaskId, sideOptions, - sidePosition, splitRatio, remoteTransition.getTransition()); + mSplitScreen.startIntents(pendingIntent1, options1, pendingIntent2, options2, + splitPosition, splitRatio, remoteTransition, instanceId); } catch (RemoteException e) { - Log.w(TAG, "Failed call startTask"); + 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"); } } } @@ -612,13 +591,13 @@ public class SystemUiProxy implements ISystemUiProxy, DisplayController.DisplayI /** * 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"); } @@ -626,25 +605,52 @@ public class SystemUiProxy implements ISystemUiProxy, DisplayController.DisplayI } public void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, - Intent fillInIntent, int taskId, Bundle mainOptions, Bundle sideOptions, + Bundle options1, int taskId, Bundle options2, + @SplitConfigurationOptions.StagePosition int splitPosition, float splitRatio, + RemoteAnimationAdapter adapter, InstanceId instanceId) { + if (mSystemUiProxy != null) { + try { + mSplitScreen.startIntentAndTaskWithLegacyTransition(pendingIntent, options1, taskId, + options2, splitPosition, splitRatio, adapter, instanceId); + } catch (RemoteException e) { + 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) { + RemoteAnimationAdapter adapter, InstanceId instanceId) { if (mSystemUiProxy != null) { try { - mSplitScreen.startIntentAndTaskWithLegacyTransition(pendingIntent, fillInIntent, - taskId, mainOptions, sideOptions, sidePosition, splitRatio, adapter); + mSplitScreen.startIntentsWithLegacyTransition(pendingIntent1, options1, + pendingIntent2, options2, sidePosition, splitRatio, adapter, instanceId); } catch (RemoteException e) { - Log.w(TAG, "Failed call startTasksWithLegacyTransition"); + 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"); } @@ -652,10 +658,10 @@ public class SystemUiProxy implements ISystemUiProxy, DisplayController.DisplayI } 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"); } @@ -678,6 +684,7 @@ public class SystemUiProxy implements ISystemUiProxy, DisplayController.DisplayI * * @return RemoteAnimationTargets of windows that need to animate but only exist in shell. */ + @Nullable public RemoteAnimationTarget[] onGoingToRecentsLegacy(RemoteAnimationTarget[] apps) { if (mSplitScreen != null) { try { @@ -689,6 +696,7 @@ public class SystemUiProxy implements ISystemUiProxy, DisplayController.DisplayI return null; } + @Nullable public RemoteAnimationTarget[] onStartingSplitLegacy(RemoteAnimationTarget[] apps) { if (mSplitScreen != null) { try { @@ -728,24 +736,24 @@ public class SystemUiProxy implements ISystemUiProxy, DisplayController.DisplayI // 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"); } @@ -791,11 +799,11 @@ public class SystemUiProxy implements ISystemUiProxy, DisplayController.DisplayI controller.dispatchSmartspaceStateToSysui(); } } catch (RemoteException e) { - Log.w(TAG, "Failed call setStartingWindowListener", e); + Log.w(TAG, "Failed call setLauncherUnlockAnimationController", e); } - } else { - mPendingLauncherUnlockAnimationController = controller; } + + mLauncherUnlockAnimationController = controller; } /** @@ -904,4 +912,44 @@ public class SystemUiProxy implements ISystemUiProxy, DisplayController.DisplayI } return new ArrayList<>(); } + + /** + * Gets the set of running tasks. + */ + public ArrayList<ActivityManager.RunningTaskInfo> getRunningTasks(int numTasks) { + if (mRecentTasks != null + && mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_PC)) { + try { + return new ArrayList<>(Arrays.asList(mRecentTasks.getRunningTasks(numTasks))); + } catch (RemoteException e) { + Log.w(TAG, "Failed call getRunningTasks", e); + } + } + return new ArrayList<>(); + } + + private boolean handleMessageAsync(Message msg) { + switch (msg.what) { + case MSG_SET_SHELF_HEIGHT: + setShelfHeightAsync(msg.arg1, msg.arg2); + return true; + } + + return false; + } + + // + // Desktop Mode + // + + /** Call shell to show all apps active on the desktop */ + public void showDesktopApps() { + if (mDesktopMode != null) { + try { + mDesktopMode.showDesktopApps(); + } catch (RemoteException e) { + Log.w(TAG, "Failed call showDesktopApps", e); + } + } + } } diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java index 54f457d2d8..c45b2f05f8 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.content.Intent; 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 androidx.annotation.UiThread; 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 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn 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 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn 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 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn @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,49 +156,43 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn } @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.getNoCreate().onStartingSplitLegacy(nonHomeApps); - - if (ENABLE_QUICKSTEP_LIVE_TILE.get() && activityInterface.isInLiveTileMode() + RemoteAnimationTarget[] nonAppTargets = SystemUiProxy.INSTANCE.get(mCtx) + .onStartingSplitLegacy(appearedTaskTargets); + if (nonAppTargets == null) { + nonAppTargets = new RemoteAnimationTarget[0]; + } + if (activityInterface.isInLiveTileMode() && activityInterface.getCreatedActivity() != null) { RecentsView recentsView = activityInterface.getCreatedActivity().getOverviewPanel(); if (recentsView != null) { recentsView.launchSideTaskInLiveTileMode(appearedTaskTarget.taskId, appearedTaskTargets, - new RemoteAnimationTargetCompat[0] /* wallpaper */, - RemoteAnimationTargetCompat.wrap(nonAppTargets) /* nonApps */); + new RemoteAnimationTarget[0] /* wallpaper */, + nonAppTargets /* nonApps */); return; } - } else if (nonAppTargets != null && nonAppTargets.length > 0) { + } else if (nonAppTargets.length > 0) { TaskViewUtils.createSplitAuxiliarySurfacesAnimator( - RemoteAnimationTargetCompat.wrap(nonAppTargets) /* nonApps */, - true /*shown*/, dividerAnimator -> dividerAnimator.start()); + nonAppTargets /* nonApps */, + true /*shown*/, dividerAnimator -> { + dividerAnimator.start(); + dividerAnimator.end(); + }); } if (mController != null) { if (mLastAppearedTaskTarget == null @@ -211,7 +208,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn @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(); @@ -232,10 +229,10 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn 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. @@ -258,6 +255,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn * 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); @@ -272,7 +270,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn 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) { @@ -296,6 +294,8 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn */ public void finishRunningRecentsAnimation(boolean toHome) { if (mController != null) { + ActiveGestureLog.INSTANCE.addLog( + /* event= */ "finishRunningRecentsAnimation", toHome); mCallbacks.notifyAnimationCanceled(); Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), toHome ? mController::finishAnimationToHome @@ -328,6 +328,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn * 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 3803f0350b..7c05a1092c 100644 --- a/quickstep/src/com/android/quickstep/TaskIconCache.java +++ b/quickstep/src/com/android/quickstep/TaskIconCache.java @@ -18,6 +18,7 @@ package com.android.quickstep; import static com.android.launcher3.uioverrides.QuickstepLauncher.GO_LOW_RAM_RECENTS_ENABLED; import static com.android.launcher3.util.DisplayController.CHANGE_DENSITY; +import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityManager.TaskDescription; import android.content.Context; @@ -46,6 +47,7 @@ import com.android.launcher3.util.DisplayController.Info; import com.android.launcher3.util.Preconditions; import com.android.quickstep.util.CancellableTask; import com.android.quickstep.util.TaskKeyLruCache; +import com.android.quickstep.util.TaskVisualsChangeListener; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.recents.model.Task.TaskKey; import com.android.systemui.shared.system.PackageManagerWrapper; @@ -70,6 +72,9 @@ public class TaskIconCache implements DisplayInfoChangeListener { private BaseIconFactory mIconFactory; + @Nullable + public TaskVisualsChangeListener mTaskVisualsChangeListener = null; + public TaskIconCache(Context context, Executor bgExecutor, IconProvider iconProvider) { mContext = context; mBgExecutor = bgExecutor; @@ -116,6 +121,7 @@ public class TaskIconCache implements DisplayInfoChangeListener { task.icon = result.icon; task.titleDescription = result.contentDescription; callback.accept(task); + dispatchIconUpdate(task.key.id); } }; mBgExecutor.execute(request); @@ -243,12 +249,13 @@ public class TaskIconCache implements DisplayInfoChangeListener { 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)); } } @@ -257,7 +264,8 @@ public class TaskIconCache implements DisplayInfoChangeListener { if (mIconFactory == null) { mIconFactory = new BaseIconFactory(mContext, DisplayController.INSTANCE.get(mContext).getInfo().getDensityDpi(), - mContext.getResources().getDimensionPixelSize(R.dimen.taskbar_icon_size)); + mContext.getResources().getDimensionPixelSize( + R.dimen.task_icon_cache_default_icon_size)); } return mIconFactory; } @@ -272,4 +280,18 @@ public class TaskIconCache implements DisplayInfoChangeListener { public Drawable icon; public String contentDescription = ""; } + + void registerTaskVisualsChangeListener(TaskVisualsChangeListener newListener) { + mTaskVisualsChangeListener = newListener; + } + + void removeTaskVisualsChangeListener() { + mTaskVisualsChangeListener = null; + } + + void dispatchIconUpdate(int taskId) { + if (mTaskVisualsChangeListener != null) { + mTaskVisualsChangeListener.onTaskIconChanged(taskId); + } + } } diff --git a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java index 1b8f6e8b8a..e24966838e 100644 --- a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java +++ b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java @@ -18,12 +18,10 @@ package com.android.quickstep; 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; import android.annotation.SuppressLint; -import android.app.ActivityManager; import android.content.Context; import android.graphics.Insets; import android.graphics.Matrix; @@ -37,17 +35,12 @@ import androidx.annotation.RequiresApi; import com.android.launcher3.BaseActivity; import com.android.launcher3.BaseDraggingActivity; -import com.android.launcher3.DeviceProfile; import com.android.launcher3.R; -import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.popup.SystemShortcut; -import com.android.launcher3.touch.PagedOrientationHandler; import com.android.launcher3.util.ResourceBasedOverride; -import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption; import com.android.launcher3.views.ActivityContext; -import com.android.quickstep.TaskShortcutFactory.SplitSelectSystemShortcut; import com.android.quickstep.util.RecentsOrientedState; import com.android.quickstep.views.OverviewActionsView; import com.android.quickstep.views.RecentsView; @@ -66,7 +59,7 @@ import java.util.List; public class TaskOverlayFactory implements ResourceBasedOverride { public static List<SystemShortcut> getEnabledShortcuts(TaskView taskView, - DeviceProfile deviceProfile, TaskIdAttributeContainer taskContainer) { + TaskIdAttributeContainer taskContainer) { final ArrayList<SystemShortcut> shortcuts = new ArrayList<>(); final BaseDraggingActivity activity = BaseActivity.fromContext(taskView.getContext()); boolean hasMultipleTasks = taskView.getTaskIds()[1] != -1; @@ -75,17 +68,11 @@ public class TaskOverlayFactory implements ResourceBasedOverride { continue; } - SystemShortcut shortcut = menuOption.getShortcut(activity, taskContainer); - if (shortcut == null) { + List<SystemShortcut> menuShortcuts = menuOption.getShortcuts(activity, taskContainer); + if (menuShortcuts == null) { continue; } - - if (menuOption == TaskShortcutFactory.SPLIT_SCREEN && - FeatureFlags.ENABLE_SPLIT_SELECT.get()) { - addSplitOptions(shortcuts, activity, taskView, deviceProfile); - } else { - shortcuts.add(shortcut); - } + shortcuts.addAll(menuShortcuts); } RecentsOrientedState orientedState = taskView.getRecentsView().getPagedViewOrientedState(); boolean canLauncherRotate = orientedState.isRecentsActivityRotationAllowed(); @@ -94,61 +81,24 @@ public class TaskOverlayFactory implements ResourceBasedOverride { // Add overview actions to the menu when in in-place rotate landscape mode. if (!canLauncherRotate && isInLandscape) { // Add screenshot action to task menu. - SystemShortcut screenshotShortcut = TaskShortcutFactory.SCREENSHOT - .getShortcut(activity, taskContainer); - if (screenshotShortcut != null) { - shortcuts.add(screenshotShortcut); + List<SystemShortcut> screenshotShortcuts = TaskShortcutFactory.SCREENSHOT + .getShortcuts(activity, taskContainer); + if (screenshotShortcuts != null) { + shortcuts.addAll(screenshotShortcuts); } // Add modal action only if display orientation is the same as the device orientation. if (orientedState.getDisplayRotation() == ROTATION_0) { - SystemShortcut modalShortcut = TaskShortcutFactory.MODAL - .getShortcut(activity, taskContainer); - if (modalShortcut != null) { - shortcuts.add(modalShortcut); + List<SystemShortcut> modalShortcuts = TaskShortcutFactory.MODAL + .getShortcuts(activity, taskContainer); + if (modalShortcuts != null) { + shortcuts.addAll(modalShortcuts); } } } return shortcuts; } - - /** - * Does NOT add split options in the following scenarios: - * * The taskView to add split options is already showing split screen tasks - * * There aren't at least 2 tasks in overview to show split options for - * * Device is in "Lock task mode" - * * The taskView to show split options for is the focused task AND we haven't started - * scrolling in overview (if we haven't scrolled, there's a split overview action button so - * we don't need this menu option) - */ - private static void addSplitOptions(List<SystemShortcut> outShortcuts, - BaseDraggingActivity activity, TaskView taskView, DeviceProfile deviceProfile) { - RecentsView recentsView = taskView.getRecentsView(); - PagedOrientationHandler orientationHandler = recentsView.getPagedOrientationHandler(); - int[] taskViewTaskIds = taskView.getTaskIds(); - boolean taskViewHasMultipleTasks = taskViewTaskIds[0] != -1 && - taskViewTaskIds[1] != -1; - boolean notEnoughTasksToSplit = recentsView.getTaskViewCount() < 2; - boolean isFocusedTask = deviceProfile.isTablet && taskView.isFocusedTask(); - boolean isTaskInExpectedScrollPosition = - recentsView.isTaskInExpectedScrollPosition(recentsView.indexOfChild(taskView)); - ActivityManager activityManager = - (ActivityManager) taskView.getContext().getSystemService(Context.ACTIVITY_SERVICE); - boolean isLockTaskMode = activityManager.isInLockTaskMode(); - - if (taskViewHasMultipleTasks || notEnoughTasksToSplit || isLockTaskMode || - (isFocusedTask && isTaskInExpectedScrollPosition)) { - return; - } - - List<SplitPositionOption> positions = - orientationHandler.getSplitPositionOptions(deviceProfile); - for (SplitPositionOption option : positions) { - outShortcuts.add(new SplitSelectSystemShortcut(activity, taskView, option)); - } - } - public TaskOverlay createOverlay(TaskThumbnailView thumbnailView) { return new TaskOverlay(thumbnailView); } @@ -170,7 +120,7 @@ public class TaskOverlayFactory implements ResourceBasedOverride { /** Note that these will be shown in order from top to bottom, if available for the task. */ private static final TaskShortcutFactory[] MENU_OPTIONS = new TaskShortcutFactory[]{ TaskShortcutFactory.APP_INFO, - TaskShortcutFactory.SPLIT_SCREEN, + TaskShortcutFactory.SPLIT_SELECT, TaskShortcutFactory.PIN, TaskShortcutFactory.INSTALL, TaskShortcutFactory.FREE_FORM, @@ -223,14 +173,10 @@ public class TaskOverlayFactory implements ResourceBasedOverride { * @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 e807e26b08..663525d0c5 100644 --- a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java +++ b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java @@ -16,11 +16,10 @@ package com.android.quickstep; -import static android.view.Display.DEFAULT_DISPLAY; +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; -import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_SPLIT_SCREEN_TAP; import android.app.Activity; import android.app.ActivityOptions; @@ -29,9 +28,16 @@ import android.graphics.Color; 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; + import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.DeviceProfile; import com.android.launcher3.R; @@ -39,6 +45,7 @@ import com.android.launcher3.logging.StatsLogManager.LauncherEvent; import com.android.launcher3.model.WellbeingModel; import com.android.launcher3.popup.SystemShortcut; import com.android.launcher3.popup.SystemShortcut.AppInfo; +import com.android.launcher3.touch.PagedOrientationHandler; import com.android.launcher3.util.InstantAppResolver; import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption; import com.android.quickstep.views.RecentsView; @@ -50,26 +57,38 @@ import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecCompat 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; +import java.util.function.Function; +import java.util.stream.Collectors; /** * Represents a system shortcut that can be shown for a recent task. */ public interface TaskShortcutFactory { - SystemShortcut getShortcut(BaseDraggingActivity activity, - TaskIdAttributeContainer taskContainer); + @Nullable + default List<SystemShortcut> getShortcuts(BaseDraggingActivity activity, + TaskIdAttributeContainer taskContainer) { + return null; + } default boolean showForSplitscreen() { return false; } + /** @return a singleton list if the provided shortcut is non-null, null otherwise */ + @Nullable + default List<SystemShortcut> createSingletonShortcutList(@Nullable SystemShortcut shortcut) { + if (shortcut != null) { + return Collections.singletonList(shortcut); + } + return null; + } + TaskShortcutFactory APP_INFO = new TaskShortcutFactory() { @Override - public SystemShortcut getShortcut(BaseDraggingActivity activity, + public List<SystemShortcut> getShortcuts(BaseDraggingActivity activity, TaskIdAttributeContainer taskContainer) { TaskView taskView = taskContainer.getTaskView(); AppInfo.SplitAccessibilityInfo accessibilityInfo = @@ -77,7 +96,8 @@ public interface TaskShortcutFactory { TaskUtils.getTitle(taskView.getContext(), taskContainer.getTask()), taskContainer.getA11yNodeId() ); - return new AppInfo(activity, taskContainer.getItemInfo(), taskView, accessibilityInfo); + return Collections.singletonList(new AppInfo(activity, taskContainer.getItemInfo(), + taskView, accessibilityInfo)); } @Override @@ -86,40 +106,10 @@ public interface TaskShortcutFactory { } }; - abstract class MultiWindowFactory implements TaskShortcutFactory { - - private final int mIconRes; - private final int mTextRes; - private final LauncherEvent mLauncherEvent; - - MultiWindowFactory(int iconRes, int textRes, LauncherEvent launcherEvent) { - mIconRes = iconRes; - mTextRes = textRes; - mLauncherEvent = launcherEvent; - } - - protected abstract boolean isAvailable(BaseDraggingActivity activity, int displayId); - protected abstract ActivityOptions makeLaunchOptions(Activity activity); - protected abstract boolean onActivityStarted(BaseDraggingActivity activity); - - @Override - public SystemShortcut getShortcut(BaseDraggingActivity activity, - TaskIdAttributeContainer taskContainer) { - final Task task = taskContainer.getTask(); - if (!task.isDockable) { - return null; - } - if (!isAvailable(activity, task.key.displayId)) { - return null; - } - return new MultiWindowSystemShortcut(mIconRes, mTextRes, activity, taskContainer, this, - mLauncherEvent); - } - } - class SplitSelectSystemShortcut extends SystemShortcut { private final TaskView mTaskView; private final SplitPositionOption mSplitPositionOption; + public SplitSelectSystemShortcut(BaseDraggingActivity target, TaskView taskView, SplitPositionOption option) { super(option.iconResId, option.textResId, target, taskView.getItemInfo(), taskView); @@ -133,19 +123,18 @@ public interface TaskShortcutFactory { } } - class MultiWindowSystemShortcut extends SystemShortcut<BaseDraggingActivity> { + class FreeformSystemShortcut extends SystemShortcut<BaseDraggingActivity> { + private static final String TAG = "FreeformSystemShortcut"; private Handler mHandler; private final RecentsView mRecentsView; private final TaskThumbnailView mThumbnailView; private final TaskView mTaskView; - private final MultiWindowFactory mFactory; private final LauncherEvent mLauncherEvent; - public MultiWindowSystemShortcut(int iconRes, int textRes, BaseDraggingActivity activity, - TaskIdAttributeContainer taskContainer, MultiWindowFactory factory, - LauncherEvent launcherEvent) { + public FreeformSystemShortcut(int iconRes, int textRes, BaseDraggingActivity activity, + TaskIdAttributeContainer taskContainer, LauncherEvent launcherEvent) { super(iconRes, textRes, activity, taskContainer.getItemInfo(), taskContainer.getTaskView()); mLauncherEvent = launcherEvent; @@ -153,55 +142,30 @@ public interface TaskShortcutFactory { mTaskView = taskContainer.getTaskView(); mRecentsView = activity.getOverviewPanel(); mThumbnailView = taskContainer.getThumbnailView(); - mFactory = factory; } @Override public void onClick(View view) { - Task.TaskKey taskKey = mTaskView.getTask().key; - final int taskId = taskKey.id; - - final View.OnLayoutChangeListener onLayoutChangeListener = - new View.OnLayoutChangeListener() { - @Override - public void onLayoutChange(View v, int l, int t, int r, int b, - int oldL, int oldT, int oldR, int oldB) { - mTaskView.getRootView().removeOnLayoutChangeListener(this); - mRecentsView.clearIgnoreResetTask(taskId); - - // Start animating in the side pages once launcher has been resized - mRecentsView.dismissTask(mTaskView, false, false); - } - }; - - final DeviceProfile.OnDeviceProfileChangeListener onDeviceProfileChangeListener = - new DeviceProfile.OnDeviceProfileChangeListener() { - @Override - public void onDeviceProfileChanged(DeviceProfile dp) { - mTarget.removeOnDeviceProfileChangeListener(this); - if (dp.isMultiWindowMode) { - mTaskView.getRootView().addOnLayoutChangeListener( - onLayoutChangeListener); - } - } - }; - dismissTaskMenuView(mTarget); + RecentsView rv = mTarget.getOverviewPanel(); + rv.switchToScreenshot(() -> { + rv.finishRecentsAnimation(true /* toHome */, () -> { + mTarget.returnToHomescreen(); + rv.getHandler().post(this::startActivity); + }); + }); + } - ActivityOptions options = mFactory.makeLaunchOptions(mTarget); + private void startActivity() { + final Task.TaskKey taskKey = mTaskView.getTask().key; + final int taskId = taskKey.id; + final ActivityOptions options = makeLaunchOptions(mTarget); if (options != null) { options.setSplashScreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_ICON); } if (options != null && ActivityManagerWrapper.getInstance().startActivityFromRecents(taskId, options)) { - if (!mFactory.onActivityStarted(mTarget)) { - return; - } - // Add a device profile change listener to kick off animating the side tasks - // once we enter multiwindow mode and relayout - mTarget.addOnDeviceProfileChangeListener(onDeviceProfileChangeListener); - final Runnable animStartedListener = () -> { // Hide the task view and wait for the window to be resized // TODO: Consider animating in launcher and do an in-place start activity @@ -233,82 +197,133 @@ public interface TaskShortcutFactory { taskId, thumbnail, taskBounds)); } }; - WindowManagerWrapper.getInstance().overridePendingAppTransitionMultiThumbFuture( + overridePendingAppTransitionMultiThumbFuture( future, animStartedListener, mHandler, true /* scaleUp */, taskKey.displayId); mTarget.getStatsLogManager().logger().withItemInfo(mTaskView.getItemInfo()) .log(mLauncherEvent); } } - } - /** @Deprecated */ - TaskShortcutFactory SPLIT_SCREEN = new MultiWindowFactory(R.drawable.ic_split_screen, - R.string.recent_task_option_split_screen, LAUNCHER_SYSTEM_SHORTCUT_SPLIT_SCREEN_TAP) { + /** + * 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); + } + } - @Override - protected boolean isAvailable(BaseDraggingActivity activity, int displayId) { - // Don't show menu-item if already in multi-window and the task is from - // the secondary display. - // TODO(b/118266305): Temporarily disable splitscreen for secondary display while new - // implementation is enabled - return !activity.getDeviceProfile().isMultiWindowMode - && (displayId == -1 || displayId == DEFAULT_DISPLAY); + private ActivityOptions makeLaunchOptions(Activity activity) { + 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(); + final Rect r = new Rect(0, 0, decorView.getWidth() / 2, decorView.getHeight() / 2); + r.offsetTo(insets.getSystemWindowInsetLeft() + 50, + insets.getSystemWindowInsetTop() + 50); + activityOptions.setLaunchBounds(r); + return activityOptions; } + } + /** + * Does NOT add split options in the following scenarios: + * * The taskView to add split options is already showing split screen tasks + * * There aren't at least 2 tasks in overview to show split options for + * * Split isn't supported by the task itself (non resizable activity) + * * We aren't currently in multi-window + * * The taskView to show split options for is the focused task AND we haven't started + * scrolling in overview (if we haven't scrolled, there's a split overview action button so + * we don't need this menu option) + */ + TaskShortcutFactory SPLIT_SELECT = new TaskShortcutFactory() { @Override - protected ActivityOptions makeLaunchOptions(Activity activity) { - final int navBarPosition = WindowManagerWrapper.getInstance().getNavBarPosition( - activity.getDisplayId()); - if (navBarPosition == WindowManagerWrapper.NAV_BAR_POS_INVALID) { + public List<SystemShortcut> getShortcuts(BaseDraggingActivity activity, + TaskIdAttributeContainer taskContainer) { + DeviceProfile deviceProfile = activity.getDeviceProfile(); + final Task task = taskContainer.getTask(); + final TaskView taskView = taskContainer.getTaskView(); + final RecentsView recentsView = taskView.getRecentsView(); + final PagedOrientationHandler orientationHandler = + recentsView.getPagedOrientationHandler(); + + int[] taskViewTaskIds = taskView.getTaskIds(); + boolean taskViewHasMultipleTasks = taskViewTaskIds[0] != -1 && + taskViewTaskIds[1] != -1; + boolean notEnoughTasksToSplit = recentsView.getTaskViewCount() < 2; + boolean isFocusedTask = deviceProfile.isTablet && taskView.isFocusedTask(); + boolean isTaskInExpectedScrollPosition = + recentsView.isTaskInExpectedScrollPosition(recentsView.indexOfChild(taskView)); + boolean isTaskSplitNotSupported = !task.isDockable; + boolean hideForExistingMultiWindow = activity.getDeviceProfile().isMultiWindowMode; + + if (taskViewHasMultipleTasks || + notEnoughTasksToSplit || + isTaskSplitNotSupported || + hideForExistingMultiWindow || + (isFocusedTask && isTaskInExpectedScrollPosition)) { return null; } - boolean dockTopOrLeft = navBarPosition != WindowManagerWrapper.NAV_BAR_POS_LEFT; - return ActivityOptionsCompat.makeSplitScreenOptions(dockTopOrLeft); - } - @Override - protected boolean onActivityStarted(BaseDraggingActivity activity) { - return true; + return orientationHandler.getSplitPositionOptions(deviceProfile) + .stream() + .map((Function<SplitPositionOption, SystemShortcut>) option -> + new SplitSelectSystemShortcut(activity, taskView, option)) + .collect(Collectors.toList()); } }; - TaskShortcutFactory FREE_FORM = new MultiWindowFactory(R.drawable.ic_split_screen, - R.string.recent_task_option_freeform, LAUNCHER_SYSTEM_SHORTCUT_FREE_FORM_TAP) { - + TaskShortcutFactory FREE_FORM = new TaskShortcutFactory() { @Override - protected boolean isAvailable(BaseDraggingActivity activity, int displayId) { - return ActivityManagerWrapper.getInstance().supportsFreeformMultiWindow(activity); - } + public List<SystemShortcut> getShortcuts(BaseDraggingActivity activity, + TaskIdAttributeContainer taskContainer) { + final Task task = taskContainer.getTask(); + if (!task.isDockable) { + return null; + } + if (!isAvailable(activity, task.key.displayId)) { + return null; + } - @Override - protected ActivityOptions makeLaunchOptions(Activity activity) { - ActivityOptions activityOptions = ActivityOptionsCompat.makeFreeformOptions(); - // Arbitrary bounds only because freeform is in dev mode right now - Rect r = new Rect(50, 50, 200, 200); - activityOptions.setLaunchBounds(r); - return activityOptions; + return Collections.singletonList(new FreeformSystemShortcut(R.drawable.ic_split_screen, + R.string.recent_task_option_freeform, activity, taskContainer, + LAUNCHER_SYSTEM_SHORTCUT_FREE_FORM_TAP)); } - @Override - protected boolean onActivityStarted(BaseDraggingActivity activity) { - activity.returnToHomescreen(); - return true; + private boolean isAvailable(BaseDraggingActivity activity, int displayId) { + return ActivityManagerWrapper.getInstance().supportsFreeformMultiWindow(activity) + && !SystemProperties.getBoolean("persist.wm.debug.desktop_mode", false) + && !SystemProperties.getBoolean("persist.wm.debug.desktop_mode_2", false); } }; - TaskShortcutFactory PIN = (activity, taskContainer) -> { - if (!SystemUiProxy.INSTANCE.get(activity).isActive()) { - return null; - } - if (!ActivityManagerWrapper.getInstance().isScreenPinningEnabled()) { - return null; - } - if (ActivityManagerWrapper.getInstance().isLockToAppActive()) { - // We shouldn't be able to pin while an app is locked. - return null; + TaskShortcutFactory PIN = new TaskShortcutFactory() { + @Override + public List<SystemShortcut> getShortcuts(BaseDraggingActivity activity, + TaskIdAttributeContainer taskContainer) { + if (!SystemUiProxy.INSTANCE.get(activity).isActive()) { + return null; + } + if (!ActivityManagerWrapper.getInstance().isScreenPinningEnabled()) { + return null; + } + if (ActivityManagerWrapper.getInstance().isLockToAppActive()) { + // We shouldn't be able to pin while an app is locked. + return null; + } + return Collections.singletonList(new PinSystemShortcut(activity, taskContainer)); } - return new PinSystemShortcut(activity, taskContainer); }; class PinSystemShortcut extends SystemShortcut<BaseDraggingActivity> { @@ -335,26 +350,52 @@ public interface TaskShortcutFactory { } } - TaskShortcutFactory INSTALL = (activity, taskContainer) -> - InstantAppResolver.newInstance(activity).isInstantApp(activity, - taskContainer.getTask().getTopComponent().getPackageName()) - ? new SystemShortcut.Install(activity, taskContainer.getItemInfo(), - taskContainer.getTaskView()) : null; + TaskShortcutFactory INSTALL = new TaskShortcutFactory() { + @Override + public List<SystemShortcut> getShortcuts(BaseDraggingActivity activity, + TaskIdAttributeContainer taskContainer) { + return InstantAppResolver.newInstance(activity).isInstantApp(activity, + taskContainer.getTask().getTopComponent().getPackageName()) ? + Collections.singletonList(new SystemShortcut.Install(activity, + taskContainer.getItemInfo(), taskContainer.getTaskView())) : + null; + } + }; - TaskShortcutFactory WELLBEING = (activity, taskContainer) -> - WellbeingModel.SHORTCUT_FACTORY.getShortcut(activity, taskContainer.getItemInfo(), - taskContainer.getTaskView()); + TaskShortcutFactory WELLBEING = new TaskShortcutFactory() { + @Override + public List<SystemShortcut> getShortcuts(BaseDraggingActivity activity, + TaskIdAttributeContainer taskContainer) { + SystemShortcut<BaseDraggingActivity> wellbeingShortcut = + WellbeingModel.SHORTCUT_FACTORY.getShortcut(activity, + taskContainer.getItemInfo(), taskContainer.getTaskView()); + return createSingletonShortcutList(wellbeingShortcut); + } + }; - TaskShortcutFactory SCREENSHOT = (activity, taskContainer) -> - taskContainer.getThumbnailView().getTaskOverlay() + TaskShortcutFactory SCREENSHOT = new TaskShortcutFactory() { + @Override + public List<SystemShortcut> getShortcuts(BaseDraggingActivity activity, + TaskIdAttributeContainer taskContainer) { + SystemShortcut screenshotShortcut = taskContainer.getThumbnailView().getTaskOverlay() .getScreenshotShortcut(activity, taskContainer.getItemInfo(), taskContainer.getTaskView()); + return createSingletonShortcutList(screenshotShortcut); + } + }; - TaskShortcutFactory MODAL = (activity, taskContainer) -> { - if (ENABLE_OVERVIEW_SELECTIONS.get()) { - return taskContainer.getThumbnailView().getTaskOverlay().getModalStateSystemShortcut( - taskContainer.getItemInfo(), taskContainer.getTaskView()); + TaskShortcutFactory MODAL = new TaskShortcutFactory() { + @Override + public List<SystemShortcut> getShortcuts(BaseDraggingActivity activity, + TaskIdAttributeContainer taskContainer) { + SystemShortcut modalStateSystemShortcut = + taskContainer.getThumbnailView().getTaskOverlay() + .getModalStateSystemShortcut( + taskContainer.getItemInfo(), taskContainer.getTaskView()); + if (ENABLE_OVERVIEW_SELECTIONS.get()) { + return createSingletonShortcutList(modalStateSystemShortcut); + } + return null; } - return null; }; } diff --git a/quickstep/src/com/android/quickstep/TaskUtils.java b/quickstep/src/com/android/quickstep/TaskUtils.java index c9db1533ff..67360c4cc3 100644 --- a/quickstep/src/com/android/quickstep/TaskUtils.java +++ b/quickstep/src/com/android/quickstep/TaskUtils.java @@ -18,19 +18,23 @@ package com.android.quickstep; 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 @@ public final class TaskUtils { * 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 final class TaskUtils { } - 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 db402aff85..67de4b1679 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,13 @@ import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncest 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.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 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 +52,7 @@ import android.graphics.Matrix.ScaleToFit; 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 +62,7 @@ import androidx.annotation.Nullable; 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,6 +73,8 @@ import com.android.launcher3.statemanager.StateManager; 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; @@ -85,7 +85,6 @@ import com.android.quickstep.views.TaskView; 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 +106,7 @@ public final class TaskViewUtils { * 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 +136,7 @@ public final class TaskViewUtils { } // 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 +159,9 @@ public final class TaskViewUtils { 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 +171,7 @@ public final class TaskViewUtils { 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); @@ -197,10 +196,10 @@ public final class TaskViewUtils { 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 +254,24 @@ public final class TaskViewUtils { @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 +309,15 @@ public final class TaskViewUtils { // 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 +332,27 @@ public final class TaskViewUtils { 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 +389,8 @@ public final class TaskViewUtils { }); 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 +410,47 @@ public final class TaskViewUtils { * 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 +463,7 @@ public final class TaskViewUtils { + "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 +497,16 @@ public final class TaskViewUtils { * 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) { @@ -468,7 +516,6 @@ public final class TaskViewUtils { animatorSet.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - super.onAnimationEnd(animation); finishCallback.run(); } }); @@ -482,7 +529,7 @@ public final class TaskViewUtils { 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; @@ -530,7 +577,6 @@ public final class TaskViewUtils { for (SurfaceControl leash: closingTargets) { t.hide(leash); } - super.onAnimationEnd(animation); finishCallback.run(); } }); @@ -538,9 +584,9 @@ public final class TaskViewUtils { } 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; @@ -568,40 +614,27 @@ public final class TaskViewUtils { 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); - // Make sure recents gets fixed up by resetting task alphas and scales, etc. windowAnimEndListener = new AnimatorListenerAdapter() { @Override + public void onAnimationStart(Animator animation) { + recentsView.onTaskLaunchedInLiveTileMode(); + } + + // Make sure recents gets fixed up by resetting task alphas and scales, etc. + @Override public void onAnimationEnd(Animator animation) { recentsView.finishRecentsAnimation(false /* toRecents */, () -> { recentsView.post(() -> { @@ -626,7 +659,7 @@ public final class TaskViewUtils { }; } pa.add(launcherAnim); - if (ENABLE_QUICKSTEP_LIVE_TILE.get() && recentsView.getRunningTaskIndex() != -1) { + if (recentsView.getRunningTaskIndex() != -1) { pa.addOnFrameCallback(recentsView::redrawLiveTile); } anim.play(pa.buildAnim()); @@ -645,7 +678,7 @@ public final class TaskViewUtils { * @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; @@ -655,9 +688,9 @@ public final class TaskViewUtils { 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; } @@ -670,16 +703,18 @@ public final class TaskViewUtils { 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(); }); dockFadeAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { - super.onAnimationStart(animation); if (shown) { for (SurfaceControl leash : auxiliarySurfaces) { + t.setLayer(leash, Integer.MAX_VALUE); t.setAlpha(leash, 0); t.show(leash); } @@ -689,10 +724,11 @@ public final class TaskViewUtils { @Override public void onAnimationEnd(Animator animation) { - super.onAnimationEnd(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 723dc721a9..d4bf5c75aa 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_ASSISTANT; 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 androidx.annotation.UiThread; 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.StagedSplitTaskPosition; import com.android.launcher3.util.TraceHelper; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.recents.model.Task.TaskKey; @@ -63,8 +65,9 @@ public class TopTaskTracker extends ISplitScreenListener.Stub implements TaskSta // Ordered list with first item being the most recent task. private final LinkedList<RunningTaskInfo> mOrderedTaskList = new LinkedList<>(); - private final StagedSplitTaskPosition mMainStagePosition = new StagedSplitTaskPosition(); - private final StagedSplitTaskPosition mSideStagePosition = new StagedSplitTaskPosition(); + + private final SplitStageInfo mMainStagePosition = new SplitStageInfo(); + private final SplitStageInfo mSideStagePosition = new SplitStageInfo(); private int mPinnedTaskId = INVALID_TASK_ID; private TopTaskTracker(Context context) { @@ -84,6 +87,19 @@ public class TopTaskTracker extends ISplitScreenListener.Stub implements TaskSta 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(); @@ -144,8 +160,8 @@ public class TopTaskTracker extends ISplitScreenListener.Stub implements TaskSta mPinnedTaskId = INVALID_TASK_ID; } - private void resetTaskId(StagedSplitTaskPosition taskPosition) { - taskPosition.taskId = INVALID_TASK_ID; + private void resetTaskId(SplitStageInfo taskPosition) { + taskPosition.taskId = -1; } /** @@ -267,5 +283,16 @@ public class TopTaskTracker extends ISplitScreenListener.Stub implements TaskSta } 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 61d36fb1f7..5d17cc77f4 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,32 @@ */ package com.android.quickstep; +import static android.accessibilityservice.AccessibilityService.GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS; import static android.view.MotionEvent.ACTION_CANCEL; import static android.view.MotionEvent.ACTION_DOWN; import static android.view.MotionEvent.ACTION_UP; import static com.android.launcher3.config.FeatureFlags.ASSISTANT_GIVES_LAUNCHER_FOCUS; -import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.quickstep.GestureState.DEFAULT_STATE; +import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER; +import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.MOTION_DOWN; +import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.MOTION_UP; import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS; -import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_RECENT_TASKS; -import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_BACK_ANIMATION; -import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_ONE_HANDED; -import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_PIP; -import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SHELL_TRANSITIONS; -import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SPLIT_SCREEN; -import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_STARTING_WINDOW; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER; +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED; +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_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 +49,6 @@ import android.app.Service; 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; @@ -55,6 +60,7 @@ 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; @@ -62,24 +68,27 @@ import androidx.annotation.NonNull; 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.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.TestProtocol; +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.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; @@ -93,9 +102,9 @@ import com.android.quickstep.inputconsumers.ScreenPinnedInputConsumer; 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; @@ -105,6 +114,7 @@ import com.android.systemui.shared.system.InputMonitorCompat; 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; @@ -125,20 +135,12 @@ import java.util.function.Function; 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 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(); @@ -166,13 +168,15 @@ public class TouchInteractionService extends Service 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 */); }); @@ -212,16 +216,12 @@ public class TouchInteractionService extends Service @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)); }); } @@ -234,10 +234,9 @@ public class TouchInteractionService extends Service }); } - @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 @@ -254,16 +253,32 @@ public class TouchInteractionService extends Service MAIN_EXECUTOR.execute(() -> mDeviceState.setDeferredGestureRegion(region)); } + @BinderThread @Override - public void onSplitScreenSecondaryBoundsChanged(Rect bounds, Rect insets) { - WindowBounds wb = new WindowBounds(bounds, insets); - MAIN_EXECUTOR.execute(() -> SplitScreenBounds.INSTANCE.setSecondaryWindowBounds(wb)); + public void onScreenTurnedOn() { + MAIN_EXECUTOR.execute(ProxyScreenStatusProvider.INSTANCE::onScreenTurnedOn); } @BinderThread @Override - public void onScreenTurnedOn() { - MAIN_EXECUTOR.execute(ProxyScreenStatusProvider.INSTANCE::onScreenTurnedOn); + 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); + } } /** @@ -439,7 +454,8 @@ public class TouchInteractionService extends Service 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()); @@ -450,8 +466,6 @@ public class TouchInteractionService extends Service // 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); @@ -470,7 +484,7 @@ public class TouchInteractionService extends Service } // 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) @@ -489,11 +503,11 @@ public class TouchInteractionService extends Service 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() @@ -512,9 +526,22 @@ public class TouchInteractionService extends Service 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. @@ -559,7 +586,7 @@ public class TouchInteractionService extends Service 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; @@ -603,8 +630,6 @@ public class TouchInteractionService extends Service mConsumer.onConsumerAboutToBeSwitched(); mGestureState = newGestureState; mConsumer = newConsumer(prevGestureState, mGestureState, event); - - ActiveGestureLog.INSTANCE.addLog("setInputConsumer: " + mConsumer.getName()); mUncheckedConsumer = mConsumer; } else if (mDeviceState.isUserUnlocked() && mDeviceState.isFullyGesturalNavMode() && mDeviceState.canTriggerAssistantAction(event)) { @@ -612,8 +637,7 @@ public class TouchInteractionService extends Service // 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, @@ -633,12 +657,17 @@ public class TouchInteractionService extends Service 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; } } @@ -660,118 +689,232 @@ public class TouchInteractionService extends Service ProtoTracer.INSTANCE.get(this).scheduleFrameUpdate(); } - private InputConsumer tryCreateAssistantInputConsumer(InputConsumer base, + private InputConsumer tryCreateAssistantInputConsumer( GestureState gestureState, MotionEvent motionEvent) { - return mDeviceState.isGestureBlockedTask(gestureState.getRunningTask()) - ? base - : new AssistantInputConsumer(this, gestureState, base, mInputMonitorCompat, - mDeviceState, motionEvent); + return tryCreateAssistantInputConsumer( + InputConsumer.NO_OP, gestureState, motionEvent, CompoundString.NO_OP); + } + + private InputConsumer tryCreateAssistantInputConsumer( + InputConsumer base, + GestureState gestureState, + MotionEvent motionEvent, + CompoundString reasonString) { + if (mDeviceState.isGestureBlockedTask(gestureState.getRunningTask())) { + reasonString.append(SUBSTRING_PREFIX) + .append("is gesture-blocked task, using base input consumer"); + return base; + } else { + reasonString.append(SUBSTRING_PREFIX).append("using AssistantInputConsumer"); + return new AssistantInputConsumer( + this, gestureState, base, mInputMonitorCompat, mDeviceState, motionEvent); + } } public GestureState createGestureState(GestureState previousGestureState) { - GestureState gestureState = new GestureState(mOverviewComponentObserver, - ActiveGestureLog.INSTANCE.generateAndSetLogId()); + final GestureState gestureState; + TopTaskTracker.CachedTaskInfo taskInfo; if (mTaskAnimationManager.isRecentsAnimationRunning()) { - gestureState.updateRunningTask(previousGestureState.getRunningTask()); + gestureState = new GestureState(mOverviewComponentObserver, + ActiveGestureLog.INSTANCE.getLogId()); + taskInfo = previousGestureState.getRunningTask(); + gestureState.updateRunningTask(taskInfo); gestureState.updateLastStartedTaskId(previousGestureState.getLastStartedTaskId()); gestureState.updatePreviouslyAppearedTaskIds( previousGestureState.getPreviouslyAppearedTaskIds()); } else { - gestureState.updateRunningTask( - TopTaskTracker.INSTANCE.get(this).getCachedTopTask(false)); - } + gestureState = new GestureState(mOverviewComponentObserver, + ActiveGestureLog.INSTANCE.incrementLogId()); + taskInfo = TopTaskTracker.INSTANCE.get(this).getCachedTopTask(false); + gestureState.updateRunningTask(taskInfo); + } + // Log initial state for the gesture. + ActiveGestureLog.INSTANCE.addLog(new CompoundString("Current running task package name=") + .append(taskInfo == null ? "no running task" : taskInfo.getPackageName())); + ActiveGestureLog.INSTANCE.addLog(new CompoundString("Current SystemUi state flags=") + .append(mDeviceState.getSystemUiStateString())); return gestureState; } - private InputConsumer newConsumer(GestureState previousGestureState, - GestureState newGestureState, MotionEvent event) { + private InputConsumer newConsumer( + GestureState previousGestureState, GestureState newGestureState, MotionEvent event) { AnimatedFloat progressProxy = mSwipeUpProxyProvider.apply(mGestureState); if (progressProxy != null) { - return new ProgressDelegateInputConsumer(this, mTaskAnimationManager, - mGestureState, mInputMonitorCompat, progressProxy); + InputConsumer consumer = new ProgressDelegateInputConsumer( + this, mTaskAnimationManager, mGestureState, mInputMonitorCompat, progressProxy); + + logInputConsumerSelectionReason(consumer, newCompoundString( + "mSwipeUpProxyProvider has been set, using ProgressDelegateInputConsumer")); + + return consumer; } boolean canStartSystemGesture = mDeviceState.canStartSystemGesture(); if (!mDeviceState.isUserUnlocked()) { + CompoundString reasonString = newCompoundString("device locked"); + InputConsumer consumer; if (canStartSystemGesture) { // This handles apps launched in direct boot mode (e.g. dialer) as well as apps // launched while device is locked even after exiting direct boot mode (e.g. camera). - return createDeviceLockedInputConsumer(newGestureState); + consumer = createDeviceLockedInputConsumer( + newGestureState, reasonString.append(SUBSTRING_PREFIX) + .append("can start system gesture")); } else { - return getDefaultInputConsumer(); + consumer = getDefaultInputConsumer( + reasonString.append(SUBSTRING_PREFIX) + .append("cannot start system gesture")); } + logInputConsumerSelectionReason(consumer, reasonString); + return consumer; } + CompoundString reasonString; + InputConsumer base; // When there is an existing recents animation running, bypass systemState check as this is // a followup gesture and the first gesture started in a valid system state. - InputConsumer base = canStartSystemGesture - || previousGestureState.isRecentsAnimationRunning() - ? newBaseConsumer(previousGestureState, newGestureState, event) - : getDefaultInputConsumer(); + if (canStartSystemGesture || previousGestureState.isRecentsAnimationRunning()) { + reasonString = newCompoundString(canStartSystemGesture + ? "can start system gesture" : "recents animation was running") + .append(", trying to use base consumer"); + base = newBaseConsumer(previousGestureState, newGestureState, event, reasonString); + } else { + reasonString = newCompoundString( + "cannot start system gesture and recents animation was not running") + .append(", trying to use default input consumer"); + base = getDefaultInputConsumer(reasonString); + } if (mDeviceState.isGesturalNavMode()) { handleOrientationSetup(base); } if (mDeviceState.isFullyGesturalNavMode()) { + String reasonPrefix = "device is in gesture navigation mode"; if (mDeviceState.canTriggerAssistantAction(event)) { - base = tryCreateAssistantInputConsumer(base, newGestureState, event); + reasonString.append(NEWLINE_PREFIX) + .append(reasonPrefix) + .append(SUBSTRING_PREFIX) + .append("gesture can trigger the assistant") + .append(", trying to use assistant input consumer"); + base = tryCreateAssistantInputConsumer(base, newGestureState, event, reasonString); } // If Taskbar is present, we listen for long press to unstash it. TaskbarActivityContext tac = mTaskbarManager.getCurrentActivityContext(); if (tac != null) { + reasonString.append(NEWLINE_PREFIX) + .append(reasonPrefix) + .append(SUBSTRING_PREFIX) + .append("TaskbarActivityContext != null, using TaskbarStashInputConsumer"); base = new TaskbarStashInputConsumer(this, base, mInputMonitorCompat, tac); } - // If 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.isBubblesExpanded() && !mDeviceState.isNotificationPanelExpanded()) - || mDeviceState.isSystemUiDialogShowing()) { + if (mDeviceState.isBubblesExpanded()) { + 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); } + + if (mDeviceState.isScreenPinningActive()) { + reasonString = newCompoundString(reasonPrefix) + .append(SUBSTRING_PREFIX) + .append("screen pinning is active, using ScreenPinnedInputConsumer"); // Note: we only allow accessibility to wrap this, and it replaces the previous // base input consumer (which should be NO_OP anyway since topTaskLocked == true). base = new ScreenPinnedInputConsumer(this, newGestureState); } if (mDeviceState.canTriggerOneHandedAction(event)) { - base = new OneHandedModeInputConsumer(this, mDeviceState, base, - mInputMonitorCompat); + reasonString.append(NEWLINE_PREFIX) + .append(reasonPrefix) + .append(SUBSTRING_PREFIX) + .append("gesture can trigger one handed mode") + .append(", using OneHandedModeInputConsumer"); + base = new OneHandedModeInputConsumer( + this, mDeviceState, base, mInputMonitorCompat); } if (mDeviceState.isAccessibilityMenuAvailable()) { - base = new AccessibilityInputConsumer(this, mDeviceState, base, - mInputMonitorCompat); + reasonString.append(NEWLINE_PREFIX) + .append(reasonPrefix) + .append(SUBSTRING_PREFIX) + .append("accessibility menu is available") + .append(", using AccessibilityInputConsumer"); + base = new AccessibilityInputConsumer( + this, mDeviceState, base, mInputMonitorCompat); } } else { + String reasonPrefix = "device is not in gesture navigation mode"; if (mDeviceState.isScreenPinningActive()) { - base = getDefaultInputConsumer(); + reasonString = newCompoundString(reasonPrefix) + .append(SUBSTRING_PREFIX) + .append("screen pinning is active, trying to use default input consumer"); + base = getDefaultInputConsumer(reasonString); } if (mDeviceState.canTriggerOneHandedAction(event)) { - base = new OneHandedModeInputConsumer(this, mDeviceState, base, - mInputMonitorCompat); + reasonString.append(NEWLINE_PREFIX) + .append(reasonPrefix) + .append(SUBSTRING_PREFIX) + .append("gesture can trigger one handed mode") + .append(", using OneHandedModeInputConsumer"); + base = new OneHandedModeInputConsumer( + this, mDeviceState, base, mInputMonitorCompat); } } + logInputConsumerSelectionReason(base, reasonString); return base; } + private CompoundString newCompoundString(String substring) { + return new CompoundString(NEWLINE_PREFIX).append(substring); + } + + private void logInputConsumerSelectionReason( + InputConsumer consumer, CompoundString reasonString) { + if (!FeatureFlags.ENABLE_INPUT_CONSUMER_REASON_LOGGING.get()) { + ActiveGestureLog.INSTANCE.addLog("setInputConsumer: " + consumer.getName()); + return; + } + ActiveGestureLog.INSTANCE.addLog(new CompoundString("setInputConsumer: ") + .append(consumer.getName()) + .append(". reason(s):") + .append(reasonString)); + if ((consumer.getType() & InputConsumer.TYPE_OTHER_ACTIVITY) != 0) { + ActiveGestureLog.INSTANCE.trackEvent(FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER); + } + } + private void handleOrientationSetup(InputConsumer baseInputConsumer) { baseInputConsumer.notifyOrientationSetup(); } - private InputConsumer newBaseConsumer(GestureState previousGestureState, - GestureState gestureState, MotionEvent event) { + private InputConsumer newBaseConsumer( + GestureState previousGestureState, + GestureState gestureState, + MotionEvent event, + CompoundString reasonString) { if (mDeviceState.isKeyguardShowingOccluded()) { // This handles apps showing over the lockscreen (e.g. camera) - return createDeviceLockedInputConsumer(gestureState); + return createDeviceLockedInputConsumer( + gestureState, + reasonString.append(SUBSTRING_PREFIX) + .append("keyguard is showing occluded") + .append(", trying to use device locked input consumer")); } + reasonString.append(SUBSTRING_PREFIX).append("keyguard is not showing occluded"); // Use overview input consumer for sharesheets on top of home. boolean forceOverviewInputConsumer = gestureState.getActivityInterface().isStarted() && gestureState.getRunningTask() != null @@ -785,23 +928,45 @@ public class TouchInteractionService extends Service 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); } } @@ -823,32 +988,63 @@ public class TouchInteractionService extends Service 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(); - } - - if (activity.getRootView().hasWindowFocus() - || previousGestureState.isRunningAnimationToLauncher() - || (ASSISTANT_GIVES_LAUNCHER_FOCUS.get() - && forceOverviewInputConsumer) - || (ENABLE_QUICKSTEP_LIVE_TILE.get() - && gestureState.getActivityInterface().isInLiveTileMode())) { + return getDefaultInputConsumer( + reasonString.append(SUBSTRING_PREFIX) + .append("activity == null, trying to use default input consumer")); + } + + 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); @@ -876,13 +1072,21 @@ public class TouchInteractionService extends Service } } + 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; @@ -997,22 +1201,39 @@ public class TouchInteractionService extends Service 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 1fef544e3e..b1320674e6 100644 --- a/quickstep/src/com/android/quickstep/ViewUtils.java +++ b/quickstep/src/com/android/quickstep/ViewUtils.java @@ -17,6 +17,7 @@ package com.android.quickstep; import android.graphics.HardwareRenderer; import android.os.Handler; +import android.view.SurfaceControl; import android.view.View; import android.view.ViewRootImpl; @@ -45,12 +46,15 @@ public class ViewUtils { 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 @@ public class ViewUtils { } @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 @@ public class ViewUtils { 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 3e01ed0b77..8a87f63aaf 100644 --- a/quickstep/src/com/android/quickstep/fallback/FallbackNavBarTouchController.java +++ b/quickstep/src/com/android/quickstep/fallback/FallbackNavBarTouchController.java @@ -22,7 +22,7 @@ import androidx.annotation.Nullable; 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 f68bbbce62..062e50e30b 100644 --- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java +++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java @@ -33,6 +33,7 @@ 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; @@ -44,7 +45,7 @@ import com.android.launcher3.anim.PendingAnimation; 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; @@ -77,6 +78,11 @@ public class FallbackRecentsStateController implements StateHandler<RecentsState } // 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); @@ -89,7 +95,7 @@ public class FallbackRecentsStateController implements StateHandler<RecentsState 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], @@ -105,14 +111,19 @@ public class FallbackRecentsStateController implements StateHandler<RecentsState boolean showAsGrid = state.displayOverviewTasksAsGrid(mActivity.getDeviceProfile()); setter.setFloat(mRecentsView, RECENTS_GRID_PROGRESS, showAsGrid ? 1f : 0f, showAsGrid ? INSTANT : FINAL_FRAME); + 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 ab3201a27e..bcaae99e2e 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.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.annotation.TargetApi; 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 androidx.annotation.Nullable; 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 @@ import java.util.ArrayList; 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 @@ public class FallbackRecentsView extends RecentsView<RecentsActivity, RecentsSta 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 @@ public class FallbackRecentsView extends RecentsView<RecentsActivity, RecentsSta } @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 @@ public class FallbackRecentsView extends RecentsView<RecentsActivity, RecentsSta @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 @@ public class FallbackRecentsView extends RecentsView<RecentsActivity, RecentsSta if (toState == MODAL_TASK) { setOverviewSelectEnabled(true); } - Log.d(BAD_STATE, "FRV onStateTransitionStart setFreezeVisibility=true, toState=" + toState); setFreezeViewVisibility(true); } @@ -237,12 +238,13 @@ public class FallbackRecentsView extends RecentsView<RecentsActivity, RecentsSta } 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 c0b6771e6a..601021b5bf 100644 --- a/quickstep/src/com/android/quickstep/fallback/RecentsState.java +++ b/quickstep/src/com/android/quickstep/fallback/RecentsState.java @@ -15,6 +15,7 @@ */ package com.android.quickstep.fallback; +import static com.android.launcher3.LauncherState.FLAG_CLOSE_POPUPS; import static com.android.launcher3.uioverrides.states.BackgroundAppState.getOverviewScaleAndOffsetForBackgroundState; import static com.android.launcher3.uioverrides.states.OverviewModalTaskState.getOverviewScaleAndOffsetForModalState; @@ -40,6 +41,7 @@ public class RecentsState implements BaseState<RecentsState> { 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 @@ -48,11 +50,13 @@ public class RecentsState implements BaseState<RecentsState> { 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_SHOW_AS_GRID | FLAG_SCRIM | FLAG_OVERVIEW_UI | FLAG_CLOSE_POPUPS + | FLAG_DISABLE_RESTORE); public final int ordinal; private final int mFlags; @@ -138,6 +142,11 @@ public class RecentsState implements BaseState<RecentsState> { 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 eca61bb07d..db4927a0d1 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 @@ public class RecentsTaskController extends TaskViewTouchController<RecentsActivi @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/DelegateInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java index 8da2fd3fed..03f8eef077 100644 --- a/quickstep/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java +++ b/quickstep/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java @@ -3,7 +3,7 @@ package com.android.quickstep.inputconsumers; import android.view.MotionEvent; import com.android.launcher3.testing.TestLogging; -import com.android.launcher3.testing.TestProtocol; +import com.android.launcher3.testing.shared.TestProtocol; import com.android.launcher3.tracing.InputConsumerProto; import com.android.quickstep.InputConsumer; import com.android.systemui.shared.system.InputMonitorCompat; diff --git a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java index 3d737ca0e4..5374ff0405 100644 --- a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java +++ b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java @@ -19,12 +19,13 @@ import static android.view.MotionEvent.ACTION_CANCEL; import static android.view.MotionEvent.ACTION_POINTER_DOWN; import static android.view.MotionEvent.ACTION_UP; -import static com.android.launcher3.Utilities.createHomeIntent; import static com.android.launcher3.Utilities.squaredHypot; import static com.android.launcher3.Utilities.squaredTouchSlop; import static com.android.launcher3.util.VelocityUtils.PX_PER_MS; import static com.android.quickstep.AbsSwipeUpHandler.MIN_PROGRESS_FOR_OVERVIEW; import static com.android.quickstep.MultiStateCallback.DEBUG_STATES; +import static com.android.quickstep.OverviewComponentObserver.startHomeIntentSafely; +import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS; import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID; import android.animation.Animator; @@ -36,14 +37,15 @@ import android.graphics.Matrix; 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.TestProtocol; +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; @@ -51,14 +53,14 @@ import com.android.quickstep.RecentsAnimationCallbacks; 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; @@ -101,6 +103,8 @@ public class DeviceLockedInputConsumer implements InputConsumer, private boolean mThresholdCrossed = false; private boolean mHomeLaunched = false; + private boolean mCancelWhenRecentsStart = false; + private boolean mDismissTask = false; private RecentsAnimationController mRecentsAnimationController; @@ -204,14 +208,29 @@ public class DeviceLockedInputConsumer implements InputConsumer, animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - if (dismissTask) { - // For now, just start the home intent so user is prompted to unlock the device. - mContext.startActivity(createHomeIntent()); + if (ENABLE_SHELL_TRANSITIONS) { + if (mTaskAnimationManager.getCurrentCallbacks() != null) { + if (mRecentsAnimationController != null) { + finishRecentsAnimationForShell(dismissTask); + } else { + // the transition of recents animation hasn't started, wait for it + mCancelWhenRecentsStart = true; + mDismissTask = dismissTask; + } + } + } else if (dismissTask) { + // For now, just start the home intent so user is prompted to + // unlock the device. + startHomeIntentSafely(mContext, mGestureState.getHomeIntent(), null); mHomeLaunched = true; } 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); @@ -238,12 +257,24 @@ public class DeviceLockedInputConsumer implements InputConsumer, mTransformParams.setTargetSet(targets); applyTransform(); mStateCallback.setState(STATE_TARGET_RECEIVED); + if (mCancelWhenRecentsStart) { + finishRecentsAnimationForShell(mDismissTask); + } } @Override public void onRecentsAnimationCanceled(HashMap<Integer, ThumbnailData> thumbnailDatas) { mRecentsAnimationController = null; mTransformParams.setTargetSet(null); + mCancelWhenRecentsStart = false; + } + + private void finishRecentsAnimationForShell(boolean dismissTask) { + mCancelWhenRecentsStart = false; + mTaskAnimationManager.finishRunningRecentsAnimation(dismissTask /* toHome */); + if (dismissTask) { + mHomeLaunched = true; + } } private void endRemoteAnimation() { @@ -264,9 +295,9 @@ public class DeviceLockedInputConsumer implements InputConsumer, @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 @@ -278,4 +309,27 @@ public class DeviceLockedInputConsumer implements InputConsumer, 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/OneHandedModeInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java index bc209027e8..d7ed79ba0a 100644 --- a/quickstep/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java +++ b/quickstep/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java @@ -21,7 +21,7 @@ import static android.view.MotionEvent.ACTION_DOWN; import static android.view.MotionEvent.ACTION_MOVE; import static android.view.MotionEvent.ACTION_UP; -import static com.android.launcher3.ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE; +import static com.android.launcher3.testing.shared.ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE; import static com.android.launcher3.Utilities.squaredHypot; import android.content.Context; @@ -30,7 +30,7 @@ import android.graphics.PointF; import android.view.MotionEvent; import com.android.launcher3.R; -import com.android.launcher3.ResourceUtils; +import com.android.launcher3.testing.shared.ResourceUtils; import com.android.launcher3.Utilities; import com.android.launcher3.util.DisplayController; import com.android.quickstep.InputConsumer; diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java index e458c1f022..a7ae6b560b 100644 --- a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java +++ b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java @@ -27,6 +27,7 @@ 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.squaredHypot; +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.TraceHelper.FLAG_CHECK_FOR_RACE_CONDITIONS; import static com.android.launcher3.util.VelocityUtils.PX_PER_MS; import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID; @@ -37,8 +38,6 @@ import android.content.ContextWrapper; 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; @@ -47,8 +46,9 @@ import android.view.ViewConfiguration; import androidx.annotation.UiThread; import com.android.launcher3.R; +import com.android.launcher3.Utilities; import com.android.launcher3.testing.TestLogging; -import com.android.launcher3.testing.TestProtocol; +import com.android.launcher3.testing.shared.TestProtocol; import com.android.launcher3.tracing.InputConsumerProto; import com.android.launcher3.util.Preconditions; import com.android.launcher3.util.TraceHelper; @@ -58,14 +58,14 @@ import com.android.quickstep.BaseActivityInterface; import com.android.quickstep.GestureState; import com.android.quickstep.InputConsumer; import com.android.quickstep.RecentsAnimationCallbacks; +import com.android.quickstep.RecentsAnimationController; import com.android.quickstep.RecentsAnimationDeviceState; +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; @@ -107,6 +107,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC private VelocityTracker mVelocityTracker; private AbsSwipeUpHandler mInteractionHandler; + private final FinishImmediatelyHandler mCleanupHandler = new FinishImmediatelyHandler(); private final boolean mIsDeferredDownTarget; private final PointF mDownPos = new PointF(); @@ -130,12 +131,6 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC // 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, @@ -146,7 +141,6 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC mNavBarPosition = mDeviceState.getNavBarPosition(); mTaskAnimationManager = taskAnimationManager; mGestureState = gestureState; - mMainThreadHandler = new Handler(Looper.getMainLooper()); mHandlerFactory = handlerFactory; mActivityInterface = mGestureState.getActivityInterface(); @@ -286,6 +280,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC float upDist = -displacement; boolean passedSlop = squaredHypot(displacementX, displacementY) >= mSquaredTouchSlop; + if (!mPassedSlopOnThisGesture && passedSlop) { mPassedSlopOnThisGesture = true; } @@ -330,7 +325,10 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC } 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); @@ -354,7 +352,6 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC } private void notifyGestureStarted(boolean isLikelyToStartNewTask) { - ActiveGestureLog.INSTANCE.addLog("startQuickstep"); if (mInteractionHandler == null) { return; } @@ -368,8 +365,6 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC } private void startTouchTrackingForWindowAnimation(long touchTimeMs) { - ActiveGestureLog.INSTANCE.addLog("startRecentsAnimation"); - mInteractionHandler = mHandlerFactory.newHandler(mGestureState, touchTimeMs); mInteractionHandler.setGestureEndCallback(this::onInteractionGestureFinished); mMotionPauseDetector.setOnMotionPauseListener(mInteractionHandler.getMotionPauseListener()); @@ -377,6 +372,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC if (mTaskAnimationManager.isRecentsAnimationRunning()) { mActiveCallbacks = mTaskAnimationManager.continueRecentsAnimation(mGestureState); + mActiveCallbacks.removeListener(mCleanupHandler); mActiveCallbacks.addListener(mInteractionHandler); mTaskAnimationManager.notifyRecentsAnimationState(mInteractionHandler); notifyGestureStarted(true /*isLikelyToStartNewTask*/); @@ -401,29 +397,34 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC mInteractionHandler.onGestureCancelled(); } else { mVelocityTracker.computeCurrentVelocity(PX_PER_MS); - float velocityX = mVelocityTracker.getXVelocity(mActivePointerId); - float velocityY = mVelocityTracker.getYVelocity(mActivePointerId); - float velocity = mNavBarPosition.isRightEdge() - ? velocityX + float velocityXPxPerMs = mVelocityTracker.getXVelocity(mActivePointerId); + float velocityYPxPerMs = mVelocityTracker.getYVelocity(mActivePointerId); + float velocityPxPerMs = mNavBarPosition.isRightEdge() + ? velocityXPxPerMs : mNavBarPosition.isLeftEdge() - ? -velocityX - : velocityY; + ? -velocityXPxPerMs + : velocityYPxPerMs; mInteractionHandler.updateDisplacement(getDisplacement(ev) - mStartDisplacement); - mInteractionHandler.onGestureEnded(velocity, new PointF(velocityX, velocityY), - mDownPos); + mInteractionHandler.onGestureEnded( + velocityPxPerMs, new PointF(velocityXPxPerMs, velocityYPxPerMs), mDownPos); } } else { // Since we start touch tracking on DOWN, we may reach this state without actually - // starting the gesture. In that case, just cleanup immediately. + // starting the gesture. In that case, we need to clean-up an unfinished or un-started + // animation. + if (mActiveCallbacks != null && mInteractionHandler != null) { + if (mTaskAnimationManager.isRecentsAnimationRunning()) { + // The animation started, but with no movement, in this case, there will be no + // animateToProgress so we have to manually finish here. + mTaskAnimationManager.finishRunningRecentsAnimation(false /* toHome */); + } else { + // The animation hasn't started yet, so insert a replacement handler into the + // callbacks which immediately finishes the animation after it starts. + mActiveCallbacks.addListener(mCleanupHandler); + } + } 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); @@ -445,7 +446,6 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC @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 @@ -464,7 +464,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC } private void removeListener() { - if (mActiveCallbacks != null) { + if (mActiveCallbacks != null && mInteractionHandler != null) { mActiveCallbacks.removeListener(mInteractionHandler); } } @@ -490,4 +490,19 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC mInteractionHandler.writeToProto(inputConsumerProto); } } + + /** + * A listener which just finishes the animation immediately after starting. Replaces + * AbsSwipeUpHandler if the gesture itself finishes before the animation even starts. + */ + private static class FinishImmediatelyHandler + implements RecentsAnimationCallbacks.RecentsAnimationListener { + + public void onRecentsAnimationStart(RecentsAnimationController controller, + RecentsAnimationTargets targets) { + Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> { + controller.finish(false /* toRecents */, null); + }); + } + } } diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java index 02ac48ebeb..64165b66e4 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; @@ -29,13 +28,12 @@ import com.android.launcher3.Utilities; import com.android.launcher3.statemanager.BaseState; import com.android.launcher3.statemanager.StatefulActivity; import com.android.launcher3.testing.TestLogging; -import com.android.launcher3.testing.TestProtocol; +import com.android.launcher3.testing.shared.TestProtocol; import com.android.launcher3.views.BaseDragLayer; import com.android.quickstep.BaseActivityInterface; 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 @@ public class OverviewInputConsumer<S extends BaseState<S>, T extends StatefulAct 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 @@ public class OverviewInputConsumer<S extends BaseState<S>, T extends StatefulAct @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 864e08da46..b70fe8e03e 100644 --- a/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java +++ b/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java @@ -15,12 +15,11 @@ */ package com.android.quickstep.inputconsumers; -import static com.android.launcher3.Utilities.createHomeIntent; import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND; import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_GESTURE; +import static com.android.quickstep.OverviewComponentObserver.startHomeIntentSafely; -import android.content.ActivityNotFoundException; import android.content.Context; import android.graphics.PointF; import android.view.MotionEvent; @@ -29,11 +28,10 @@ import com.android.launcher3.BaseActivity; import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.logger.LauncherAtom; import com.android.launcher3.testing.TestLogging; -import com.android.launcher3.testing.TestProtocol; +import com.android.launcher3.testing.shared.TestProtocol; 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,12 +77,7 @@ public class OverviewWithoutFocusInputConsumer implements InputConsumer, @Override public void onSwipeUp(boolean wasFling, PointF finalVelocity) { - try { - mContext.startActivity(mGestureState.getHomeIntent()); - } catch (NullPointerException | ActivityNotFoundException | SecurityException e) { - mContext.startActivity(createHomeIntent()); - } - ActiveGestureLog.INSTANCE.addLog("startQuickstep"); + startHomeIntentSafely(mContext, mGestureState.getHomeIntent(), null); 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 71dca663f0..45ffa1c510 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.content.Intent; 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.TestProtocol; +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 d34b40bf0c..349f4d2f2f 100644 --- a/quickstep/src/com/android/quickstep/inputconsumers/ResetGestureInputConsumer.java +++ b/quickstep/src/com/android/quickstep/inputconsumers/ResetGestureInputConsumer.java @@ -17,18 +17,25 @@ package com.android.quickstep.inputconsumers; 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 class ResetGestureInputConsumer implements InputConsumer { 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/SysUiOverlayInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/SysUiOverlayInputConsumer.java index 878f132531..4806ac1d6e 100644 --- a/quickstep/src/com/android/quickstep/inputconsumers/SysUiOverlayInputConsumer.java +++ b/quickstep/src/com/android/quickstep/inputconsumers/SysUiOverlayInputConsumer.java @@ -23,7 +23,7 @@ import android.util.Log; import android.view.MotionEvent; import com.android.launcher3.testing.TestLogging; -import com.android.launcher3.testing.TestProtocol; +import com.android.launcher3.testing.shared.TestProtocol; import com.android.quickstep.InputConsumer; import com.android.quickstep.RecentsAnimationDeviceState; import com.android.quickstep.util.TriggerSwipeUpTouchTracker; diff --git a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java index 3785de4ecd..3afd0a3431 100644 --- a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java +++ b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java @@ -15,16 +15,27 @@ */ package com.android.quickstep.inputconsumers; +import static android.view.MotionEvent.INVALID_POINTER_ID; + import static com.android.launcher3.Utilities.squaredHypot; +import static com.android.launcher3.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 +48,41 @@ public class TaskbarStashInputConsumer extends DelegateInputConsumer { 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 +90,10 @@ public class TaskbarStashInputConsumer extends DelegateInputConsumer { onLongPressDetected(motionEvent); } }); + + mTransitionCallback = mIsTransientTaskbar + ? taskbarActivityContext.getTranslationCallbacks() + : null; } @Override @@ -76,28 +112,81 @@ public class TaskbarStashInputConsumer extends DelegateInputConsumer { 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 +200,9 @@ public class TaskbarStashInputConsumer extends DelegateInputConsumer { } 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 5680170a8f..e0262d02eb 100644 --- a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java +++ b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java @@ -19,6 +19,7 @@ import static com.android.launcher3.Utilities.mapBoundToRange; import static com.android.launcher3.Utilities.mapRange; import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN; import static com.android.launcher3.anim.Interpolators.LINEAR; +import static com.android.quickstep.OverviewComponentObserver.startHomeIntentSafely; import android.animation.Animator; import android.app.Activity; @@ -41,6 +42,7 @@ import android.graphics.drawable.Drawable; 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; @@ -52,12 +54,13 @@ import android.widget.TextView; 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; @@ -77,11 +80,14 @@ public class AllSetActivity extends Activity { "#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; private static final int MAX_SWIPE_DURATION = 350; + private static final float ANIMATION_PAUSE_ALPHA_THRESHOLD = 0.1f; + private TISBindHelper mTISBindHelper; private TISBinder mBinder; @@ -106,7 +112,8 @@ public class AllSetActivity extends Activity { 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); @@ -117,11 +124,12 @@ public class AllSetActivity extends Activity { 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); @@ -132,66 +140,85 @@ public class AllSetActivity extends Activity { } 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(); } private void runOnUiHelperThread(Runnable runnable) { + if (!isResumed() + || getContentViewAlphaForSwipeProgress() <= ANIMATION_PAUSE_ALPHA_THRESHOLD) { + return; + } Executors.UI_HELPER_EXECUTOR.execute(runnable); } private void startBackgroundAnimation() { - if (Utilities.ATLEAST_S && mVibrator != null && mVibrator.areAllPrimitivesSupported( - VibrationEffect.Composition.PRIMITIVE_THUD)) { - if (mBackgroundAnimatorListener == null) { - mBackgroundAnimatorListener = - new Animator.AnimatorListener() { - @Override - public void onAnimationStart(Animator animation) { - runOnUiHelperThread(() -> mVibrator.vibrate(getVibrationEffect())); - } - - @Override - public void onAnimationRepeat(Animator animation) { - runOnUiHelperThread(() -> mVibrator.vibrate(getVibrationEffect())); - } - - @Override - public void onAnimationEnd(Animator animation) { - runOnUiHelperThread(mVibrator::cancel); - } - - @Override - public void onAnimationCancel(Animator animation) { - runOnUiHelperThread(mVibrator::cancel); - } - }; - } - mAnimatedBackground.addAnimatorListener(mBackgroundAnimatorListener); + if (!Utilities.ATLEAST_S || mVibrator == null) { + return; } - mAnimatedBackground.playAnimation(); - } + boolean supportsThud = mVibrator.areAllPrimitivesSupported( + VibrationEffect.Composition.PRIMITIVE_THUD); - /** - * Sets up the vibration effect for the next round of animation. The parameters vary between - * different illustrations. - */ - private VibrationEffect getVibrationEffect() { - return VibrationEffect.startComposition() - .addPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD, 1.0f, 50) - .compose(); + if (!supportsThud && !mVibrator.areAllPrimitivesSupported( + VibrationEffect.Composition.PRIMITIVE_TICK)) { + return; + } + if (mBackgroundAnimatorListener == null) { + VibrationEffect vibrationEffect = VibrationEffect.startComposition() + .addPrimitive(supportsThud + ? VibrationEffect.Composition.PRIMITIVE_THUD + : VibrationEffect.Composition.PRIMITIVE_TICK, + /* scale= */ 1.0f, + /* delay= */ 50) + .compose(); + + mBackgroundAnimatorListener = + new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator animation) { + runOnUiHelperThread(() -> mVibrator.vibrate(vibrationEffect)); + } + + @Override + public void onAnimationRepeat(Animator animation) { + runOnUiHelperThread(() -> mVibrator.vibrate(vibrationEffect)); + } + + @Override + public void onAnimationEnd(Animator animation) { + runOnUiHelperThread(mVibrator::cancel); + } + + @Override + public void onAnimationCancel(Animator animation) { + runOnUiHelperThread(mVibrator::cancel); + } + }; + } + mAnimatedBackground.addAnimatorListener(mBackgroundAnimatorListener); + mAnimatedBackground.playAnimation(); } @Override protected void onResume() { super.onResume(); + maybeResumeOrPauseBackgroundAnimation(); if (mBinder != null) { mBinder.getTaskbarManager().setSetupUIVisible(true); mBinder.setSwipeUpProxy(this::createSwipeUpProxy); @@ -210,6 +237,7 @@ public class AllSetActivity extends Activity { protected void onPause() { super.onPause(); clearBinderOverride(); + maybeResumeOrPauseBackgroundAnimation(); if (mSwipeProgress.value >= 1) { finishAndRemoveTask(); } @@ -234,9 +262,6 @@ public class AllSetActivity extends Activity { } private AnimatedFloat createSwipeUpProxy(GestureState state) { - if (!state.getHomeIntent().getComponent().getPackageName().equals(getPackageName())) { - return null; - } if (state.getRunningTaskId() != getTaskId()) { return null; } @@ -244,10 +269,25 @@ public class AllSetActivity extends Activity { return mSwipeProgress; } + private float getContentViewAlphaForSwipeProgress() { + return Utilities.mapBoundToRange( + mSwipeProgress.value, 0, HINT_BOTTOM_FACTOR, 1, 0, LINEAR); + } + + private void maybeResumeOrPauseBackgroundAnimation() { + boolean shouldPlayAnimation = + getContentViewAlphaForSwipeProgress() > ANIMATION_PAUSE_ALPHA_THRESHOLD + && isResumed(); + if (mAnimatedBackground.isAnimating() && !shouldPlayAnimation) { + mAnimatedBackground.pauseAnimation(); + } else if (!mAnimatedBackground.isAnimating() && shouldPlayAnimation) { + mAnimatedBackground.resumeAnimation(); + } + } + private void onSwipeProgressUpdate() { mBackground.setProgress(mSwipeProgress.value); - float alpha = Utilities.mapBoundToRange( - mSwipeProgress.value, 0, HINT_BOTTOM_FACTOR, 1, 0, LINEAR); + float alpha = getContentViewAlphaForSwipeProgress(); mContentView.setAlpha(alpha); mContentView.setTranslationY((alpha - 1) * mSwipeUpShift); @@ -259,12 +299,7 @@ public class AllSetActivity extends Activity { mLauncherStartAnim.setPlayFraction(Utilities.mapBoundToRange( mSwipeProgress.value, 0, 1, 0, 1, FAST_OUT_SLOW_IN)); } - - if (alpha == 0f) { - mAnimatedBackground.pauseAnimation(); - } else if (!mAnimatedBackground.isAnimating()) { - mAnimatedBackground.resumeAnimation(); - } + maybeResumeOrPauseBackgroundAnimation(); } /** @@ -284,7 +319,7 @@ public class AllSetActivity extends Activity { @Override public boolean performAccessibilityAction(View host, int action, Bundle args) { if (action == AccessibilityAction.ACTION_CLICK.getId()) { - startActivity(Utilities.createHomeIntent()); + startHomeIntentSafely(AllSetActivity.this, null); finish(); return true; } diff --git a/quickstep/src/com/android/quickstep/interaction/EdgeBackGestureHandler.java b/quickstep/src/com/android/quickstep/interaction/EdgeBackGestureHandler.java index d059d828f8..8660d871cc 100644 --- a/quickstep/src/com/android/quickstep/interaction/EdgeBackGestureHandler.java +++ b/quickstep/src/com/android/quickstep/interaction/EdgeBackGestureHandler.java @@ -29,7 +29,7 @@ import android.view.ViewGroup.LayoutParams; import androidx.annotation.Nullable; -import com.android.launcher3.ResourceUtils; +import com.android.launcher3.testing.shared.ResourceUtils; import com.android.launcher3.Utilities; import com.android.launcher3.util.DisplayController; diff --git a/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java b/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java index b2b2f59773..8eb40593c9 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.SpringAnimation; import androidx.dynamicanimation.animation.SpringForce; import com.android.launcher3.R; -import com.android.launcher3.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 bf7023c217..4a701202db 100644 --- a/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java +++ b/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java @@ -28,8 +28,8 @@ import android.view.Window; 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 @@ public class GestureSandboxActivity extends FragmentActivity { 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 f9818600cf..57874d9deb 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.NavBarGestu 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 android.view.ViewConfiguration; import androidx.annotation.Nullable; import com.android.launcher3.R; -import com.android.launcher3.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 6b016cee13..05b246b906 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.graphics.PointF; 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 b70c411196..670ee9b333 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.PointF; 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 androidx.annotation.Nullable; 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 @@ abstract class SwipeUpGestureTutorialController extends TutorialController { }; RectFSpringAnim windowAnim = createWindowAnimationToHome(startShift, homeAnimFactory)[0]; - windowAnim.start(mContext, velocityPxPerMs); + windowAnim.start(mContext, mDp, velocityPxPerMs); return windowAnim; } } @@ -415,21 +416,23 @@ abstract class SwipeUpGestureTutorialController extends TutorialController { 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 bd0250d27d..5efc45e385 100644 --- a/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java +++ b/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java @@ -16,8 +16,8 @@ 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.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; @@ -40,15 +40,14 @@ import android.util.Xml; import com.android.launcher3.AutoInstallsLayout; import com.android.launcher3.R; -import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.logging.InstanceId; import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.logging.StatsLogManager.StatsLogger; import com.android.launcher3.model.DeviceGridState; import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.DisplayController.Info; -import com.android.launcher3.util.DisplayController.NavigationMode; import com.android.launcher3.util.MainThreadInitializedObject; +import com.android.launcher3.util.NavigationMode; import com.android.launcher3.util.SettingsCache; import org.xmlpull.v1.XmlPullParser; @@ -179,11 +178,9 @@ public class SettingsChangeLogger implements logger::log); SharedPreferences prefs = getPrefs(mContext); - if (FeatureFlags.ENABLE_THEMED_ICONS.get()) { - logger.log(prefs.getBoolean(KEY_THEMED_ICONS, false) - ? LAUNCHER_THEMED_ICON_ENABLED - : LAUNCHER_THEMED_ICON_DISABLED); - } + logger.log(prefs.getBoolean(KEY_THEMED_ICONS, false) + ? LAUNCHER_THEMED_ICON_ENABLED + : LAUNCHER_THEMED_ICON_DISABLED); mLoggablePrefs.forEach((key, lp) -> logger.log(() -> prefs.getBoolean(key, lp.defaultValue) ? lp.eventIdOn : lp.eventIdOff)); diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java index 45c80366f4..0a155cbd47 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.BgDataModel; 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 @@ public class StatsLogCompatManager extends StatsLogManager { 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 @@ public class StatsLogCompatManager extends StatsLogManager { @Override protected StatsLatencyLogger createLatencyLogger() { - return new StatsCompatLatencyLogger(mContext, mActivityContext); + return new StatsCompatLatencyLogger(); + } + + @Override + protected StatsImpressionLogger createImpressionLogger() { + return new StatsCompatImpressionLogger(); } /** @@ -190,7 +199,8 @@ public class StatsLogCompatManager extends StatsLogManager { 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 @@ public class StatsLogCompatManager extends StatsLogManager { 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 @@ public class StatsLogCompatManager extends StatsLogManager { } @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 @@ public class StatsLogCompatManager extends StatsLogManager { 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 @@ public class StatsLogCompatManager extends StatsLogManager { 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 @@ public class StatsLogCompatManager extends StatsLogManager { 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 @@ public class StatsLogCompatManager extends StatsLogManager { 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 @@ public class StatsLogCompatManager extends StatsLogManager { * 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 @@ public class StatsLogCompatManager extends StatsLogManager { } @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 @@ public class StatsLogCompatManager extends StatsLogManager { 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 0000000000..60065fb16c --- /dev/null +++ b/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java @@ -0,0 +1,364 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.quickstep.util; + +import android.util.ArraySet; + +import androidx.annotation.NonNull; + +import java.io.PrintWriter; +import java.util.List; +import java.util.Set; + +/** + * Utility class for tracking gesture navigation events as they happen, then detecting and reporting + * known issues at log dump time. + */ +public class ActiveGestureErrorDetector { + + /** + * Enums associated to gesture navigation events. + */ + public enum GestureEvent { + MOTION_DOWN, MOTION_UP, SET_END_TARGET, SET_END_TARGET_HOME, SET_END_TARGET_NEW_TASK, + ON_SETTLED_ON_END_TARGET, START_RECENTS_ANIMATION, FINISH_RECENTS_ANIMATION, + CANCEL_RECENTS_ANIMATION, SET_ON_PAGE_TRANSITION_END_CALLBACK, CANCEL_CURRENT_ANIMATION, + CLEANUP_SCREENSHOT, SCROLLER_ANIMATION_ABORTED, TASK_APPEARED, EXPECTING_TASK_APPEARED, + FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER, + + /** + * These GestureEvents are specifically associated to state flags that get set in + * {@link com.android.quickstep.MultiStateCallback}. If a state flag needs to be tracked + * for error detection, an enum should be added here and that state flag-enum pair should + * be added to the state flag's container class' {@code getTrackedEventForState} method. + */ + STATE_GESTURE_STARTED, STATE_GESTURE_COMPLETED, STATE_GESTURE_CANCELLED, + STATE_END_TARGET_ANIMATION_FINISHED, STATE_RECENTS_SCROLLING_FINISHED, + STATE_CAPTURE_SCREENSHOT, STATE_SCREENSHOT_CAPTURED, STATE_HANDLER_INVALIDATED, + STATE_RECENTS_ANIMATION_CANCELED, STATE_LAUNCHER_DRAWN(true, false); + + public final boolean mLogEvent; + public final boolean mTrackEvent; + + GestureEvent() { + this(false, true); + } + + GestureEvent(boolean logEvent, boolean trackEvent) { + mLogEvent = logEvent; + mTrackEvent = trackEvent; + } + } + + private ActiveGestureErrorDetector() {} + + protected static void analyseAndDump( + @NonNull String prefix, + @NonNull PrintWriter writer, + List<ActiveGestureLog.EventLog> eventLogs) { + writer.println(prefix + "ActiveGestureErrorDetector:"); + for (int i = 0; i < eventLogs.size(); i++) { + ActiveGestureLog.EventLog eventLog = eventLogs.get(i); + if (eventLog == null) { + continue; + } + int gestureId = eventLog.logId; + writer.println(prefix + "\tError messages for gesture ID: " + gestureId); + + boolean errorDetected = false; + // Use a Set since the order is inherently checked in the loop. + final Set<GestureEvent> encounteredEvents = new ArraySet<>(); + // Set flags and check order of operations. + for (ActiveGestureLog.EventEntry eventEntry : eventLog.eventEntries) { + GestureEvent gestureEvent = eventEntry.getGestureEvent(); + if (gestureEvent == null) { + continue; + } + encounteredEvents.add(gestureEvent); + switch (gestureEvent) { + case MOTION_UP: + errorDetected |= printErrorIfTrue( + !encounteredEvents.contains(GestureEvent.MOTION_DOWN), + prefix, + /* errorMessage= */ "Motion up detected before/without" + + " motion down.", + writer); + break; + case ON_SETTLED_ON_END_TARGET: + errorDetected |= printErrorIfTrue( + !encounteredEvents.contains(GestureEvent.SET_END_TARGET), + prefix, + /* errorMessage= */ "onSettledOnEndTarget called " + + "before/without setEndTarget.", + writer); + break; + case FINISH_RECENTS_ANIMATION: + errorDetected |= printErrorIfTrue( + !encounteredEvents.contains(GestureEvent.START_RECENTS_ANIMATION), + prefix, + /* errorMessage= */ "finishRecentsAnimation called " + + "before/without startRecentsAnimation.", + writer); + break; + case CANCEL_RECENTS_ANIMATION: + errorDetected |= printErrorIfTrue( + !encounteredEvents.contains(GestureEvent.START_RECENTS_ANIMATION), + prefix, + /* errorMessage= */ "cancelRecentsAnimation called " + + "before/without startRecentsAnimation.", + writer); + break; + case CLEANUP_SCREENSHOT: + errorDetected |= printErrorIfTrue( + !encounteredEvents.contains(GestureEvent.STATE_SCREENSHOT_CAPTURED), + prefix, + /* errorMessage= */ "recents activity screenshot was " + + "cleaned up before/without STATE_SCREENSHOT_CAPTURED " + + "being set.", + writer); + break; + case SCROLLER_ANIMATION_ABORTED: + errorDetected |= printErrorIfTrue( + encounteredEvents.contains(GestureEvent.SET_END_TARGET_HOME) + && !encounteredEvents.contains( + GestureEvent.ON_SETTLED_ON_END_TARGET), + prefix, + /* errorMessage= */ "recents view scroller animation " + + "aborted after setting end target HOME, but before" + + " settling on end target.", + writer); + break; + case TASK_APPEARED: + errorDetected |= printErrorIfTrue( + !encounteredEvents.contains(GestureEvent.SET_END_TARGET_NEW_TASK), + prefix, + /* errorMessage= */ "onTasksAppeared called " + + "before/without setting end target to new task", + writer); + errorDetected |= printErrorIfTrue( + !encounteredEvents.contains(GestureEvent.EXPECTING_TASK_APPEARED), + prefix, + /* errorMessage= */ "onTasksAppeared was not expected to be called", + writer); + break; + case EXPECTING_TASK_APPEARED: + errorDetected |= printErrorIfTrue( + !encounteredEvents.contains(GestureEvent.SET_END_TARGET_NEW_TASK), + prefix, + /* errorMessage= */ "expecting onTasksAppeared to be called " + + "before/without setting end target to new task", + writer); + break; + case STATE_GESTURE_COMPLETED: + errorDetected |= printErrorIfTrue( + !encounteredEvents.contains(GestureEvent.MOTION_UP), + prefix, + /* errorMessage= */ "STATE_GESTURE_COMPLETED set " + + "before/without motion up.", + writer); + errorDetected |= printErrorIfTrue( + !encounteredEvents.contains(GestureEvent.STATE_GESTURE_STARTED), + prefix, + /* errorMessage= */ "STATE_GESTURE_COMPLETED set " + + "before/without STATE_GESTURE_STARTED.", + writer); + break; + case STATE_GESTURE_CANCELLED: + errorDetected |= printErrorIfTrue( + !encounteredEvents.contains(GestureEvent.MOTION_UP), + prefix, + /* errorMessage= */ "STATE_GESTURE_CANCELLED set " + + "before/without motion up.", + writer); + errorDetected |= printErrorIfTrue( + !encounteredEvents.contains(GestureEvent.STATE_GESTURE_STARTED), + prefix, + /* errorMessage= */ "STATE_GESTURE_CANCELLED set " + + "before/without STATE_GESTURE_STARTED.", + writer); + break; + case STATE_SCREENSHOT_CAPTURED: + errorDetected |= printErrorIfTrue( + !encounteredEvents.contains(GestureEvent.STATE_CAPTURE_SCREENSHOT), + prefix, + /* errorMessage= */ "STATE_SCREENSHOT_CAPTURED set " + + "before/without STATE_CAPTURE_SCREENSHOT.", + writer); + break; + case STATE_RECENTS_SCROLLING_FINISHED: + errorDetected |= printErrorIfTrue( + !encounteredEvents.contains( + GestureEvent.SET_ON_PAGE_TRANSITION_END_CALLBACK), + prefix, + /* errorMessage= */ "STATE_RECENTS_SCROLLING_FINISHED " + + "set before/without calling " + + "setOnPageTransitionEndCallback.", + writer); + break; + case STATE_RECENTS_ANIMATION_CANCELED: + errorDetected |= printErrorIfTrue( + !encounteredEvents.contains( + GestureEvent.START_RECENTS_ANIMATION), + prefix, + /* errorMessage= */ "STATE_RECENTS_ANIMATION_CANCELED " + + "set before/without startRecentsAnimation.", + writer); + break; + case MOTION_DOWN: + case SET_END_TARGET: + case SET_END_TARGET_HOME: + case SET_END_TARGET_NEW_TASK: + case START_RECENTS_ANIMATION: + case SET_ON_PAGE_TRANSITION_END_CALLBACK: + case CANCEL_CURRENT_ANIMATION: + case FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER: + case STATE_GESTURE_STARTED: + case STATE_END_TARGET_ANIMATION_FINISHED: + case STATE_CAPTURE_SCREENSHOT: + case STATE_HANDLER_INVALIDATED: + case STATE_LAUNCHER_DRAWN: + default: + // No-Op + } + } + + // Check that all required events were found. + errorDetected |= printErrorIfTrue( + !encounteredEvents.contains(GestureEvent.MOTION_DOWN), + prefix, + /* errorMessage= */ "Motion down never detected.", + writer); + errorDetected |= printErrorIfTrue( + !encounteredEvents.contains(GestureEvent.MOTION_UP), + prefix, + /* errorMessage= */ "Motion up never detected.", + writer); + + errorDetected |= printErrorIfTrue( + /* condition= */ encounteredEvents.contains(GestureEvent.SET_END_TARGET) + && !encounteredEvents.contains(GestureEvent.ON_SETTLED_ON_END_TARGET), + prefix, + /* errorMessage= */ "setEndTarget was called, but " + + "onSettledOnEndTarget wasn't.", + writer); + errorDetected |= printErrorIfTrue( + /* condition= */ encounteredEvents.contains(GestureEvent.SET_END_TARGET) + && !encounteredEvents.contains( + GestureEvent.STATE_END_TARGET_ANIMATION_FINISHED), + prefix, + /* errorMessage= */ "setEndTarget was called, but " + + "STATE_END_TARGET_ANIMATION_FINISHED was never set.", + writer); + errorDetected |= printErrorIfTrue( + /* condition= */ encounteredEvents.contains(GestureEvent.SET_END_TARGET) + && !encounteredEvents.contains( + GestureEvent.STATE_RECENTS_SCROLLING_FINISHED), + prefix, + /* errorMessage= */ "setEndTarget was called, but " + + "STATE_RECENTS_SCROLLING_FINISHED was never set.", + writer); + errorDetected |= printErrorIfTrue( + /* condition= */ encounteredEvents.contains( + GestureEvent.STATE_END_TARGET_ANIMATION_FINISHED) + && encounteredEvents.contains( + GestureEvent.STATE_RECENTS_SCROLLING_FINISHED) + && !encounteredEvents.contains(GestureEvent.ON_SETTLED_ON_END_TARGET), + prefix, + /* errorMessage= */ "STATE_END_TARGET_ANIMATION_FINISHED and " + + "STATE_RECENTS_SCROLLING_FINISHED were set, but onSettledOnEndTarget " + + "wasn't called.", + writer); + + errorDetected |= printErrorIfTrue( + /* condition= */ encounteredEvents.contains( + GestureEvent.START_RECENTS_ANIMATION) + && !encounteredEvents.contains(GestureEvent.FINISH_RECENTS_ANIMATION) + && !encounteredEvents.contains(GestureEvent.CANCEL_RECENTS_ANIMATION), + prefix, + /* errorMessage= */ "startRecentsAnimation was called, but " + + "finishRecentsAnimation and cancelRecentsAnimation weren't.", + writer); + + errorDetected |= printErrorIfTrue( + /* condition= */ encounteredEvents.contains(GestureEvent.STATE_GESTURE_STARTED) + && !encounteredEvents.contains(GestureEvent.STATE_GESTURE_COMPLETED) + && !encounteredEvents.contains(GestureEvent.STATE_GESTURE_CANCELLED), + prefix, + /* errorMessage= */ "STATE_GESTURE_STARTED was set, but " + + "STATE_GESTURE_COMPLETED and STATE_GESTURE_CANCELLED weren't.", + writer); + + errorDetected |= printErrorIfTrue( + /* condition= */ encounteredEvents.contains( + GestureEvent.STATE_CAPTURE_SCREENSHOT) + && !encounteredEvents.contains(GestureEvent.STATE_SCREENSHOT_CAPTURED), + prefix, + /* errorMessage= */ "STATE_CAPTURE_SCREENSHOT was set, but " + + "STATE_SCREENSHOT_CAPTURED wasn't.", + writer); + + errorDetected |= printErrorIfTrue( + /* condition= */ encounteredEvents.contains( + GestureEvent.SET_ON_PAGE_TRANSITION_END_CALLBACK) + && !encounteredEvents.contains( + GestureEvent.STATE_RECENTS_SCROLLING_FINISHED), + prefix, + /* errorMessage= */ "setOnPageTransitionEndCallback called, but " + + "STATE_RECENTS_SCROLLING_FINISHED wasn't set.", + writer); + + errorDetected |= printErrorIfTrue( + /* condition= */ encounteredEvents.contains( + GestureEvent.FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER) + && !encounteredEvents.contains(GestureEvent.CANCEL_CURRENT_ANIMATION) + && !encounteredEvents.contains(GestureEvent.STATE_HANDLER_INVALIDATED), + prefix, + /* errorMessage= */ "AbsSwipeUpHandler.cancelCurrentAnimation " + + "wasn't called and STATE_HANDLER_INVALIDATED wasn't set.", + writer); + + errorDetected |= printErrorIfTrue( + /* condition= */ encounteredEvents.contains( + GestureEvent.STATE_RECENTS_ANIMATION_CANCELED) + && !encounteredEvents.contains(GestureEvent.CLEANUP_SCREENSHOT), + prefix, + /* errorMessage= */ "STATE_RECENTS_ANIMATION_CANCELED was set but " + + "the task screenshot wasn't cleaned up.", + writer); + + errorDetected |= printErrorIfTrue( + /* condition= */ encounteredEvents.contains( + GestureEvent.EXPECTING_TASK_APPEARED) + && !encounteredEvents.contains(GestureEvent.TASK_APPEARED), + prefix, + /* errorMessage= */ "onTaskAppeared was expected to be called but wasn't.", + writer); + + if (!errorDetected) { + writer.println(prefix + "\t\tNo errors detected."); + } + } + } + + private static boolean printErrorIfTrue( + boolean condition, String prefix, String errorMessage, PrintWriter writer) { + if (!condition) { + return false; + } + writer.println(prefix + "\t\t- " + errorMessage); + return true; + } +} diff --git a/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java b/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java index fabfc4bb51..23fdd58877 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 class ActiveGestureLog extends EventLogArray { */ 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 0000000000..b7b7825d78 --- /dev/null +++ b/quickstep/src/com/android/quickstep/util/AnimUtils.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.quickstep.util; + +/** + * Utility class containing methods to help manage animations, interpolators, and timings. + */ +public class AnimUtils { + /** + * Fetches device-specific timings for the Overview > Split animation + * (splitscreen initiated from Overview). + */ + public static SplitAnimationTimings getDeviceOverviewToSplitTimings(boolean isTablet) { + return isTablet + ? SplitAnimationTimings.TABLET_OVERVIEW_TO_SPLIT + : SplitAnimationTimings.PHONE_OVERVIEW_TO_SPLIT; + } + + /** + * Fetches device-specific timings for the Split > Confirm animation + * (splitscreen confirmed by selecting a second app). + */ + public static SplitAnimationTimings getDeviceSplitToConfirmTimings(boolean isTablet) { + return isTablet + ? SplitAnimationTimings.TABLET_SPLIT_TO_CONFIRM + : SplitAnimationTimings.PHONE_SPLIT_TO_CONFIRM; + } +} diff --git a/quickstep/src/com/android/quickstep/util/BaseDepthController.java b/quickstep/src/com/android/quickstep/util/BaseDepthController.java new file mode 100644 index 0000000000..877e28ab4e --- /dev/null +++ b/quickstep/src/com/android/quickstep/util/BaseDepthController.java @@ -0,0 +1,168 @@ +/* + * 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) { + mWallpaperManager.setWallpaperZoomOut(windowToken, depth); + } + + 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 143042fb31..2a513ee7ee 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 @@ import java.util.Map; 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 @@ public abstract class BaseUnfoldMoveFromCenterAnimator implements TransitionProg mMoveFromCenterAnimation.updateDisplayProperties(); onPrepareViewsForAnimation(); onTransitionProgress(0f); + mRotationChangeProvider.addCallback(mRotationListener); } @CallSuper @@ -62,6 +70,7 @@ public abstract class BaseUnfoldMoveFromCenterAnimator implements TransitionProg @Override public void onTransitionFinished() { mAnimationInProgress = false; + mRotationChangeProvider.removeCallback(mRotationListener); mMoveFromCenterAnimation.onTransitionFinished(); clearRegisteredViews(); } @@ -109,4 +118,14 @@ public abstract class BaseUnfoldMoveFromCenterAnimator implements TransitionProg 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 0000000000..433d23fa97 --- /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 e2563e398d..2be4f0a519 100644 --- a/quickstep/src/com/android/quickstep/util/GroupTask.java +++ b/quickstep/src/com/android/quickstep/util/GroupTask.java @@ -19,7 +19,8 @@ package com.android.quickstep.util; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.android.launcher3.util.SplitConfigurationOptions.StagedSplitBounds; +import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds; +import com.android.quickstep.views.TaskView; import com.android.systemui.shared.recents.model.Task; /** @@ -27,23 +28,25 @@ import com.android.systemui.shared.recents.model.Task; * are represented as an app-pair in the recents task list. */ public class GroupTask { - public @NonNull Task task1; - public @Nullable Task task2; - public @Nullable StagedSplitBounds mStagedSplitBounds; + @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 StagedSplitBounds stagedSplitBounds) { - task1 = t1; - task2 = t2; - mStagedSplitBounds = stagedSplitBounds; + public GroupTask(@NonNull Task t1, @Nullable Task t2, @Nullable SplitBounds splitBounds) { + this(t1, t2, splitBounds, t2 != null ? TaskView.Type.GROUPED : TaskView.Type.SINGLE); } - public GroupTask(@NonNull GroupTask group) { - task1 = new Task(group.task1); - task2 = group.task2 != null - ? new Task(group.task2) - : null; - mStagedSplitBounds = group.mStagedSplitBounds; + protected GroupTask(@NonNull Task t1, @Nullable Task t2, @Nullable SplitBounds splitBounds, + @TaskView.Type int taskViewType) { + task1 = t1; + task2 = t2; + mSplitBounds = splitBounds; + this.taskViewType = taskViewType; } public boolean containsTask(int taskId) { @@ -53,4 +56,14 @@ public class GroupTask { 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 63d5b0dd50..9fe24dec66 100644 --- a/quickstep/src/com/android/quickstep/util/ImageActionUtils.java +++ b/quickstep/src/com/android/quickstep/util/ImageActionUtils.java @@ -43,7 +43,6 @@ import android.net.Uri; import android.util.Log; import android.view.View; -import androidx.annotation.UiThread; import androidx.annotation.WorkerThread; import androidx.core.content.FileProvider; @@ -86,67 +85,70 @@ public class ImageActionUtils { * Launch the activity to share image for overview sharing. This is to share cropped bitmap * with specific share targets (with shortcutInfo and appTarget) rendered in overview. */ - @UiThread public static void shareImage(Context context, Supplier<Bitmap> bitmapSupplier, RectF rectF, ShortcutInfo shortcutInfo, AppTarget appTarget, String tag) { - if (bitmapSupplier.get() == null) { - return; - } - Rect crop = new Rect(); - rectF.round(crop); - Intent intent = new Intent(); - Uri uri = getImageUri(bitmapSupplier.get(), crop, context, tag); - ClipData clipdata = new ClipData(new ClipDescription("content", - new String[]{"image/png"}), - new ClipData.Item(uri)); - intent.setAction(Intent.ACTION_SEND) - .setComponent(new ComponentName(appTarget.getPackageName(), appTarget.getClassName())) - .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - .addFlags(FLAG_GRANT_READ_URI_PERMISSION) - .setType("image/png") - .putExtra(Intent.EXTRA_STREAM, uri) - .putExtra(Intent.EXTRA_SHORTCUT_ID, shortcutInfo.getId()) - .setClipData(clipdata); - - if (context.getUserId() != appTarget.getUser().getIdentifier()) { - intent.prepareToLeaveUser(context.getUserId()); - intent.fixUris(context.getUserId()); - context.startActivityAsUser(intent, appTarget.getUser()); - } else { - context.startActivity(intent); - } + UI_HELPER_EXECUTOR.execute(() -> { + Bitmap bitmap = bitmapSupplier.get(); + if (bitmap == null) { + return; + } + Rect crop = new Rect(); + rectF.round(crop); + Intent intent = new Intent(); + Uri uri = getImageUri(bitmap, crop, context, tag); + ClipData clipdata = new ClipData(new ClipDescription("content", + new String[]{"image/png"}), + new ClipData.Item(uri)); + intent.setAction(Intent.ACTION_SEND) + .setComponent( + new ComponentName(appTarget.getPackageName(), appTarget.getClassName())) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + .addFlags(FLAG_GRANT_READ_URI_PERMISSION) + .setType("image/png") + .putExtra(Intent.EXTRA_STREAM, uri) + .putExtra(Intent.EXTRA_SHORTCUT_ID, shortcutInfo.getId()) + .setClipData(clipdata); + + if (context.getUserId() != appTarget.getUser().getIdentifier()) { + intent.prepareToLeaveUser(context.getUserId()); + intent.fixUris(context.getUserId()); + context.startActivityAsUser(intent, appTarget.getUser()); + } else { + context.startActivity(intent); + } + }); } /** * Launch the activity to share image. */ - @UiThread public static void startShareActivity(Context context, Supplier<Bitmap> bitmapSupplier, Rect crop, Intent intent, String tag) { - if (bitmapSupplier.get() == null) { - Log.e(tag, "No snapshot available, not starting share."); - return; - } - - UI_HELPER_EXECUTOR.execute(() -> persistBitmapAndStartActivity(context, - bitmapSupplier.get(), crop, intent, ImageActionUtils::getShareIntentForImageUri, - tag)); + UI_HELPER_EXECUTOR.execute(() -> { + Bitmap bitmap = bitmapSupplier.get(); + if (bitmap == null) { + Log.e(tag, "No snapshot available, not starting share."); + return; + } + persistBitmapAndStartActivity(context, bitmap, crop, intent, + ImageActionUtils::getShareIntentForImageUri, tag); + }); } /** * Launch the activity to share image with shared element transition. */ - @UiThread public static void startShareActivity(Context context, Supplier<Bitmap> bitmapSupplier, Rect crop, Intent intent, String tag, View sharedElement) { - if (bitmapSupplier.get() == null) { - Log.e(tag, "No snapshot available, not starting share."); - return; - } - - UI_HELPER_EXECUTOR.execute(() -> persistBitmapAndStartActivity(context, - bitmapSupplier.get(), crop, intent, ImageActionUtils::getShareIntentForImageUri, - tag, sharedElement)); + UI_HELPER_EXECUTOR.execute(() -> { + Bitmap bitmap = bitmapSupplier.get(); + if (bitmap == null) { + Log.e(tag, "No snapshot available, not starting share."); + return; + } + persistBitmapAndStartActivity(context, bitmap, + crop, intent, ImageActionUtils::getShareIntentForImageUri, tag, sharedElement); + }); } /** diff --git a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java index 97be4370b9..170c622035 100644 --- a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java +++ b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java @@ -18,13 +18,11 @@ package com.android.quickstep.util; 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.Workspace; 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 class LauncherUnfoldAnimationController { 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 @@ public class LauncherUnfoldAnimationController { @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 d0856bed20..f7136a5b68 100644 --- a/quickstep/src/com/android/quickstep/util/LayoutUtils.java +++ b/quickstep/src/com/android/quickstep/util/LayoutUtils.java @@ -23,7 +23,7 @@ import android.view.ViewGroup; 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 0000000000..bad8506eec --- /dev/null +++ b/quickstep/src/com/android/quickstep/util/LogUtils.kt @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.quickstep.util + +import android.util.Pair +import com.android.internal.logging.InstanceIdSequence +import com.android.launcher3.logging.InstanceId + +object LogUtils { + /** + * @return a [Pair] of two InstanceIds but with different types, one that can be used by framework + * (if needing to pass through an intent or such) and one used in Launcher + */ + @JvmStatic + fun getShellShareableInstanceId(): + Pair<com.android.internal.logging.InstanceId, InstanceId> { + val internalInstanceId = InstanceIdSequence(InstanceId.INSTANCE_ID_MAX).newInstanceId() + val launcherInstanceId = InstanceId(internalInstanceId.id) + return Pair(internalInstanceId, launcherInstanceId) + } +} diff --git a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java index b83e26e5de..69ed2f8028 100644 --- a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java +++ b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java @@ -17,13 +17,14 @@ package com.android.quickstep.util; import android.content.Context; import android.content.res.Resources; +import android.util.Log; import android.view.MotionEvent; import android.view.VelocityTracker; import com.android.launcher3.Alarm; import com.android.launcher3.R; +import com.android.launcher3.Utilities; import com.android.launcher3.compat.AccessibilityManagerCompat; -import com.android.launcher3.testing.TestProtocol; /** * Given positions along x- or y-axis, tracks velocity and acceleration and determines when there is @@ -31,6 +32,8 @@ import com.android.launcher3.testing.TestProtocol; */ public class MotionPauseDetector { + private static final String TAG = "MotionPauseDetector"; + // The percentage of the previous speed that determines whether this is a rapid deceleration. // The bigger this number, the easier it is to trigger the first pause. private static final float RAPID_DECELERATION_FACTOR = 0.6f; @@ -43,6 +46,12 @@ public class MotionPauseDetector { */ private static final long HARDER_TRIGGER_TIMEOUT = 400; + /** + * When running in a test harness, if no motion is added for this amount of time, assume the + * motion has paused. (We use an increased timeout since sometimes test devices can be slow.) + */ + private static final long TEST_HARNESS_TRIGGER_TIMEOUT = 2000; + private final float mSpeedVerySlow; private final float mSpeedSlow; private final float mSpeedSomewhatFast; @@ -85,7 +94,8 @@ public class MotionPauseDetector { mSpeedSomewhatFast = res.getDimension(R.dimen.motion_pause_detector_speed_somewhat_fast); mSpeedFast = res.getDimension(R.dimen.motion_pause_detector_speed_fast); mForcePauseTimeout = new Alarm(); - mForcePauseTimeout.setOnAlarmListener(alarm -> updatePaused(true /* isPaused */)); + mForcePauseTimeout.setOnAlarmListener(alarm -> updatePaused(true /* isPaused */, + "Force pause timeout after " + alarm.getLastSetTimeout() + "ms" /* reason */)); mMakePauseHarderToTrigger = makePauseHarderToTrigger; mVelocityProvider = new SystemVelocityProvider(axis); } @@ -102,7 +112,7 @@ public class MotionPauseDetector { */ public void setDisallowPause(boolean disallowPause) { mDisallowPause = disallowPause; - updatePaused(mIsPaused); + updatePaused(mIsPaused, "Set disallowPause=" + disallowPause); } /** @@ -119,9 +129,11 @@ public class MotionPauseDetector { * @param pointerIndex Index for the pointer being tracked in the motion event */ public void addPosition(MotionEvent ev, int pointerIndex) { - long timeoutMs = TestProtocol.sForcePauseTimeout != null - ? TestProtocol.sForcePauseTimeout - : mMakePauseHarderToTrigger ? HARDER_TRIGGER_TIMEOUT : FORCE_PAUSE_TIMEOUT; + long timeoutMs = Utilities.IS_RUNNING_IN_TEST_HARNESS + ? TEST_HARNESS_TRIGGER_TIMEOUT + : mMakePauseHarderToTrigger + ? HARDER_TRIGGER_TIMEOUT + : FORCE_PAUSE_TIMEOUT; mForcePauseTimeout.setAlarm(timeoutMs); float newVelocity = mVelocityProvider.addMotionEvent(ev, ev.getPointerId(pointerIndex)); if (mPreviousVelocity != null) { @@ -134,21 +146,27 @@ public class MotionPauseDetector { float speed = Math.abs(velocity); float previousSpeed = Math.abs(prevVelocity); boolean isPaused; + String isPausedReason = ""; if (mIsPaused) { // Continue to be paused until moving at a fast speed. isPaused = speed < mSpeedFast || previousSpeed < mSpeedFast; + isPausedReason = "Was paused, but started moving at a fast speed"; } else { if (velocity < 0 != prevVelocity < 0) { // We're just changing directions, not necessarily stopping. isPaused = false; + isPausedReason = "Velocity changed directions"; } else { isPaused = speed < mSpeedVerySlow && previousSpeed < mSpeedVerySlow; + isPausedReason = "Pause requires back to back slow speeds"; if (!isPaused && !mHasEverBeenPaused) { // We want to be more aggressive about detecting the first pause to ensure it // feels as responsive as possible; getting two very slow speeds back to back // takes too long, so also check for a rapid deceleration. boolean isRapidDeceleration = speed < previousSpeed * RAPID_DECELERATION_FACTOR; isPaused = isRapidDeceleration && speed < mSpeedSomewhatFast; + isPausedReason = "Didn't have back to back slow speeds, checking for rapid" + + " deceleration on first pause only"; } if (mMakePauseHarderToTrigger) { if (speed < mSpeedSlow) { @@ -156,22 +174,31 @@ public class MotionPauseDetector { mSlowStartTime = time; } isPaused = time - mSlowStartTime >= HARDER_TRIGGER_TIMEOUT; + isPausedReason = "Maintained slow speed for sufficient duration when making" + + " pause harder to trigger"; } else { mSlowStartTime = 0; isPaused = false; + isPausedReason = "Intentionally making pause harder to trigger"; } } } } - updatePaused(isPaused); + updatePaused(isPaused, isPausedReason); } - private void updatePaused(boolean isPaused) { + private void updatePaused(boolean isPaused, String reason) { if (mDisallowPause) { + reason = "Disallow pause; otherwise, would have been " + isPaused + " due to " + reason; isPaused = false; } if (mIsPaused != isPaused) { mIsPaused = isPaused; + 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 527a6d2d9a..59c82633d2 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 0000000000..e189a66ee4 --- /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 0000000000..f1dde53d12 --- /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 0000000000..3d9e09ee81 --- /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 3777c659e3..8f79ccf450 100644 --- a/quickstep/src/com/android/quickstep/util/ProxyScreenStatusProvider.java +++ b/quickstep/src/com/android/quickstep/util/ProxyScreenStatusProvider.java @@ -39,6 +39,16 @@ public class ProxyScreenStatusProvider implements ScreenStatusProvider { 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 fb32581508..e928b27751 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.ALL_APPS; import static com.android.launcher3.LauncherState.HINT_STATE; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.OVERVIEW; -import static com.android.launcher3.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.LauncherState; 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 @@ public class QuickstepOnboardingPrefs extends OnboardingPrefs<QuickstepLauncher> }); } - 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 edaa32637a..1d008da5b3 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.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.StateManager.AtomicAnimationFactory; 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 @@ public class RecentsAtomicAnimationFactory<ACTIVITY_TYPE extends StatefulActivit 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 6038a22de8..db8c7f23b9 100644 --- a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java +++ b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java @@ -45,15 +45,14 @@ import androidx.annotation.NonNull; import com.android.launcher3.DeviceProfile; import com.android.launcher3.InvariantDeviceProfile; -import com.android.launcher3.Utilities; -import com.android.launcher3.testing.TestProtocol; +import com.android.launcher3.LauncherPrefs; +import com.android.launcher3.testing.shared.TestProtocol; import com.android.launcher3.touch.PagedOrientationHandler; import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.SettingsCache; import com.android.quickstep.BaseActivityInterface; import com.android.quickstep.SystemUiProxy; import com.android.quickstep.TaskAnimationManager; -import com.android.quickstep.views.TaskView; import java.lang.annotation.Retention; import java.util.function.IntConsumer; @@ -140,7 +139,7 @@ public class RecentsOrientedState implements 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) { @@ -221,8 +220,7 @@ public class RecentsOrientedState implements private boolean updateHandler() { mRecentsActivityRotation = inferRecentsActivityRotation(mDisplayRotation); - if (mRecentsActivityRotation == mTouchRotation || (isRecentsActivityRotationAllowed() - && (mFlags & FLAG_SWIPE_UP_NOT_RUNNING) != 0)) { + if (mRecentsActivityRotation == mTouchRotation || isRecentsActivityRotationAllowed()) { mOrientationHandler = PagedOrientationHandler.PORTRAIT; } else if (mTouchRotation == ROTATION_90) { mOrientationHandler = PagedOrientationHandler.LANDSCAPE; @@ -400,37 +398,10 @@ public class RecentsOrientedState implements * Returns the scale and pivot so that the provided taskRect can fit the provided full size */ public float getFullScreenScaleAndPivot(Rect taskView, DeviceProfile dp, PointF outPivot) { - Rect insets = dp.getInsets(); - float fullWidth = dp.widthPx; - float fullHeight = dp.heightPx; - if (TaskView.clipLeft(dp)) { - fullWidth -= insets.left; - } - if (TaskView.clipRight(dp)) { - fullWidth -= insets.right; - } - if (TaskView.clipTop(dp)) { - fullHeight -= insets.top; - } - if (TaskView.clipBottom(dp)) { - fullHeight -= insets.bottom; - } - - getTaskDimension(mContext, dp, outPivot); + getTaskDimension(dp, outPivot); float scale = Math.min(outPivot.x / taskView.width(), outPivot.y / taskView.height()); - // We also scale the preview as part of fullScreenParams, so account for that as well. - if (fullWidth > 0) { - scale = scale * dp.widthPx / fullWidth; - } - if (scale == 1) { - outPivot.set(fullWidth / 2, fullHeight / 2); - } else if (dp.isMultiWindowMode) { - float denominator = 1 / (scale - 1); - // Ensure that the task aligns to right bottom for the root view - float y = (scale * taskView.bottom - fullHeight) * denominator; - float x = (scale * taskView.right - fullWidth) * denominator; - outPivot.set(x, y); + outPivot.set(taskView.centerX(), taskView.centerY()); } else { float factor = scale / (scale - 1); outPivot.set(taskView.left * factor, taskView.top * factor); 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 0000000000..a2f48ddc9b --- /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 c4909de510..68739ba458 100644 --- a/quickstep/src/com/android/quickstep/util/RectFSpringAnim.java +++ b/quickstep/src/com/android/quickstep/util/RectFSpringAnim.java @@ -214,7 +214,7 @@ public class RectFSpringAnim extends ReleaseCheck { * @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 @@ public class RectFSpringAnim extends ReleaseCheck { 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 ee82ae67df..10f2eaa2f0 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 81c124f7e2..382cf79e1e 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 @@ public class RemoteFadeOutAnimationListener implements AnimatorUpdateListener { 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 0000000000..7dc1b32858 --- /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 483a1c652a..0000000000 --- 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 250235925e..681f068e52 100644 --- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java +++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java @@ -22,39 +22,51 @@ import static android.app.PendingIntent.FLAG_MUTABLE; 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; +import com.android.launcher3.testing.shared.TestProtocol; import com.android.launcher3.util.SplitConfigurationOptions; import com.android.launcher3.util.SplitConfigurationOptions.StagePosition; 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; @@ -63,44 +75,82 @@ import java.util.function.Consumer; * 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); + } + + /** + * 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); } - public void setInitialTaskSelect(Intent intent, @StagePosition int stagePosition) { - mInitialTaskIntent = intent; + private void setInitialData(@StagePosition int stagePosition, + StatsLogManager.EventEnum splitEvent, ItemInfo itemInfo) { + mItemInfo = itemInfo; mStagePosition = stagePosition; - mInitialTaskId = INVALID_TASK_ID; + mSplitEvent = splitEvent; } /** @@ -108,40 +158,35 @@ public class SplitSelectStateController { * 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) * @param task The second task that will be launched. */ 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(); @@ -157,127 +202,224 @@ public class SplitSelectStateController { */ 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) { - // 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}; + 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"); + 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); @@ -287,7 +429,7 @@ public class SplitSelectStateController { } @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 @@ -306,9 +448,12 @@ public class SplitSelectStateController { 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; } /** @@ -316,7 +461,7 @@ public class SplitSelectStateController { * chosen */ public boolean isSplitSelectActive() { - return isInitialTaskIntentSet() && mSecondTaskId == INVALID_TASK_ID; + return isInitialTaskIntentSet() && !isSecondTaskIntentSet(); } /** @@ -324,10 +469,26 @@ public class SplitSelectStateController { * be launched */ public boolean isBothSplitAppsConfirmed() { - return isInitialTaskIntentSet() && mSecondTaskId != INVALID_TASK_ID; + return isInitialTaskIntentSet() && isSecondTaskIntentSet(); } private boolean isInitialTaskIntentSet() { return (mInitialTaskId != INVALID_TASK_ID || mInitialTaskIntent != null); } + + 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 0000000000..f5b00cf42b --- /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 0000000000..e5c74dc2e3 --- /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 0000000000..24d832640f --- /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 32e08ffa8d..ad54a709d7 100644 --- a/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java +++ b/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java @@ -35,19 +35,21 @@ import android.view.ViewGroup; import androidx.annotation.Nullable; -import com.android.launcher3.BaseQuickstepLauncher; import com.android.launcher3.CellLayout; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Hotseat; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; +import com.android.launcher3.QuickstepTransitionManager; import com.android.launcher3.R; import com.android.launcher3.ShortcutAndWidgetContainer; 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; @@ -60,10 +62,10 @@ import com.android.systemui.plugins.ResourceProvider; public class StaggeredWorkspaceAnim { private static final int APP_CLOSE_ROW_START_DELAY_MS = 10; - // How long it takes to fade in each staggered row. - private static final int ALPHA_DURATION_MS = 250; // Should be used for animations running alongside this StaggeredWorkspaceAnim. public static final int DURATION_MS = 250; + public static final int DURATION_TASKBAR_MS = + QuickstepTransitionManager.TASKBAR_TO_HOME_DURATION; private static final float MAX_VELOCITY_PX_PER_S = 22f; @@ -91,16 +93,20 @@ public class StaggeredWorkspaceAnim { mSpringTransY = transFactor * launcher.getResources() .getDimensionPixelSize(R.dimen.swipe_up_max_workspace_trans_y); + DeviceProfile grid = launcher.getDeviceProfile(); + long duration = grid.isTaskbarPresent ? DURATION_TASKBAR_MS : DURATION_MS; if (staggerWorkspace) { - DeviceProfile grid = launcher.getDeviceProfile(); Workspace<?> workspace = launcher.getWorkspace(); Hotseat hotseat = launcher.getHotseat(); - // Hotseat and QSB takes up two additional rows. - int totalRows = grid.inv.numRows + (grid.isVerticalBarLayout() ? 0 : 2); + boolean staggerHotseat = !grid.isVerticalBarLayout() && !grid.isTaskbarPresent; + boolean staggerQsb = + !grid.isVerticalBarLayout() && !(grid.isTaskbarPresent && grid.isQsbInline); + int totalRows = grid.inv.numRows + (staggerHotseat ? 1 : 0) + (staggerQsb ? 1 : 0); // Add animation for all the visible workspace pages - workspace.forEachVisiblePage(page -> addAnimationForPage((CellLayout) page, totalRows)); + workspace.forEachVisiblePage( + page -> addAnimationForPage((CellLayout) page, totalRows, duration)); boolean workspaceClipChildren = workspace.getClipChildren(); boolean workspaceClipToPadding = workspace.getClipToPadding(); @@ -117,25 +123,35 @@ public class StaggeredWorkspaceAnim { 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); + CellLayoutLayoutParams lp = ((CellLayoutLayoutParams) child.getLayoutParams()); + addStaggeredAnimationForView(child, lp.cellY + 1, totalRows, duration); } } else { final int hotseatRow, qsbRow; if (grid.isTaskbarPresent) { - qsbRow = grid.inv.numRows + 1; - hotseatRow = grid.inv.numRows + 2; + if (grid.isQsbInline) { + qsbRow = grid.inv.numRows + 1; + hotseatRow = grid.inv.numRows + 1; + } else { + qsbRow = grid.inv.numRows + 1; + hotseatRow = grid.inv.numRows + 2; + } } else { hotseatRow = grid.inv.numRows + 1; qsbRow = grid.inv.numRows + 2; } - for (int i = hotseatIcons.getChildCount() - 1; i >= 0; i--) { - View child = hotseatIcons.getChildAt(i); - addStaggeredAnimationForView(child, hotseatRow, totalRows); - } - addStaggeredAnimationForView(hotseat.getQsb(), qsbRow, totalRows); + // Do not stagger hotseat as a whole when taskbar is present, and stagger QSB only + // if it's not inline. + if (staggerHotseat) { + for (int i = hotseatIcons.getChildCount() - 1; i >= 0; i--) { + View child = hotseatIcons.getChildAt(i); + addStaggeredAnimationForView(child, hotseatRow, totalRows, duration); + } + } + if (staggerQsb) { + addStaggeredAnimationForView(hotseat.getQsb(), qsbRow, totalRows, duration); + } } mAnimators.addListener(new AnimatorListenerAdapter() { @@ -153,19 +169,19 @@ public class StaggeredWorkspaceAnim { mAnimators.addListener(forEndCallback(launcher::resumeExpensiveViewUpdates)); if (animateOverviewScrim) { - PendingAnimation pendingAnimation = new PendingAnimation(DURATION_MS); + PendingAnimation pendingAnimation = new PendingAnimation(duration); launcher.getWorkspace().getStateTransitionAnimation() .setScrim(pendingAnimation, NORMAL, new StateAnimationConfig()); mAnimators.play(pendingAnimation.buildAnim()); } - addDepthAnimationForState(launcher, NORMAL, DURATION_MS); + addDepthAnimationForState(launcher, NORMAL, duration); mAnimators.play(launcher.getRootView().getSysUiScrim().createSysuiMultiplierAnim(0f, 1f) - .setDuration(DURATION_MS)); + .setDuration(duration)); } - private void addAnimationForPage(CellLayout page, int totalRows) { + private void addAnimationForPage(CellLayout page, int totalRows, long duration) { ShortcutAndWidgetContainer itemsContainer = page.getShortcutsAndWidgets(); boolean pageClipChildren = page.getClipChildren(); @@ -177,8 +193,8 @@ public class StaggeredWorkspaceAnim { // 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); + CellLayoutLayoutParams lp = ((CellLayoutLayoutParams) child.getLayoutParams()); + addStaggeredAnimationForView(child, lp.cellY + lp.cellVSpan, totalRows, duration); } mAnimators.addListener(new AnimatorListenerAdapter() { @@ -231,8 +247,9 @@ public class StaggeredWorkspaceAnim { * @param v A view on the workspace. * @param row The bottom-most row that contains the view. * @param totalRows Total number of rows. + * @param duration duration of the animation */ - private void addStaggeredAnimationForView(View v, int row, int totalRows) { + private void addStaggeredAnimationForView(View v, int row, int totalRows, long duration) { if (mIgnoredView != null && mIgnoredView == v) return; // Invert the rows, because we stagger starting from the bottom of the screen. int invertedRow = totalRows - row; @@ -266,7 +283,7 @@ public class StaggeredWorkspaceAnim { v.setAlpha(0); ObjectAnimator alpha = ObjectAnimator.ofFloat(v, View.ALPHA, 0f, 1f); alpha.setInterpolator(LINEAR); - alpha.setDuration(ALPHA_DURATION_MS); + alpha.setDuration(duration); alpha.setStartDelay(startDelay); alpha.addListener(new AnimatorListenerAdapter() { @Override @@ -278,11 +295,11 @@ public class StaggeredWorkspaceAnim { } 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 0000000000..7ab285dfa7 --- /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 1200208e30..95473dc39a 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.View; 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 @@ public class SurfaceTransactionApplier extends ReleaseCheck { * @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 @@ public class SurfaceTransactionApplier extends ReleaseCheck { } /** - * 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/SwipePipToHomeAnimator.java b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java index b222f51e50..74e4acc421 100644 --- a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java +++ b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java @@ -253,7 +253,7 @@ public class SwipePipToHomeAnimator extends RectFSpringAnim { rotatedPosition.degree, rotatedPosition.positionX, rotatedPosition.positionY); } else { return mSurfaceTransactionHelper.scaleAndCrop(tx, mLeash, mSourceRectHint, mAppBounds, - bounds, insets); + bounds, insets, progress); } } @@ -279,7 +279,10 @@ public class SwipePipToHomeAnimator extends RectFSpringAnim { // get the final leash operations but do not apply to the leash. final SurfaceControl.Transaction tx = PipSurfaceTransactionHelper.newSurfaceControlTransaction(); - return onAnimationUpdate(tx, new RectF(mDestinationBounds), END_PROGRESS); + final PictureInPictureSurfaceTransaction pipTx = + onAnimationUpdate(tx, new RectF(mDestinationBounds), END_PROGRESS); + pipTx.setShouldDisableCanAffectSystemUiFlags(true); + return pipTx; } private RotatedPosition getRotatedPosition(float progress) { diff --git a/quickstep/src/com/android/quickstep/util/SystemWindowManagerProxy.java b/quickstep/src/com/android/quickstep/util/SystemWindowManagerProxy.java index 9bb3d56556..5dc461363b 100644 --- a/quickstep/src/com/android/quickstep/util/SystemWindowManagerProxy.java +++ b/quickstep/src/com/android/quickstep/util/SystemWindowManagerProxy.java @@ -15,12 +15,20 @@ */ package com.android.quickstep.util; +import static android.view.Display.DEFAULT_DISPLAY; + import android.content.Context; -import android.hardware.display.DisplayManager; -import android.view.Display; +import android.util.ArrayMap; +import android.view.Surface; +import android.view.WindowManager; +import android.view.WindowMetrics; +import com.android.launcher3.util.WindowBounds; +import com.android.launcher3.util.window.CachedDisplayInfo; import com.android.launcher3.util.window.WindowManagerProxy; +import java.util.Set; + /** * Extension of {@link WindowManagerProxy} with some assumption for the default system Launcher */ @@ -31,23 +39,23 @@ public class SystemWindowManagerProxy extends WindowManagerProxy { } @Override - protected String getDisplayId(Display display) { - return display.getUniqueId(); - } - - @Override - public boolean isInternalDisplay(Display display) { - return display.getType() == Display.TYPE_INTERNAL; - } - - @Override - public int getRotation(Context context) { - return context.getResources().getConfiguration().windowConfiguration.getRotation(); + public int getRotation(Context displayInfoContext) { + return displayInfoContext.getResources().getConfiguration().windowConfiguration + .getRotation(); } @Override - protected Display[] getDisplays(Context context) { - return context.getSystemService(DisplayManager.class).getDisplays( - DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED); + public ArrayMap<CachedDisplayInfo, WindowBounds[]> estimateInternalDisplayBounds( + Context displayInfoContext) { + ArrayMap<CachedDisplayInfo, WindowBounds[]> result = new ArrayMap<>(); + WindowManager windowManager = displayInfoContext.getSystemService(WindowManager.class); + Set<WindowMetrics> possibleMaximumWindowMetrics = + windowManager.getPossibleMaximumWindowMetrics(DEFAULT_DISPLAY); + for (WindowMetrics windowMetrics : possibleMaximumWindowMetrics) { + CachedDisplayInfo info = getDisplayInfo(windowMetrics, Surface.ROTATION_0); + WindowBounds[] bounds = estimateWindowBounds(displayInfoContext, info); + result.put(info, bounds); + } + return result; } } 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 0000000000..bf8612a797 --- /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 0000000000..cbf46bfcb8 --- /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 0000000000..3756b4af59 --- /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 5212755ec7..04af19f729 100644 --- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java +++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java @@ -15,16 +15,17 @@ */ 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; 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.StagePosition; +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; @@ -34,22 +35,22 @@ import android.graphics.PointF; 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.StagedSplitBounds; +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 @@ -100,8 +101,8 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { // Cached calculations private boolean mLayoutValid = false; private int mOrientationStateId; - private StagedSplitBounds mStagedSplitBounds; - private boolean mDrawsBelowRecents; + private SplitBounds mSplitBounds; + private Boolean mDrawsBelowRecents = null; private boolean mIsGridTask; private int mTaskRectTranslationX; private int mTaskRectTranslationY; @@ -152,13 +153,13 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { } Rect fullTaskSize; - if (mStagedSplitBounds != null) { + if (mSplitBounds != null) { // The task rect changes according to the staged split task sizes, but recents // fullscreen scale and pivot remains the same since the task fits into the existing // sized task space bounds fullTaskSize = new Rect(mTaskRect); mOrientationState.getOrientationHandler() - .setSplitTaskSwipeRect(mDp, mTaskRect, mStagedSplitBounds, mStagePosition); + .setSplitTaskSwipeRect(mDp, mTaskRect, mSplitBounds, mStagePosition); mTaskRect.offset(mTaskRectTranslationX, mTaskRectTranslationY); } else { fullTaskSize = mTaskRect; @@ -170,8 +171,11 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { /** * 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); } /** @@ -180,16 +184,17 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { * * @param splitInfo set to {@code null} when not in staged split mode */ - public void setPreview(RemoteAnimationTargetCompat runningTarget, StagedSplitBounds splitInfo) { + public void setPreview(RemoteAnimationTarget runningTarget, SplitBounds splitInfo) { setPreview(runningTarget); - mStagedSplitBounds = splitInfo; - if (mStagedSplitBounds == null) { + mSplitBounds = splitInfo; + if (mSplitBounds == null) { mStagePosition = STAGE_POSITION_UNDEFINED; return; } mStagePosition = mThumbnailPosition.equals(splitInfo.leftTopBounds) ? STAGE_POSITION_TOP_OR_LEFT : STAGE_POSITION_BOTTOM_OR_RIGHT; + mPositionHelper.setSplitBounds(convertSplitBounds(mSplitBounds), mStagePosition); } /** @@ -316,9 +321,9 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { // 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); @@ -385,15 +390,21 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { @Override public void onBuildTargetParams( - Builder builder, RemoteAnimationTargetCompat app, TransformParams params) { - builder.withMatrix(mMatrix) - .withWindowCrop(mTmpCropRect) - .withCornerRadius(getCurrentCornerRadius()); - - if (ENABLE_QUICKSTEP_LIVE_TILE.get() && params.getRecentsSurface() != null) { - // When relativeLayer = 0, it reverts the surfaces back to the original order. - builder.withRelativeLayerTo(params.getRecentsSurface(), - mDrawsBelowRecents ? Integer.MIN_VALUE : 0); + SurfaceProperties builder, RemoteAnimationTarget app, TransformParams params) { + builder.setMatrix(mMatrix) + .setWindowCrop(mTmpCropRect) + .setCornerRadius(getCurrentCornerRadius()); + + // 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.setLayer(mDrawsBelowRecents + ? Integer.MIN_VALUE + 1 + : ENABLE_SHELL_TRANSITIONS ? Integer.MAX_VALUE : 0); } } @@ -411,4 +422,15 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { 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/TaskVisualsChangeListener.java b/quickstep/src/com/android/quickstep/util/TaskVisualsChangeListener.java new file mode 100644 index 0000000000..66bff730bf --- /dev/null +++ b/quickstep/src/com/android/quickstep/util/TaskVisualsChangeListener.java @@ -0,0 +1,45 @@ +/* + * 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.UserHandle; + +import com.android.systemui.shared.recents.model.Task; +import com.android.systemui.shared.recents.model.ThumbnailData; + +/** + * Listener for receiving various task properties changes + */ +public interface TaskVisualsChangeListener { + + /** + * Called when the task thumbnail changes + */ + default Task onTaskThumbnailChanged(int taskId, ThumbnailData thumbnailData) { + return null; + } + + /** + * Called when the icon for a task changes + */ + default void onTaskIconChanged(String pkg, UserHandle user) {} + + /** + * Called when the icon for a task changes + */ + default void onTaskIconChanged(int taskId) {} +} diff --git a/quickstep/src/com/android/quickstep/util/TransformParams.java b/quickstep/src/com/android/quickstep/util/TransformParams.java index 75d6001afd..aa9a45bd8b 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 @@ public class TransformParams { * 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,26 +138,26 @@ public class TransformParams { return this; } - public SurfaceParams[] createSurfaceParams(BuilderProxy proxy) { + public SurfaceTransaction createSurfaceParams(BuilderProxy proxy) { RemoteAnimationTargets targets = mTargetSet; - SurfaceParams[] surfaceParams = new SurfaceParams[targets.unfilteredApps.length]; + SurfaceTransaction transaction = new SurfaceTransaction(); mRecentsSurface = getRecentsSurface(targets); for (int i = 0; i < targets.unfilteredApps.length; i++) { - RemoteAnimationTargetCompat app = targets.unfilteredApps[i]; - SurfaceParams.Builder builder = new SurfaceParams.Builder(app.leash); + 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); @@ -164,16 +165,22 @@ public class TransformParams { } else { mBaseBuilderProxy.onBuildTargetParams(builder, app, this); } - surfaceParams[i] = builder.build(); } - return surfaceParams; + + // always put wallpaper layer to bottom. + final int wallpaperLength = targets.wallpapers != null ? targets.wallpapers.length : 0; + for (int i = 0; i < wallpaperLength; i++) { + RemoteAnimationTarget wallpaper = targets.wallpapers[i]; + transaction.forSurface(wallpaper.leash).setLayer(Integer.MIN_VALUE); + } + 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 { @@ -205,15 +212,11 @@ public class TransformParams { 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(); } } @@ -221,9 +224,9 @@ public class TransformParams { 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 dc97dd6f86..01a997a451 100644 --- a/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterHotseatAnimator.java +++ b/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterHotseatAnimator.java @@ -21,6 +21,7 @@ import android.view.WindowManager; 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 @@ public class UnfoldMoveFromCenterHotseatAnimator extends BaseUnfoldMoveFromCente 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 354d1579b0..95a4b8f2e2 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.CellLayout; 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 @@ public class UnfoldMoveFromCenterWorkspaceAnimator extends BaseUnfoldMoveFromCen 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 0000000000..0f0306e344 --- /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 5eb543e40d..34fa7f1764 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.animation.ObjectAnimator; 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.Interpolators; 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 @@ public class WorkspaceRevealAnim { } // 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 50be5ea565..d098ffc2fb 100644 --- a/quickstep/src/com/android/quickstep/views/ClearAllButton.java +++ b/quickstep/src/com/android/quickstep/views/ClearAllButton.java @@ -99,6 +99,10 @@ public class ClearAllButton extends Button { 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 0000000000..c878278da5 --- /dev/null +++ b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java @@ -0,0 +1,475 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.quickstep.views; + +import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; + +import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED; + +import android.content.Context; +import android.graphics.Point; +import android.graphics.Rect; +import android.graphics.drawable.ShapeDrawable; +import android.graphics.drawable.shapes.RoundRectShape; +import android.os.SystemProperties; +import android.util.AttributeSet; +import android.util.Log; +import android.util.SparseArray; +import android.view.MotionEvent; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.Utilities; +import com.android.launcher3.touch.PagedOrientationHandler; +import com.android.launcher3.util.RunnableList; +import com.android.quickstep.RecentsModel; +import com.android.quickstep.SystemUiProxy; +import com.android.quickstep.TaskThumbnailCache; +import com.android.quickstep.util.CancellableTask; +import com.android.quickstep.util.RecentsOrientedState; +import com.android.systemui.shared.recents.model.Task; +import com.android.systemui.shared.recents.model.ThumbnailData; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.function.Consumer; + +/** + * TaskView that contains all tasks that are part of the desktop. + */ +// TODO(b/249371338): TaskView needs to be refactored to have better support for N tasks. +public class DesktopTaskView extends TaskView { + + /** Flag to indicate whether desktop 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<>(); + + public DesktopTaskView(Context context) { + this(context, null); + } + + public DesktopTaskView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public DesktopTaskView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + float[] outerRadii = new float[8]; + Arrays.fill(outerRadii, getTaskCornerRadius()); + RoundRectShape shape = new RoundRectShape(outerRadii, null, null); + ShapeDrawable background = new ShapeDrawable(shape); + background.setTint(getResources().getColor(android.R.color.system_neutral2_300)); + // TODO(b/244348395): this should be wallpaper + setBackground(background); + + mSnapshotViews.add(mSnapshotView); + } + + @Override + public void bind(Task task, RecentsOrientedState orientedState) { + bind(Collections.singletonList(task), orientedState); + } + + /** + * Updates this desktop task to the gives task list defined in {@code tasks} + */ + public void bind(List<Task> tasks, RecentsOrientedState orientedState) { + if (DEBUG) { + StringBuilder sb = new StringBuilder(); + sb.append("bind tasks=").append(tasks.size()).append("\n"); + for (Task task : tasks) { + sb.append(" key=").append(task.key).append("\n"); + } + Log.d(TAG, sb.toString()); + } + if (tasks.isEmpty()) { + return; + } + cancelPendingLoadTasks(); + + mTasks = tasks; + mSnapshotViewMap.clear(); + + // Ensure there are equal number of snapshot views and tasks. + // More tasks than views, add views. More views than tasks, remove views. + // TODO(b/251586230): use a ViewPool for creating TaskThumbnailViews + if (mSnapshotViews.size() > mTasks.size()) { + int diff = mSnapshotViews.size() - mTasks.size(); + for (int i = 0; i < diff; i++) { + TaskThumbnailView snapshotView = mSnapshotViews.remove(0); + removeView(snapshotView); + } + } else if (mSnapshotViews.size() < mTasks.size()) { + int diff = mTasks.size() - mSnapshotViews.size(); + for (int i = 0; i < diff; i++) { + TaskThumbnailView snapshotView = new TaskThumbnailView(getContext()); + mSnapshotViews.add(snapshotView); + addView(snapshotView, new LayoutParams(WRAP_CONTENT, WRAP_CONTENT)); + } + } + + for (int i = 0; i < mTasks.size(); i++) { + Task task = mTasks.get(i); + TaskThumbnailView snapshotView = mSnapshotViews.get(i); + snapshotView.bind(task); + mSnapshotViewMap.put(task.key.id, snapshotView); + } + + updateTaskIdContainer(); + updateTaskIdAttributeContainer(); + + setOrientationState(orientedState); + } + + private void updateTaskIdContainer() { + // TODO(b/249371338): TaskView expects the array to have at least 2 elements. + // At least 2 elements in the array + mTaskIdContainer = new int[Math.max(mTasks.size(), 2)]; + for (int i = 0; i < mTasks.size(); i++) { + mTaskIdContainer[i] = mTasks.get(i).key.id; + } + } + + private void updateTaskIdAttributeContainer() { + // TODO(b/249371338): TaskView expects the array to have at least 2 elements. + // At least 2 elements in the array + mTaskIdAttributeContainer = new TaskIdAttributeContainer[Math.max(mTasks.size(), 2)]; + for (int i = 0; i < mTasks.size(); i++) { + Task task = mTasks.get(i); + TaskThumbnailView thumbnailView = mSnapshotViewMap.get(task.key.id); + mTaskIdAttributeContainer[i] = createAttributeContainer(task, thumbnailView); + } + } + + private TaskIdAttributeContainer createAttributeContainer(Task task, + TaskThumbnailView thumbnailView) { + return new TaskIdAttributeContainer(task, thumbnailView, null, STAGE_POSITION_UNDEFINED); + } + + @Nullable + @Override + public Task getTask() { + // TODO(b/249371338): returning first task. This won't work well with multiple tasks. + return mTasks.size() > 0 ? mTasks.get(0) : null; + } + + @Override + public TaskThumbnailView getThumbnail() { + // TODO(b/249371338): returning single thumbnail. This won't work well with multiple tasks. + Task task = getTask(); + if (task != null) { + return mSnapshotViewMap.get(task.key.id); + } + return null; + } + + @Override + public boolean containsTaskId(int taskId) { + // Thumbnail map contains taskId -> thumbnail map. Use the keys for contains + return mSnapshotViewMap.contains(taskId); + } + + @Override + public void onTaskListVisibilityChanged(boolean visible, int changes) { + cancelPendingLoadTasks(); + if (visible) { + RecentsModel model = RecentsModel.INSTANCE.get(getContext()); + TaskThumbnailCache thumbnailCache = model.getThumbnailCache(); + + if (needsUpdate(changes, FLAG_UPDATE_THUMBNAIL)) { + for (Task task : mTasks) { + CancellableTask<?> thumbLoadRequest = + thumbnailCache.updateThumbnailInBackground(task, thumbnailData -> { + TaskThumbnailView thumbnailView = mSnapshotViewMap.get(task.key.id); + if (thumbnailView != null) { + thumbnailView.setThumbnail(task, thumbnailData); + } + }); + if (thumbLoadRequest != null) { + mPendingThumbnailRequests.add(thumbLoadRequest); + } + } + } + } else { + if (needsUpdate(changes, FLAG_UPDATE_THUMBNAIL)) { + for (Task task : mTasks) { + TaskThumbnailView thumbnailView = mSnapshotViewMap.get(task.key.id); + if (thumbnailView != null) { + thumbnailView.setThumbnail(null, null); + } + // Reset the task thumbnail ref + task.thumbnail = null; + } + } + } + } + + @Override + public void setOrientationState(RecentsOrientedState orientationState) { + // TODO(b/249371338): this copies logic from TaskView + PagedOrientationHandler orientationHandler = orientationState.getOrientationHandler(); + boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL; + DeviceProfile deviceProfile = mActivity.getDeviceProfile(); + + LayoutParams iconParams = (LayoutParams) mIconView.getLayoutParams(); + + int thumbnailTopMargin = deviceProfile.overviewTaskThumbnailTopMarginPx; + int taskIconHeight = deviceProfile.overviewTaskIconSizePx; + int taskMargin = deviceProfile.overviewTaskMarginPx; + + orientationHandler.setTaskIconParams(iconParams, taskMargin, taskIconHeight, + thumbnailTopMargin, isRtl); + + LayoutParams snapshotParams = (LayoutParams) mSnapshotView.getLayoutParams(); + snapshotParams.topMargin = thumbnailTopMargin; + + for (int i = 0; i < mSnapshotViewMap.size(); i++) { + TaskThumbnailView thumbnailView = mSnapshotViewMap.valueAt(i); + thumbnailView.setLayoutParams(snapshotParams); + } + } + + @Override + protected void cancelPendingLoadTasks() { + for (CancellableTask<?> cancellableTask : mPendingThumbnailRequests) { + cancellableTask.cancel(); + } + mPendingThumbnailRequests.clear(); + } + + @Override + public boolean offerTouchToChildren(MotionEvent event) { + return false; + } + + @Override + protected boolean showTaskMenuWithContainer(IconView iconView) { + return false; + } + + @Override + public RunnableList launchTasks() { + SystemUiProxy.INSTANCE.get(getContext()).showDesktopApps(); + getRecentsView().startHome(); + return null; + } + + @Nullable + @Override + public RunnableList launchTaskAnimated() { + return launchTasks(); + } + + @Override + public void launchTask(@NonNull Consumer<Boolean> callback, boolean freezeTaskList) { + launchTasks(); + callback.accept(true); + } + + @Override + void refreshThumbnails(@Nullable HashMap<Integer, ThumbnailData> thumbnailDatas) { + // Sets new thumbnails based on the incoming data and refreshes the rest. + // Create a copy of the thumbnail map, so we can track thumbnails that need refreshing. + SparseArray<TaskThumbnailView> thumbnailsToRefresh = mSnapshotViewMap.clone(); + if (thumbnailDatas != null) { + for (Task task : mTasks) { + int key = task.key.id; + TaskThumbnailView thumbnailView = thumbnailsToRefresh.get(key); + ThumbnailData thumbnailData = thumbnailDatas.get(key); + if (thumbnailView != null && thumbnailData != null) { + thumbnailView.setThumbnail(task, thumbnailData); + // Remove this thumbnail from the list that should be refreshed. + thumbnailsToRefresh.remove(key); + } + } + } + + // Refresh the rest that were not updated. + for (int i = 0; i < thumbnailsToRefresh.size(); i++) { + thumbnailsToRefresh.valueAt(i).refresh(); + } + } + + @Override + public TaskThumbnailView[] getThumbnails() { + TaskThumbnailView[] thumbnails = new TaskThumbnailView[mSnapshotViewMap.size()]; + for (int i = 0; i < thumbnails.length; i++) { + thumbnails[i] = mSnapshotViewMap.valueAt(i); + } + return thumbnails; + } + + @Override + public void onRecycle() { + resetPersistentViewTransforms(); + // Clear any references to the thumbnail (it will be re-read either from the cache or the + // system on next bind) + for (Task task : mTasks) { + TaskThumbnailView thumbnailView = mSnapshotViewMap.get(task.key.id); + if (thumbnailView != null) { + thumbnailView.setThumbnail(task, null); + } + } + setOverlayEnabled(false); + onTaskListVisibilityChanged(false); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + int containerWidth = MeasureSpec.getSize(widthMeasureSpec); + int containerHeight = MeasureSpec.getSize(heightMeasureSpec); + + setMeasuredDimension(containerWidth, containerHeight); + + int thumbnails = mSnapshotViewMap.size(); + if (thumbnails == 0) { + return; + } + + int windowWidth = mActivity.getDeviceProfile().widthPx; + int windowHeight = mActivity.getDeviceProfile().heightPx; + + float scaleWidth = containerWidth / (float) windowWidth; + float scaleHeight = containerHeight / (float) windowHeight; + + if (DEBUG) { + Log.d(TAG, + "onMeasure: container=[" + containerWidth + "," + containerHeight + "] window=[" + + windowWidth + "," + windowHeight + "] scale=[" + scaleWidth + "," + + scaleHeight + "]"); + } + + // Desktop tile is a shrunk down version of launcher and freeform task thumbnails. + for (int i = 0; i < mTasks.size(); i++) { + Task task = mTasks.get(i); + Rect taskSize = task.appBounds; + if (taskSize == null) { + // Default to quarter of the desktop if we did not get app bounds. + taskSize = new Rect(0, 0, windowWidth / 4, windowHeight / 4); + } + + int thumbWidth = (int) (taskSize.width() * scaleWidth); + int thumbHeight = (int) (taskSize.height() * scaleHeight); + + TaskThumbnailView thumbnailView = mSnapshotViewMap.get(task.key.id); + if (thumbnailView != null) { + thumbnailView.measure(MeasureSpec.makeMeasureSpec(thumbWidth, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(thumbHeight, MeasureSpec.EXACTLY)); + + // Position the task to the same position as it would be on the desktop + Point positionInParent = task.positionInParent; + if (positionInParent == null) { + positionInParent = new Point(0, 0); + } + int taskX = (int) (positionInParent.x * scaleWidth); + int taskY = (int) (positionInParent.y * scaleHeight); + thumbnailView.setX(taskX); + thumbnailView.setY(taskY); + + if (DEBUG) { + Log.d(TAG, "onMeasure: task=" + task.key + " thumb=[" + thumbWidth + "," + + thumbHeight + "]" + " pos=[" + taskX + "," + taskY + "]"); + } + } + } + } + + @Override + public void setOverlayEnabled(boolean overlayEnabled) { + // Intentional no-op to prevent setting smart actions overlay on thumbnails + } + + @Override + public void setFullscreenProgress(float progress) { + // TODO(b/249371338): this copies parent implementation and makes it work for N thumbs + progress = Utilities.boundToRange(progress, 0, 1); + mFullscreenProgress = progress; + for (int i = 0; i < mSnapshotViewMap.size(); i++) { + TaskThumbnailView thumbnailView = mSnapshotViewMap.valueAt(i); + thumbnailView.getTaskOverlay().setFullscreenProgress(progress); + updateSnapshotRadius(); + } + } + + @Override + protected void updateSnapshotRadius() { + for (int i = 0; i < mSnapshotViewMap.size(); i++) { + mSnapshotViewMap.valueAt(i).setFullscreenParams(mCurrentFullscreenParams); + } + } + + @Override + protected void setIconsAndBannersTransitionProgress(float progress, boolean invert) { + // no-op + } + + @Override + public void setColorTint(float amount, int tintColor) { + for (int i = 0; i < mSnapshotViewMap.size(); i++) { + mSnapshotViewMap.valueAt(i).setDimAlpha(amount); + } + } + + @Override + protected void applyThumbnailSplashAlpha() { + for (int i = 0; i < mSnapshotViewMap.size(); i++) { + mSnapshotViewMap.valueAt(i).setSplashAlpha(mTaskThumbnailSplashAlpha); + } + } + + @Override + void setThumbnailVisibility(int visibility) { + for (int i = 0; i < mSnapshotViewMap.size(); i++) { + mSnapshotViewMap.valueAt(i).setVisibility(visibility); + } + } + + @Override + protected boolean confirmSecondSplitSelectApp() { + // Desktop tile can't be in split screen + return false; + } +} diff --git a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java index 79b15c713a..96504afcd4 100644 --- a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java +++ b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java @@ -53,7 +53,7 @@ import com.android.launcher3.DeviceProfile; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.touch.PagedOrientationHandler; -import com.android.launcher3.util.SplitConfigurationOptions.StagedSplitBounds; +import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds; import com.android.systemui.shared.recents.model.Task; import java.lang.annotation.Retention; @@ -103,7 +103,7 @@ public final class DigitalWellBeingToast { */ private float mModalOffset = 0f; @Nullable - private StagedSplitBounds mStagedSplitBounds; + private SplitBounds mSplitBounds; private int mSplitBannerConfig = SPLIT_BANNER_FULLSCREEN; private float mSplitOffsetTranslationY; private float mSplitOffsetTranslationX; @@ -164,9 +164,9 @@ public final class DigitalWellBeingToast { }); } - public void setSplitConfiguration(StagedSplitBounds stagedSplitBounds) { - mStagedSplitBounds = stagedSplitBounds; - if (mStagedSplitBounds == null + public void setSplitConfiguration(SplitBounds splitBounds) { + mSplitBounds = splitBounds; + if (mSplitBounds == null || !mActivity.getDeviceProfile().isTablet || mTaskView.isFocusedTask()) { mSplitBannerConfig = SPLIT_BANNER_FULLSCREEN; @@ -180,11 +180,11 @@ public final class DigitalWellBeingToast { } // For landscape grid, for 30% width we only show icon, otherwise show icon and time - if (mTask.key.id == mStagedSplitBounds.leftTopTaskId) { - mSplitBannerConfig = mStagedSplitBounds.leftTaskPercent < THRESHOLD_LEFT_ICON_ONLY ? + if (mTask.key.id == mSplitBounds.leftTopTaskId) { + mSplitBannerConfig = mSplitBounds.leftTaskPercent < THRESHOLD_LEFT_ICON_ONLY ? SPLIT_GRID_BANNER_SMALL : SPLIT_GRID_BANNER_LARGE; } else { - mSplitBannerConfig = mStagedSplitBounds.leftTaskPercent > THRESHOLD_RIGHT_ICON_ONLY ? + mSplitBannerConfig = mSplitBounds.leftTaskPercent > THRESHOLD_RIGHT_ICON_ONLY ? SPLIT_GRID_BANNER_SMALL : SPLIT_GRID_BANNER_LARGE; } } @@ -321,7 +321,7 @@ public final class DigitalWellBeingToast { PagedOrientationHandler orientationHandler = mTaskView.getPagedOrientationHandler(); Pair<Float, Float> translations = orientationHandler .getDwbLayoutTranslations(mTaskView.getMeasuredWidth(), - mTaskView.getMeasuredHeight(), mStagedSplitBounds, deviceProfile, + mTaskView.getMeasuredHeight(), mSplitBounds, deviceProfile, mTaskView.getThumbnails(), mTask.key.id, mBanner); mSplitOffsetTranslationX = translations.first; mSplitOffsetTranslationY = translations.second; @@ -385,4 +385,12 @@ public final class DigitalWellBeingToast { 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 c980d1eac2..75a8ea2b3b 100644 --- a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java +++ b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java @@ -1,8 +1,8 @@ package com.android.quickstep.views; -import static com.android.launcher3.anim.Interpolators.ACCEL; -import static com.android.launcher3.anim.Interpolators.DEACCEL_3; +import static com.android.launcher3.AbstractFloatingView.TYPE_TASK_MENU; import static com.android.launcher3.anim.Interpolators.LINEAR; +import static com.android.launcher3.anim.Interpolators.clampToProgress; import android.animation.ValueAnimator; import android.content.Context; @@ -11,25 +11,30 @@ import android.graphics.Canvas; 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; import androidx.annotation.Nullable; +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; @@ -39,7 +44,8 @@ import com.android.systemui.shared.system.QuickStepContract; * 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. * @@ -47,6 +53,30 @@ import com.android.systemui.shared.system.QuickStepContract; */ 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; @@ -56,6 +86,7 @@ public class FloatingTaskView extends FrameLayout { private PagedOrientationHandler mOrientationHandler; @SplitConfigurationOptions.StagePosition private int mStagePosition; + private final Rect mTmpRect = new Rect(); public FloatingTaskView(Context context) { this(context, null); @@ -70,6 +101,9 @@ public class FloatingTaskView extends FrameLayout { mActivity = BaseActivity.fromContext(context); mIsRtl = Utilities.isRtl(getResources()); mFullscreenParams = new FullscreenDrawParams(context); + + mSplitHolderSize = context.getResources().getDimensionPixelSize( + R.dimen.split_placeholder_icon_size); } @Override @@ -98,9 +132,8 @@ public class FloatingTaskView extends FrameLayout { 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()); } @@ -110,22 +143,42 @@ public class FloatingTaskView extends FrameLayout { */ public static FloatingTaskView getFloatingTaskView(StatefulActivity launcher, View originalView, @Nullable Bitmap thumbnail, Drawable icon, RectF positionOut) { - final BaseDragLayer dragLayer = launcher.getDragLayer(); - ViewGroup parent = (ViewGroup) dragLayer.getParent(); + final ViewGroup dragLayer = launcher.getDragLayer(); final FloatingTaskView floatingView = (FloatingTaskView) launcher.getLayoutInflater() - .inflate(R.layout.floating_split_select_view, parent, false); + .inflate(R.layout.floating_split_select_view, dragLayer, false); floatingView.init(launcher, originalView, thumbnail, icon, positionOut); - parent.addView(floatingView); + // Add this animating view underneath the existing open task menu view (if there is one) + View openTaskView = AbstractFloatingView.getOpenView(launcher, TYPE_TASK_MENU); + int openTaskViewIndex = dragLayer.indexOfChild(openTaskView); + if (openTaskViewIndex == -1) { + // Add to top if not + openTaskViewIndex = dragLayer.getChildCount(); + } + dragLayer.addView(floatingView, openTaskViewIndex - 1); return floatingView; } 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); - final InsettableFrameLayout.LayoutParams lp = new InsettableFrameLayout.LayoutParams( + 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())); initPosition(mStartingPosition, lp); @@ -160,6 +213,10 @@ public class FloatingTaskView extends FrameLayout { 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; @@ -177,8 +234,50 @@ public class FloatingTaskView extends FrameLayout { 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]; @@ -192,26 +291,47 @@ public class FloatingTaskView extends FrameLayout { 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. @@ -223,9 +343,19 @@ public class FloatingTaskView extends FrameLayout { 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; @@ -246,12 +376,16 @@ public class FloatingTaskView extends FrameLayout { * offscreen). */ void centerIconView(IconView iconView, float onScreenRectCenterX, float onScreenRectCenterY) { - mOrientationHandler.updateStagedSplitIconParams(iconView, onScreenRectCenterX, + mOrientationHandler.updateSplitIconParams(iconView, onScreenRectCenterX, onScreenRectCenterY, mFullscreenParams.mScaleX, mFullscreenParams.mScaleY, iconView.getDrawableWidth(), iconView.getDrawableHeight(), 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 8a5f42afaf..6431bdf5aa 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.os.Build; 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.FloatingView; 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 @@ public class FloatingWidgetView extends FrameLayout implements AnimatorListener, * 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 244a794562..3f7d677970 100644 --- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java +++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java @@ -2,7 +2,6 @@ package com.android.quickstep.views; 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; @@ -17,15 +16,18 @@ import com.android.launcher3.DeviceProfile; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.util.RunnableList; -import com.android.launcher3.util.SplitConfigurationOptions.StagedSplitBounds; +import com.android.launcher3.util.SplitConfigurationOptions; +import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds; import com.android.launcher3.util.TransformingTouchDelegate; import com.android.quickstep.RecentsModel; import com.android.quickstep.TaskIconCache; 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; @@ -53,7 +55,7 @@ public class GroupedTaskView extends TaskView { private CancellableTask mIconLoadRequest2; private final float[] mIcon2CenterCoords = new float[2]; private TransformingTouchDelegate mIcon2TouchDelegate; - @Nullable private StagedSplitBounds mSplitBoundsConfig; + @Nullable private SplitBounds mSplitBoundsConfig; private final DigitalWellBeingToast mDigitalWellBeingToast2; public GroupedTaskView(Context context) { @@ -78,15 +80,41 @@ public class GroupedTaskView extends TaskView { } public void bind(Task primary, Task secondary, RecentsOrientedState orientedState, - @Nullable StagedSplitBounds splitBoundsConfig) { + @Nullable SplitBounds splitBoundsConfig) { super.bind(primary, orientedState); mSecondaryTask = secondary; 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 @@ -126,8 +154,8 @@ public class GroupedTaskView extends TaskView { } } - public void updateSplitBoundsConfig(StagedSplitBounds stagedSplitBounds) { - mSplitBoundsConfig = stagedSplitBounds; + public void updateSplitBoundsConfig(SplitBounds splitBounds) { + mSplitBoundsConfig = splitBounds; invalidate(); } @@ -174,7 +202,7 @@ public class GroupedTaskView extends TaskView { // 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( @@ -189,8 +217,9 @@ public class GroupedTaskView extends TaskView { @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 @@ -208,16 +237,23 @@ public class GroupedTaskView extends TaskView { } @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) { @@ -250,8 +286,7 @@ public class GroupedTaskView extends TaskView { @Override public void setOverlayEnabled(boolean overlayEnabled) { - super.setOverlayEnabled(overlayEnabled); - mSnapshotView2.setOverlayEnabled(overlayEnabled); + // Intentional no-op to prevent setting smart actions overlay on thumbnails } @Override @@ -296,8 +331,8 @@ public class GroupedTaskView extends TaskView { } @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); @@ -313,4 +348,33 @@ public class GroupedTaskView extends TaskView { mSnapshotView2.setDimAlpha(amount); mDigitalWellBeingToast2.setBannerColorTint(tintColor, amount); } + + @Override + protected void applyThumbnailSplashAlpha() { + super.applyThumbnailSplashAlpha(); + 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 306ebd73c8..6c27587058 100644 --- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java +++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java @@ -21,24 +21,24 @@ 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.LauncherState.SPRING_LOADED; -import static com.android.launcher3.testing.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.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.util.SplitSelectStateController; @@ -47,7 +47,7 @@ import com.android.quickstep.util.SplitSelectStateController; * {@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) { @@ -67,7 +67,6 @@ public class LauncherRecentsView extends RecentsView<BaseQuickstepLauncher, Laun public void init(OverviewActionsView actionsView, SplitSelectStateController splitPlaceholderView) { super.init(actionsView, splitPlaceholderView); - Log.d(BAD_STATE, "LauncherRecentsView init setContentAlpha=0"); setContentAlpha(0); } @@ -89,9 +88,23 @@ public class LauncherRecentsView extends RecentsView<BaseQuickstepLauncher, Laun } @Override + public void onTaskIconChanged(int taskId) { + // If Launcher needs to return to split select state, do it now, after the icon has updated. + if (mActivity.hasPendingSplitSelectInfo()) { + PendingSplitSelectInfo recoveryData = mActivity.getPendingSplitSelectInfo(); + if (recoveryData.getStagedTaskId() == taskId) { + initiateSplitSelect( + getTaskViewByTaskId(recoveryData.getStagedTaskId()), + recoveryData.getStagePosition(), recoveryData.getSource() + ); + mActivity.finishSplitSelectRecovery(); + } + } + } + + @Override public void reset() { super.reset(); - setLayoutRotation(Surface.ROTATION_0, Surface.ROTATION_0); } @@ -103,7 +116,6 @@ public class LauncherRecentsView extends RecentsView<BaseQuickstepLauncher, Laun if (toState == OVERVIEW_MODAL_TASK) { setOverviewSelectEnabled(true); } - Log.d(BAD_STATE, "LRV onStateTransitionStart setFreezeVisibility=true, toState=" + toState); setFreezeViewVisibility(true); } @@ -115,8 +127,6 @@ public class LauncherRecentsView extends RecentsView<BaseQuickstepLauncher, Laun } 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); @@ -137,6 +147,9 @@ public class LauncherRecentsView extends RecentsView<BaseQuickstepLauncher, Laun & CLEAR_ALL_BUTTON) != 0; setDisallowScrollToClearAll(!hasClearAllButton); } + if (mActivity.getDesktopVisibilityController() != null) { + mActivity.getDesktopVisibilityController().setOverviewStateEnabled(enabled); + } } @Override @@ -152,13 +165,12 @@ public class LauncherRecentsView extends RecentsView<BaseQuickstepLauncher, Laun } @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(); } } @@ -177,8 +189,9 @@ public class LauncherRecentsView extends RecentsView<BaseQuickstepLauncher, Laun @Override public void initiateSplitSelect(TaskView taskView, - @SplitConfigurationOptions.StagePosition int stagePosition) { - super.initiateSplitSelect(taskView, stagePosition); + @SplitConfigurationOptions.StagePosition int stagePosition, + StatsLogManager.EventEnum splitEvent) { + super.initiateSplitSelect(taskView, stagePosition, splitEvent); mActivity.getStateManager().goToState(LauncherState.OVERVIEW_SPLIT_SELECT); } diff --git a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java index 16be5a21e9..16841661cf 100644 --- a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java +++ b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java @@ -22,10 +22,8 @@ import android.graphics.Rect; import android.util.AttributeSet; import android.view.View; import android.view.View.OnClickListener; -import android.view.ViewGroup; import android.widget.Button; import android.widget.FrameLayout; -import android.widget.LinearLayout; import androidx.annotation.IntDef; import androidx.annotation.Nullable; @@ -33,11 +31,11 @@ import androidx.annotation.Nullable; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Insettable; import com.android.launcher3.R; -import com.android.launcher3.uioverrides.ApiWrapper; +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; @@ -56,7 +54,9 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo 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 { } @@ -64,6 +64,7 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo 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, @@ -80,8 +81,17 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo private static final int INDEX_VISIBILITY_ALPHA = 1; 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; - private final MultiValueAlpha mMultiValueAlpha; + 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; @ActionsHiddenFlags @@ -90,6 +100,12 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo @ActionsDisabledFlags protected int mDisabledFlags; + @SplitButtonHiddenFlags + private int mSplitButtonHiddenFlags; + + @SplitButtonDisabledFlags + private int mSplitButtonDisabledFlags; + @Nullable protected T mCallbacks; @@ -107,16 +123,16 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo public OverviewActionsView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr, 0); - mMultiValueAlpha = new MultiValueAlpha(this, 5); - mMultiValueAlpha.setUpdateVisibility(true); } @Override protected void onFinishInflate() { super.onFinishInflate(); + mMultiValueAlpha = new MultiValueAlpha(findViewById(R.id.action_buttons), NUM_ALPHAS); + mMultiValueAlpha.setUpdateVisibility(true); + findViewById(R.id.action_screenshot).setOnClickListener(this); findViewById(R.id.action_clear_all).setOnClickListener(this); - mSplitButton = findViewById(R.id.action_split); mSplitButton.setOnClickListener(this); } @@ -165,7 +181,7 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo 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); } /** @@ -184,18 +200,60 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo } 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 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); } /** @@ -205,10 +263,15 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo if (mDp == null) { return; } - boolean alignFor3ButtonTaskbar = mDp.isTaskbarPresent && !mDp.isGestureMode; - if (alignFor3ButtonTaskbar) { + boolean largeScreenLandscape = mDp.isTablet && !mDp.isTwoPanels && mDp.isLandscape; + // 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 taskbar is in overview, overview action has dedicated space above nav buttons + && !FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get()) { // Add extra horizontal spacing - int additionalPadding = ApiWrapper.getHotseatEndOffset(getContext()); + int additionalPadding = mDp.hotseatBarEndOffset; if (isLayoutRtl()) { setPadding(mInsets.left + additionalPadding, 0, mInsets.right, 0); } else { @@ -236,11 +299,8 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo return 0; } - if (mDp.isVerticalBarLayout()) { - return mDp.getInsets().bottom; - } - - if (!mDp.isGestureMode && mDp.isTaskbarPresent) { + if (!mDp.isGestureMode && mDp.isTaskbarPresent + && !FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get()) { return mDp.getOverviewActionsClaimedSpaceBelow(); } @@ -257,12 +317,6 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo mTaskSize.set(taskSize); updateVerticalMargin(DisplayController.getNavigationMode(getContext())); - LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( - dp.isVerticalBarLayout() ? 0 : dp.overviewActionsButtonSpacing, - ViewGroup.LayoutParams.MATCH_PARENT); - params.weight = dp.isVerticalBarLayout() ? 1 : 0; - findViewById(R.id.action_split_space).setLayoutParams(params); - requestLayout(); mSplitButton.setCompoundDrawablesWithIntrinsicBounds( @@ -270,12 +324,17 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo 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 220a7f463c..c16aecbfeb 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -34,27 +34,36 @@ import static com.android.launcher3.Utilities.squaredTouchSlop; import static com.android.launcher3.anim.Interpolators.ACCEL; import static com.android.launcher3.anim.Interpolators.ACCEL_0_75; import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL; +import static com.android.launcher3.anim.Interpolators.DEACCEL_2; +import static com.android.launcher3.anim.Interpolators.EMPHASIZED_DECELERATE; import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN; import static com.android.launcher3.anim.Interpolators.FINAL_FRAME; import static com.android.launcher3.anim.Interpolators.LINEAR; +import static com.android.launcher3.anim.Interpolators.OVERSHOOT_0_75; import static com.android.launcher3.anim.Interpolators.clampToProgress; -import static com.android.launcher3.config.FeatureFlags.ENABLE_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; @@ -66,9 +75,13 @@ import android.animation.PropertyValuesHolder; 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; @@ -96,6 +109,7 @@ import android.view.HapticFeedbackConstants; 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; @@ -121,6 +135,7 @@ import com.android.launcher3.InvariantDeviceProfile; 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; @@ -128,6 +143,7 @@ import com.android.launcher3.anim.SpringProperty; import com.android.launcher3.compat.AccessibilityManagerCompat; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.icons.cache.HandlerRunnable; +import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.popup.QuickstepSystemShortcut; import com.android.launcher3.statehandlers.DepthController; import com.android.launcher3.statemanager.BaseState; @@ -137,21 +153,20 @@ import com.android.launcher3.touch.PagedOrientationHandler; 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.SplitConfigurationOptions.StagedSplitBounds; 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.RecentsModel.TaskVisualsChangeListener; import com.android.quickstep.RemoteAnimationTargets; import com.android.quickstep.RemoteTargetGluer; import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle; @@ -162,28 +177,33 @@ import com.android.quickstep.TaskThumbnailCache; 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; @@ -196,7 +216,7 @@ import java.util.function.Consumer; 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; @@ -264,7 +284,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T 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 @@ -362,6 +382,10 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T } }; + /** + * Progress of Recents view from carousel layout to grid layout. If Recents is not shown as a + * grid, then the value remains 0. + */ public static final FloatProperty<RecentsView> RECENTS_GRID_PROGRESS = new FloatProperty<RecentsView>("recentsGrid") { @Override @@ -375,6 +399,23 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T } }; + /** + * 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> TASK_THUMBNAIL_SPLASH_ALPHA = + new FloatProperty<RecentsView>("taskThumbnailSplashAlpha") { + @Override + public void setValue(RecentsView view, float taskThumbnailSplashAlpha) { + view.setTaskThumbnailSplashAlpha(taskThumbnailSplashAlpha); + } + + @Override + public Float get(RecentsView view) { + return view.mTaskThumbnailSplashAlpha; + } + }; + // OverScroll constants private static final int OVERSCROLL_PAGE_SNAP_ANIMATION_DURATION = 270; @@ -445,10 +486,11 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T 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; @@ -459,12 +501,16 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T 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 mTaskThumbnailSplashAlpha = 0; private boolean mShowAsGridLastOnLayout = false; private final IntSet mTopRowIdSet = new IntSet(); @@ -536,7 +582,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T if (taskRemoved) { dismissTask(taskId); } - }); + }, RecentsFilterState.getFilter(mFilterState.getPackageNameToFilter())); } })); } @@ -622,14 +668,14 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T @Nullable private TaskView mSplitHiddenTaskView; @Nullable - private View mSecondSplitHiddenView; + private TaskView mSecondSplitHiddenView; @Nullable - private StagedSplitBounds mSplitBoundsConfig; - private final Toast mSplitToast = Toast.makeText(getContext(), - R.string.toast_split_select_app, Toast.LENGTH_SHORT); + private SplitBounds mSplitBoundsConfig; private final Toast mSplitUnsupportedToast = Toast.makeText(getContext(), R.string.toast_split_app_unsupported, Toast.LENGTH_SHORT); + private SplitInstructionsView mSplitInstructionsView; + @Nullable private QuickstepSystemShortcut.SplitSelectSource mSplitSelectSource; @@ -677,6 +723,9 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T @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); @@ -702,6 +751,8 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T 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); @@ -710,6 +761,8 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T 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); @@ -736,6 +789,55 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T 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() { @@ -781,8 +883,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T } super.dispatchDraw(canvas); } - if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mEnableDrawingLiveTile - && mRemoteTargetHandles != null) { + if (mEnableDrawingLiveTile && mRemoteTargetHandles != null) { redrawLiveTile(); } } @@ -877,7 +978,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T mSplitSelectStateController = splitController; } - public SplitSelectStateController getSplitPlaceholder() { + public SplitSelectStateController getSplitSelectController() { return mSplitSelectStateController; } @@ -905,10 +1006,9 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T .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(); } @@ -924,8 +1024,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T .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(); @@ -947,6 +1046,8 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T } if (child instanceof GroupedTaskView) { mGroupedTaskViewPool.recycle((GroupedTaskView) taskView); + } else if (child instanceof DesktopTaskView) { + mDesktopTaskViewPool.recycle((DesktopTaskView) taskView); } else { mTaskViewPool.recycle(taskView); } @@ -1021,8 +1122,8 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T } } - 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)) { @@ -1034,14 +1135,15 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T 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() { @@ -1165,14 +1267,60 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T 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 RecentModel, 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 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 findLastActiveTaskAndDoSplitOperation(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(); @@ -1203,17 +1351,22 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T if (!mActivity.getDeviceProfile().isTablet) { mActionsView.updateDisabledFlags(OverviewActionsView.DISABLED_SCROLLING, true); } + InteractionJankMonitorWrapper.begin(/* view= */ this, + InteractionJankMonitorWrapper.CUJ_RECENTS_SCROLLING); } @Override protected void onPageEndTransition() { super.onPageEndTransition(); + ActiveGestureLog.INSTANCE.addLog( + "onPageEndTransition: current page index updated", getNextPage()); if (isClearAllHidden() && !mActivity.getDeviceProfile().isTablet) { mActionsView.updateDisabledFlags(OverviewActionsView.DISABLED_SCROLLING, false); } if (getNextPage() > 0) { setSwipeDownShouldLaunchApp(true); } + InteractionJankMonitorWrapper.end(InteractionJankMonitorWrapper.CUJ_RECENTS_SCROLLING); } @Override @@ -1398,6 +1551,17 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T 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)); @@ -1411,6 +1575,9 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T // 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; } @@ -1436,36 +1603,68 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T 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.mStagedSplitBounds.leftTopTaskId == groupTask.task1.key.id; + 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.mStagedSplitBounds); + groupTask.mSplitBounds); + } else if (taskView instanceof DesktopTaskView) { + ((DesktopTaskView) taskView).bind(((DesktopTask) groupTask).tasks, + mOrientationState); } else { taskView.bind(groupTask.task1, mOrientationState); } + + // enables instance filtering if the feature flag for it is on + if (FeatureFlags.ENABLE_MULTI_INSTANCE.get()) { + taskView.setUpShowAllInstancesListener(); + } } + if (!taskGroups.isEmpty()) { addView(mClearAllButton); } - boolean settlingOnNewTask = mNextPage != INVALID_PAGE; - if (settlingOnNewTask) { - // Restore mCurrentPage but don't call setCurrentPage() as that clobbers the scroll. - mCurrentPage = previousCurrentPage; - } else { - setCurrentPage(previousCurrentPage); - } - // Keep same previous focused task TaskView newFocusedTaskView = getTaskViewByTaskId(focusedTaskId); // If the list changed, maybe the focused task doesn't exist anymore @@ -1490,21 +1689,36 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T } 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 && @@ -1517,6 +1731,9 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T resetTaskVisuals(); onTaskStackUpdated(); updateEnabledOverlays(); + if (isPageScrollsInitialized()) { + onPageScrollsInitialized(); + } } private boolean isModal() { @@ -1582,24 +1799,23 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T 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); @@ -1679,6 +1895,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T // Changed orientations, update controllers so they intercept accordingly. mActivity.getDragLayer().recreateControllers(); onOrientationChanged(); + resetTaskVisuals(); } boolean isInLandscape = mOrientationState.getTouchRotation() != ROTATION_0 @@ -1700,7 +1917,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T 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(); } @@ -1833,9 +2050,11 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T 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(); } @@ -1848,6 +2067,9 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T } 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 @@ -1855,7 +2077,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T 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()); @@ -1991,7 +2213,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T 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 { @@ -2072,10 +2294,19 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T * 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; @@ -2100,7 +2331,8 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T */ public void reloadIfNeeded() { if (!mModel.isTaskListValid(mTaskListChangeId)) { - mTaskListChangeId = mModel.getTasks(this::applyLoadPlan); + mTaskListChangeId = mModel.getTasks(this::applyLoadPlan, RecentsFilterState + .getFilter(mFilterState.getPackageNameToFilter())); } } @@ -2167,8 +2399,14 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T 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(); } } @@ -2180,12 +2418,13 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T @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) { @@ -2209,6 +2448,13 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T } } } + int splashAlpha = endState.showTaskThumbnailSplash() ? 1 : 0; + if (animatorSet == null) { + setTaskThumbnailSplashAlpha(splashAlpha); + } else { + animatorSet.play( + ObjectAnimator.ofFloat(this, TASK_THUMBNAIL_SPLASH_ALPHA, splashAlpha)); + } } /** @@ -2222,9 +2468,6 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T setEnableFreeScroll(true); setEnableDrawingLiveTile(mCurrentGestureEndTarget == GestureState.GestureEndTarget.RECENTS); - if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) { - setRunningTaskViewShowScreenshot(true); - } setRunningTaskHidden(false); animateUpTaskIconScale(); animateActionsViewIn(); @@ -2262,12 +2505,19 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T } 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 @@ -2276,7 +2526,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T ((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 @@ -2300,7 +2550,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T boolean runningTaskTileHidden = mRunningTaskTileHidden; setCurrentTask(runningTaskViewId); mFocusedTaskViewId = runningTaskViewId; - setCurrentPage(getRunningTaskIndex()); + runOnPageScrollsInitialized(() -> setCurrentPage(getRunningTaskIndex())); setRunningTaskViewShowScreenshot(false); setRunningTaskHidden(runningTaskTileHidden); // Update task size after setting current task. @@ -2311,6 +2561,18 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T 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. */ @@ -2349,12 +2611,10 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T } 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); } } @@ -2645,17 +2905,25 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T * @param gridProgress 0 = carousel; 1 = 2 row grid. */ private void setGridProgress(float gridProgress) { + mGridProgress = gridProgress; + + int taskCount = getTaskViewCount(); + for (int i = 0; i < taskCount; i++) { + requireTaskViewAt(i).setGridProgress(gridProgress); + } + mClearAllButton.setGridProgress(gridProgress); + } + + private void setTaskThumbnailSplashAlpha(float taskThumbnailSplashAlpha) { int taskCount = getTaskViewCount(); if (taskCount == 0) { return; } - mGridProgress = gridProgress; - + mTaskThumbnailSplashAlpha = taskThumbnailSplashAlpha; for (int i = 0; i < taskCount; i++) { - requireTaskViewAt(i).setGridProgress(gridProgress); + requireTaskViewAt(i).setTaskThumbnailSplashAlpha(taskThumbnailSplashAlpha); } - mClearAllButton.setGridProgress(gridProgress); } private void enableLayoutTransitions() { @@ -2711,7 +2979,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T 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(); @@ -2723,8 +2991,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T 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() @@ -2745,39 +3012,110 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T 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 */); } + + // TODO (b/257513449): Launch animation not fully complete. OK to remove flag once it is. + if (ENABLE_LAUNCH_FROM_STAGED_APP.get()) { + mFirstFloatingTaskView.setOnClickListener(this::animateToFullscreen); + } + + // SplitInstructionsView: animate in + safeRemoveDragLayerView(mSplitInstructionsView); + mSplitInstructionsView = SplitInstructionsView.getSplitInstructionsView(mActivity); + mSplitInstructionsView.setAlpha(0); + anim.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) { - mSplitToast.show(); InteractionJankMonitorWrapper.end( InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER); } else { + // If transition to split select was interrupted, clean up to prevent glitches + resetFromSplitSelectionState(); InteractionJankMonitorWrapper.cancel( InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER); } }); } + private void animateToFullscreen(View view) { + FloatingTaskView stagedTaskView = (FloatingTaskView) view; + + boolean isTablet = mActivity.getDeviceProfile().isTablet; + int duration = isTablet + ? SplitAnimationTimings.TABLET_CONFIRM_DURATION + : SplitAnimationTimings.PHONE_CONFIRM_DURATION; + + PendingAnimation pendingAnimation = new PendingAnimation(duration); + + Rect firstTaskStartingBounds = new Rect(); + Rect firstTaskEndingBounds = new Rect(); + + stagedTaskView.getBoundsOnScreen(firstTaskStartingBounds); + mActivity.getDragLayer().getBoundsOnScreen(firstTaskEndingBounds); + + stagedTaskView.addConfirmAnimation( + pendingAnimation, + new RectF(firstTaskStartingBounds), + firstTaskEndingBounds, + false /* fadeWithThumbnail */, + true /* isStagedTask */); + + pendingAnimation.addEndListener(success -> launchStagedTask()); + + pendingAnimation.buildAnim().start(); + } + /** * Creates a {@link PendingAnimation} for dismissing the specified {@link TaskView}. * @param dismissedTaskView the {@link TaskView} to be dismissed @@ -2788,17 +3126,16 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T * @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(); @@ -2939,8 +3276,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T 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 -> @@ -2966,6 +3302,9 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T } } + SplitAnimationTimings splitTimings = + AnimUtils.getDeviceOverviewToSplitTimings(mActivity.getDeviceProfile().isTablet); + int distanceFromDismissedTask = 0; for (int i = 0; i < count; i++) { View child = getChildAt(i); @@ -3008,11 +3347,31 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T 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( @@ -3043,11 +3402,35 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T // 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(); @@ -3062,7 +3445,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T 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, @@ -3070,7 +3453,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T } 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); @@ -3086,8 +3469,8 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T anim.setFloat(taskView, taskView.getPrimaryDismissTranslationProperty(), mIsRtl ? primaryTranslation : -primaryTranslation, - clampToProgress(LINEAR, animationStartProgress, - dismissTranslationInterpolationEnd)); + clampToProgress(dismissInterpolator, animationStartProgress, + animationEndProgress)); } } } @@ -3096,7 +3479,9 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T anim.addOnFrameCallback(this::updateCurveProperties); } - // Add a tiny bit of translation Z, so that it draws on top of other views + // Add a tiny bit of translation Z, so that it draws on top of other views. This is relevant + // (e.g.) when we dismiss a task by sliding it upward: if there is a row of icons above, we + // want the dragged task to stay above all other views. if (animateTaskView) { dismissedTaskView.setTranslationZ(0.1f); } @@ -3109,8 +3494,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T 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 { @@ -3127,8 +3511,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T 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 { @@ -3229,7 +3612,8 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T removeViewInLayout(mClearAllButton); if (isHomeTaskDismissed) { updateEmptyMessage(); - } else { + } else if (!(ENABLE_TASKBAR_IN_OVERVIEW.get() && + mSplitSelectStateController.isSplitSelectActive())) { startHome(); } } else { @@ -3317,7 +3701,6 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T mPendingAnimation = null; } }); - return anim; } /** @@ -3329,11 +3712,11 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T 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); } /** @@ -3485,8 +3868,10 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T } 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") @@ -3790,8 +4175,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T ? ((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); @@ -3999,36 +4383,45 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T } } + /** + * 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, null); - } } + /** + * 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); } } @@ -4036,32 +4429,48 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T * 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; @@ -4071,30 +4480,39 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T 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"); + + // Fade out all other views underneath placeholders + ObjectAnimator tvFade = ObjectAnimator.ofFloat(this, RecentsView.CONTENT_ALPHA,1, 0); + pendingAnimation.add(tvFade, DEACCEL_2, SpringProperty.DEFAULT); pendingAnimation.buildAnim().start(); return true; } @@ -4103,19 +4521,20 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T @SuppressLint("WrongCall") protected void resetFromSplitSelectionState() { if (mSplitSelectSource != null || mSplitHiddenTaskViewIndex != -1) { - if (mFirstFloatingTaskView != null) { - mActivity.getRootView().removeView(mFirstFloatingTaskView); - mFirstFloatingTaskView = null; - } - if (mSecondFloatingTaskView != null) { - mActivity.getRootView().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; } @@ -4129,20 +4548,32 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T 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; } @@ -4168,6 +4599,10 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T taskViewsFloat.first.set(this, getSplitSelectTranslation()); taskViewsFloat.second.set(this, 0f); + if (mSplitInstructionsView != null) { + mSplitInstructionsView.ensureProperRotation(); + } + applySplitPrimaryScrollOffset(); } @@ -4242,30 +4677,31 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T * 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, @@ -4285,6 +4721,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T properties)); } } + anim.play(ObjectAnimator.ofFloat(this, TASK_THUMBNAIL_SPLASH_ALPHA, 0, 1)); return anim; } @@ -4340,21 +4777,21 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T 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, 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) { @@ -4366,7 +4803,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T dividerAnimator.end(); }); } - if (ENABLE_QUICKSTEP_LIVE_TILE.get() && tv.isRunningTask()) { + if (tv.isRunningTask()) { finishRecentsAnimation(false /* toRecents */, null); onTaskLaunchAnimationEnd(true /* success */); } else { @@ -4385,6 +4822,16 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T return mPendingAnimation; } + protected void launchStagedTask() { + if (mSplitHiddenTaskView != null) { + // Split staging was started from an existing running task (in Overview) + mSplitHiddenTaskView.launchTask(success -> resetFromSplitSelectionState()); + } else { + // Split staging was started from a new intent (from app menu in Home/AllApps) + mActivity.startActivity(mSplitSelectSource.intent); + } + } + protected void onTaskLaunchAnimationEnd(boolean success) { if (success) { resetTaskVisuals(); @@ -4477,7 +4924,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T RemoteTargetGluer gluer = new RemoteTargetGluer(getContext(), getSizeStrategy()); mRemoteTargetHandles = gluer.assignTargetsForSplitScreen( getContext(), recentsAnimationTargets); - mSplitBoundsConfig = gluer.getStagedSplitBounds(); + 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 // to create new RemoteAnimationTargets in assignTargetsForSplitScreen(), and the @@ -4527,12 +4974,6 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T @Nullable Runnable onFinishComplete) { // TODO(b/197232424#comment#10) Move this back into onRecentsAnimationComplete(). Maybe? cleanupRemoteTargets(); - if (!toRecents && ENABLE_QUICKSTEP_LIVE_TILE.get()) { - // Reset the minimized state since we force-toggled the minimized state when entering - // overview, but never actually finished the recents animation. This is a catch all for - // cases where we haven't already reset it. - SystemUiProxy.INSTANCE.get(getContext()).setSplitScreenMinimized(false); - } if (mRecentsAnimationController == null) { if (onFinishComplete != null) { @@ -4545,7 +4986,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T 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}. @@ -4553,6 +4994,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T new PictureInPictureSurfaceTransaction.Builder() .setAlpha(0f) .build(); + tx.setShouldDisableCanAffectSystemUiFlags(false); int[] taskIds = TopTaskTracker.INSTANCE.get(getContext()).getRunningSplitTaskIds(); for (int taskId : taskIds) { mRecentsAnimationController.setFinishTaskTransaction(taskId, @@ -4755,9 +5197,35 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T } /** + * 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; } @@ -4863,10 +5331,10 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T } private void updateEnabledOverlays() { - int overlayEnabledPage = mOverlayEnabled ? getNextPage() : -1; int taskCount = getTaskViewCount(); for (int i = 0; i < taskCount; i++) { - requireTaskViewAt(i).setOverlayEnabled(i == overlayEnabledPage); + TaskView taskView = requireTaskViewAt(i); + taskView.setOverlayEnabled(mOverlayEnabled && isTaskViewFullyVisible(taskView)); } } @@ -4987,17 +5455,8 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T 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; @@ -5236,6 +5695,16 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T 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 new file mode 100644 index 0000000000..b0b111d376 --- /dev/null +++ b/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java @@ -0,0 +1,139 @@ +/* + * Copyright 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 com.android.launcher3.util.NavigationMode.THREE_BUTTONS; + +import android.content.Context; +import android.util.AttributeSet; +import android.util.FloatProperty; +import android.view.ViewGroup; +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; + +/** + * A rounded rectangular component containing a single TextView. + * Appears when a split is in progress, and tells the user to select a second app to initiate + * splitscreen. + * + * Appears and disappears concurrently with a FloatingTaskView. + */ +public class SplitInstructionsView extends FrameLayout { + private final StatefulActivity mLauncher; + private AppCompatTextView mTextView; + + public static final FloatProperty<SplitInstructionsView> UNFOLD = + new FloatProperty<SplitInstructionsView>("SplitInstructionsUnfold") { + @Override + public void setValue(SplitInstructionsView splitInstructionsView, float v) { + splitInstructionsView.setScaleY(v); + } + + @Override + public Float get(SplitInstructionsView splitInstructionsView) { + return splitInstructionsView.getScaleY(); + } + }; + + public SplitInstructionsView(Context context) { + this(context, null); + } + + public SplitInstructionsView(Context context, @Nullable AttributeSet attrs) { + this(context, attrs, 0); + } + + public SplitInstructionsView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + mLauncher = (StatefulActivity) context; + } + + static SplitInstructionsView getSplitInstructionsView(StatefulActivity launcher) { + ViewGroup dragLayer = launcher.getDragLayer(); + final SplitInstructionsView splitInstructionsView = + (SplitInstructionsView) launcher.getLayoutInflater().inflate( + R.layout.split_instructions_view, + dragLayer, + 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; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + ensureProperRotation(); + } + + void ensureProperRotation() { + ((RecentsView) mLauncher.getOverviewPanel()).getPagedOrientationHandler() + .setSplitInstructionsParams( + this, + mLauncher.getDeviceProfile(), + getMeasuredHeight(), + getMeasuredWidth(), + getThreeButtonNavShift() + ); + } + + // In some cases, when user is using 3-button nav, there isn't enough room for both the + // 3-button nav and a centered SplitInstructionsView. This function will return an int that will + // be used to shift the SplitInstructionsView over a bit so that everything looks well-spaced. + // In many cases, this will return 0, since we don't need to shift it away from the center. + int getThreeButtonNavShift() { + DeviceProfile dp = mLauncher.getDeviceProfile(); + if ((DisplayController.getNavigationMode(getContext()) == THREE_BUTTONS) + && ((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_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 + // want to shift it over by half the 3-button layout's width. + // If the user is using an RtL layout, we shift it the opposite way. + return -((3 * navButtonWidth + extraMargin) / 2) * (isLayoutRtl() ? -1 : 1); + } else { + 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 28080d477f..08004dcf1f 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.Paint; 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 @@ public class SplitPlaceholderView extends FrameLayout { 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 3803f1b426..2c9afb4b4c 100644 --- a/quickstep/src/com/android/quickstep/views/TaskMenuView.java +++ b/quickstep/src/com/android/quickstep/views/TaskMenuView.java @@ -16,9 +16,6 @@ package com.android.quickstep.views; -import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE; -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; @@ -26,7 +23,6 @@ import android.animation.AnimatorSet; 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; @@ -35,7 +31,6 @@ import android.view.Gravity; 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; @@ -59,13 +54,12 @@ import com.android.quickstep.views.TaskView.TaskIdAttributeContainer; /** * 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; @@ -84,7 +78,6 @@ public class TaskMenuView extends AbstractFloatingView implements OnScrollChange mActivity = BaseDraggingActivity.fromContext(context); setClipToOutline(true); - mTaskInsetMargin = getResources().getDimension(R.dimen.task_card_margin); } @Override @@ -132,50 +125,6 @@ public class TaskMenuView extends AbstractFloatingView implements OnScrollChange }; } - 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(); @@ -207,17 +156,9 @@ public class TaskMenuView extends AbstractFloatingView implements OnScrollChange 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) { @@ -232,8 +173,7 @@ public class TaskMenuView extends AbstractFloatingView implements OnScrollChange private void addMenuOptions(TaskIdAttributeContainer taskContainer) { mTaskName.setText(TaskUtils.getTitle(getContext(), taskContainer.getTask())); mTaskName.setOnClickListener(v -> close(true)); - TaskOverlayFactory.getEnabledShortcuts(mTaskView, mActivity.getDeviceProfile(), - taskContainer) + TaskOverlayFactory.getEnabledShortcuts(mTaskView, taskContainer) .forEach(this::addMenuOption); } @@ -245,17 +185,9 @@ public class TaskMenuView extends AbstractFloatingView implements OnScrollChange LayoutParams lp = (LayoutParams) menuOptionView.getLayoutParams(); mTaskView.getPagedOrientationHandler().setLayoutParamsForTaskMenuOptionItem(lp, menuOptionView, mActivity.getDeviceProfile()); - menuOptionView.setOnClickListener(view -> { - if (ENABLE_QUICKSTEP_LIVE_TILE.get()) { - RecentsView recentsView = mTaskView.getRecentsView(); - recentsView.switchToScreenshot(null, - () -> recentsView.finishRecentsAnimation(true /* toRecents */, - false /* shouldPip */, - () -> menuOption.onClick(view))); - } else { - menuOption.onClick(view); - } - }); + // Set an onClick listener on each menu option. The onClick method is responsible for + // ending LiveTile mode on the thumbnail if needed. + menuOptionView.setOnClickListener(menuOption::onClick); mOptionLayout.addView(menuOptionView); } @@ -263,18 +195,18 @@ public class TaskMenuView extends AbstractFloatingView implements OnScrollChange 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); @@ -289,7 +221,22 @@ public class TaskMenuView extends AbstractFloatingView implements OnScrollChange 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() { @@ -335,7 +282,6 @@ public class TaskMenuView extends AbstractFloatingView implements OnScrollChange 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 06a579300a..bdc0585f83 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.ArrowPopup import com.android.launcher3.popup.RoundedArrowDrawable import com.android.launcher3.popup.SystemShortcut import com.android.launcher3.util.Themes -import com.android.quickstep.KtR import com.android.quickstep.TaskOverlayFactory import com.android.quickstep.views.TaskView.TaskIdAttributeContainer @@ -53,9 +52,9 @@ class TaskMenuViewWithArrow<T : BaseDraggingActivity> : ArrowPopup<T> { .fromContext<BaseDraggingActivity>(taskContainer.taskView.context) val taskMenuViewWithArrow = activity.layoutInflater .inflate( - KtR.layout.task_menu_with_arrow, - activity.dragLayer, - false + R.layout.task_menu_with_arrow, + activity.dragLayer, + false ) as TaskMenuViewWithArrow<*> return taskMenuViewWithArrow.populateAndShowForTask(taskContainer, alignSecondRow) @@ -93,7 +92,7 @@ class TaskMenuViewWithArrow<T : BaseDraggingActivity> : ArrowPopup<T> { private var optionMeasuredHeight = 0 private val arrowHorizontalPadding: Int get() = if (taskView.isFocusedTask) - resources.getDimensionPixelSize(KtR.dimen.task_menu_horizontal_padding) + resources.getDimensionPixelSize(R.dimen.task_menu_horizontal_padding) else 0 @@ -119,7 +118,7 @@ class TaskMenuViewWithArrow<T : BaseDraggingActivity> : ArrowPopup<T> { override fun onFinishInflate() { super.onFinishInflate() - optionLayout = findViewById(KtR.id.menu_option_layout) + optionLayout = findViewById(R.id.menu_option_layout) } private fun populateAndShowForTask( @@ -164,13 +163,13 @@ class TaskMenuViewWithArrow<T : BaseDraggingActivity> : ArrowPopup<T> { private fun addMenuOptions() { // Add the options TaskOverlayFactory - .getEnabledShortcuts(taskView, mActivityContext.deviceProfile, taskContainer) + .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 @@ -187,7 +186,7 @@ class TaskMenuViewWithArrow<T : BaseDraggingActivity> : ArrowPopup<T> { private fun addMenuOption(menuOption: SystemShortcut<*>) { val menuOptionView = mActivityContext.layoutInflater.inflate( - KtR.layout.task_view_menu_option, this, false + R.layout.task_view_menu_option, this, false ) as LinearLayout menuOption.setIconAndLabelFor( menuOptionView.findViewById(R.id.icon), diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java index d8120ff255..f6e172aad0 100644 --- a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java +++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java @@ -19,8 +19,8 @@ package com.android.quickstep.views; 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; @@ -36,12 +36,13 @@ import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Shader; +import android.graphics.drawable.Drawable; import android.os.Build; import android.util.AttributeSet; import android.util.FloatProperty; import android.util.Property; -import android.view.Surface; import android.view.View; +import android.widget.ImageView; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; @@ -50,12 +51,15 @@ import androidx.core.graphics.ColorUtils; import com.android.launcher3.BaseActivity; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Utilities; +import com.android.launcher3.touch.PagedOrientationHandler; import com.android.launcher3.util.MainThreadInitializedObject; import com.android.launcher3.util.SystemUiController; +import com.android.launcher3.util.SystemUiController.SystemUiControllerFlags; import com.android.quickstep.TaskOverlayFactory.TaskOverlay; 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. @@ -82,6 +86,7 @@ public class TaskThumbnailView extends View { private TaskOverlay mOverlay; private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); private final Paint mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Paint mSplashBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); private final Paint mClearPaint = new Paint(); private final Paint mDimmingPaintAfterClearing = new Paint(); private final int mDimColor; @@ -90,6 +95,8 @@ public class TaskThumbnailView extends View { private final Rect mPreviewRect = new Rect(); private final PreviewPositionHelper mPreviewPositionHelper = new PreviewPositionHelper(); private TaskView.FullscreenDrawParams mFullscreenParams; + private ImageView mSplashView; + private Drawable mSplashViewDrawable; @Nullable private Task mTask; @@ -100,6 +107,8 @@ public class TaskThumbnailView extends View { /** How much this thumbnail is dimmed, 0 not dimmed at all, 1 totally dimmed. */ private float mDimAlpha = 0f; + /** Controls visibility of the splash view, 0 is transparent, 255 fully opaque. */ + private int mSplashAlpha = 0; private boolean mOverlayEnabled; @@ -115,6 +124,7 @@ public class TaskThumbnailView extends View { super(context, attrs, defStyleAttr); mPaint.setFilterBitmap(true); mBackgroundPaint.setColor(Color.WHITE); + mSplashBackgroundPaint.setColor(Color.WHITE); mClearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); mActivity = BaseActivity.fromContext(context); // Initialize with placeholder value. It is overridden later by TaskView @@ -134,6 +144,8 @@ public class TaskThumbnailView extends View { int color = task == null ? Color.BLACK : task.colorBackground | 0xFF000000; mPaint.setColor(color); mBackgroundPaint.setColor(color); + mSplashBackgroundPaint.setColor(color); + updateSplashView(mTask.icon); } /** @@ -151,6 +163,9 @@ public class TaskThumbnailView extends View { boolean thumbnailWasNull = mThumbnailData == null; mThumbnailData = (thumbnailData != null && thumbnailData.thumbnail != null) ? thumbnailData : null; + if (mTask != null) { + updateSplashView(mTask.icon); + } if (refreshNow) { refresh(thumbnailWasNull && mThumbnailData != null); } @@ -201,6 +216,18 @@ public class TaskThumbnailView extends View { updateThumbnailPaintFilter(); } + /** + * Sets the alpha of the splash view. + */ + public void setSplashAlpha(float splashAlpha) { + mSplashAlpha = (int) (Utilities.boundToRange(splashAlpha, 0f, 1f) * 255); + if (mSplashViewDrawable != null) { + mSplashViewDrawable.setAlpha(mSplashAlpha); + } + mSplashBackgroundPaint.setAlpha(mSplashAlpha); + invalidate(); + } + public TaskOverlay getTaskOverlay() { if (mOverlay == null) { mOverlay = getTaskView().getRecentsView().getTaskOverlayFactory().createOverlay(this); @@ -237,16 +264,13 @@ public class TaskThumbnailView extends View { boundsToBitmapSpace.mapRect(boundsInBitmapSpace, viewRect); DeviceProfile dp = mActivity.getDeviceProfile(); - int leftInset = TaskView.clipLeft(dp) ? Math.round(boundsInBitmapSpace.left) : 0; - int topInset = TaskView.clipTop(dp) ? Math.round(boundsInBitmapSpace.top) : 0; - int rightInset = TaskView.clipRight(dp) ? Math.round( - bitmapRect.right - boundsInBitmapSpace.right) : 0; - int bottomInset = TaskView.clipBottom(dp) + int bottomInset = dp.isTablet ? Math.round(bitmapRect.bottom - boundsInBitmapSpace.bottom) : 0; - return Insets.of(leftInset, topInset, rightInset, bottomInset); + return Insets.of(0, 0, 0, bottomInset); } + @SystemUiControllerFlags public int getSysUiStatusNavFlags() { if (mThumbnailData != null) { int flags = 0; @@ -262,6 +286,12 @@ public class TaskThumbnailView extends View { } @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + updateSplashView(mSplashViewDrawable); + } + + @Override protected void onDraw(Canvas canvas) { RectF currentDrawnInsets = mFullscreenParams.mCurrentDrawnInsets; canvas.save(); @@ -289,13 +319,11 @@ public class TaskThumbnailView extends View { 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 @@ -311,6 +339,17 @@ public class TaskThumbnailView extends View { } canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius, mPaint); + + // Draw splash above thumbnail to hide inconsistencies in rotation and aspect ratios. + if (shouldShowSplashView()) { + if (mSplashView != null) { + canvas.drawRoundRect(x, y, width + 1, height + 1, cornerRadius, + cornerRadius, mSplashBackgroundPaint); + + mSplashView.layout((int) x, (int) (y + 1), (int) width, (int) height - 1); + mSplashView.draw(canvas); + } + } } public TaskView getTaskView() { @@ -326,13 +365,87 @@ public class TaskThumbnailView extends View { } /** + * Determine if the splash should be shown over top of the thumbnail. + * + * <p>We want to show the splash if the aspect ratio or rotation of the thumbnail would be + * different from the task. + */ + public boolean shouldShowSplashView() { + return isThumbnailAspectRatioDifferentFromThumbnailData() + || isThumbnailRotationDifferentFromTask(); + } + + private void updateSplashView(Drawable icon) { + if (icon == null || icon.getConstantState() == null) { + return; + } + mSplashViewDrawable = icon.getConstantState().newDrawable().mutate(); + mSplashViewDrawable.setAlpha(mSplashAlpha); + ImageView imageView = mSplashView == null ? new ImageView(getContext()) : mSplashView; + imageView.setImageDrawable(mSplashViewDrawable); + + imageView.setScaleType(ImageView.ScaleType.MATRIX); + Matrix matrix = new Matrix(); + + float drawableWidth = mSplashViewDrawable.getIntrinsicWidth(); + float drawableHeight = mSplashViewDrawable.getIntrinsicHeight(); + float viewWidth = getMeasuredWidth(); + float viewCenterX = viewWidth / 2f; + float viewHeight = getMeasuredHeight(); + float viewCenterY = viewHeight / 2f; + float centeredDrawableLeft = (viewWidth - drawableWidth) / 2f; + float centeredDrawableTop = (viewHeight - drawableHeight) / 2f; + float nonGridScale = getTaskView() == null ? 1 : 1 / getTaskView().getNonGridScale(); + float recentsMaxScale = getTaskView() == null || getTaskView().getRecentsView() == null + ? 1 : 1 / getTaskView().getRecentsView().getMaxScaleForFullScreen(); + float scale = nonGridScale * recentsMaxScale; + + // Center the image in the view. + matrix.setTranslate(centeredDrawableLeft, centeredDrawableTop); + // Apply scale transformation after translation, pivoting around center of view. + matrix.postScale(scale, scale, viewCenterX, viewCenterY); + + imageView.setImageMatrix(matrix); + mSplashView = imageView; + } + + private boolean isThumbnailAspectRatioDifferentFromThumbnailData() { + if (mThumbnailData == null || mThumbnailData.thumbnail == null) { + return false; + } + + 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 isRelativePercentDifferenceGreaterThan(thumbnailViewAspect, + thumbnailDataAspect, MAX_PCT_BEFORE_ASPECT_RATIOS_CONSIDERED_DIFFERENT); + } + + private boolean isThumbnailRotationDifferentFromTask() { + RecentsView recents = getTaskView().getRecentsView(); + if (recents == null || mThumbnailData == null) { + return false; + } + + if (recents.getPagedOrientationHandler() == PagedOrientationHandler.PORTRAIT) { + int currentRotation = recents.getPagedViewOrientedState().getRecentsActivityRotation(); + return (currentRotation - mThumbnailData.rotation) % 2 != 0; + } else { + return recents.getPagedOrientationHandler().getRotation() != mThumbnailData.rotation; + } + } + + /** * Potentially re-init the task overlay. Be cautious when calling this as the overlay may * do processing on initialization. */ private void refreshOverlay() { if (mOverlayEnabled) { - getTaskOverlay().initOverlay(mTask, mThumbnailData, mPreviewPositionHelper.mMatrix, - mPreviewPositionHelper.mIsOrientationChanged); + getTaskOverlay().initOverlay(mTask, mThumbnailData, mPreviewPositionHelper.getMatrix(), + mPreviewPositionHelper.isOrientationChanged()); } else { getTaskOverlay().reset(); } @@ -353,7 +466,8 @@ public class TaskThumbnailView extends View { } 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()); @@ -361,10 +475,10 @@ public class TaskThumbnailView extends View { .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); @@ -404,257 +518,4 @@ public class TaskThumbnailView extends View { } 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(); - if (TaskView.clipLeft(dp)) { - thumbnailClipHint.left = thumbnailData.insets.left; - } - if (TaskView.clipRight(dp)) { - thumbnailClipHint.right = thumbnailData.insets.right; - } - if (TaskView.clipTop(dp)) { - thumbnailClipHint.top = thumbnailData.insets.top; - } - if (TaskView.clipBottom(dp)) { - thumbnailClipHint.bottom = thumbnailData.insets.bottom; - } - - float scale = thumbnailData.scale; - final float thumbnailScale; - - // Landscape vs portrait change. - // Note: Disable rotation in grid layout. - boolean windowingModeSupportsRotation = !dp.isMultiWindowMode - && 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, 0.1f); - 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 - if (!TaskView.clipLeft(dp)) { - thumbnailClipHint.left = thumbnailData.letterboxInsets.left; - } - if (!TaskView.clipRight(dp)) { - thumbnailClipHint.right = thumbnailData.letterboxInsets.right; - } - if (!TaskView.clipTop(dp)) { - thumbnailClipHint.top = thumbnailData.letterboxInsets.top; - } - if (!TaskView.clipBottom(dp)) { - 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); - } - - Rect splitScreenInsets = dp.getInsets(); - if (!isRotated) { - // No Rotation - if (dp.isMultiWindowMode) { - mClippedInsets.offsetTo(splitScreenInsets.left * scale, - splitScreenInsets.top * scale); - } else { - mClippedInsets.offsetTo(thumbnailClipHint.left * scale, - thumbnailClipHint.top * scale); - } - mMatrix.setTranslate( - -thumbnailClipHint.left * scale, - -thumbnailClipHint.top * scale); - } else { - setThumbnailRotation(deltaRotate, thumbnailClipHint, scale, thumbnailBounds, dp); - } - - final float widthWithInsets; - final float heightWithInsets; - if (isOrientationDifferent) { - widthWithInsets = thumbnailBounds.height() * thumbnailScale; - heightWithInsets = thumbnailBounds.width() * thumbnailScale; - } else { - widthWithInsets = thumbnailBounds.width() * thumbnailScale; - heightWithInsets = thumbnailBounds.height() * thumbnailScale; - } - mClippedInsets.left *= thumbnailScale; - mClippedInsets.top *= thumbnailScale; - - if (dp.isMultiWindowMode) { - mClippedInsets.right = splitScreenInsets.right * scale * thumbnailScale; - mClippedInsets.bottom = splitScreenInsets.bottom * scale * thumbnailScale; - } else { - mClippedInsets.right = Math.max(0, - widthWithInsets - mClippedInsets.left - canvasWidth); - mClippedInsets.bottom = Math.max(0, - heightWithInsets - mClippedInsets.top - canvasHeight); - } - - 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, RectF thumbnailInsets, float scale, - Rect thumbnailPosition, DeviceProfile dp) { - float newLeftInset = 0; - float newTopInset = 0; - float translateX = 0; - float translateY = 0; - - mMatrix.setRotate(90 * deltaRotate); - switch (deltaRotate) { /* Counter-clockwise */ - case Surface.ROTATION_90: - newLeftInset = thumbnailInsets.bottom; - newTopInset = thumbnailInsets.left; - translateX = thumbnailPosition.height(); - break; - case Surface.ROTATION_270: - newLeftInset = thumbnailInsets.top; - newTopInset = thumbnailInsets.right; - translateY = thumbnailPosition.width(); - break; - case Surface.ROTATION_180: - newLeftInset = -thumbnailInsets.top; - newTopInset = -thumbnailInsets.left; - translateX = thumbnailPosition.width(); - translateY = thumbnailPosition.height(); - break; - } - mClippedInsets.offsetTo(newLeftInset * scale, newTopInset * scale); - mMatrix.postTranslate(translateX, translateY); - if (TaskView.useFullThumbnail(dp)) { - mMatrix.postTranslate(-mClippedInsets.left, -mClippedInsets.top); - } - } - - /** - * Insets to used for clipping the thumbnail (in case it is drawing outside its own space) - */ - public RectF getInsetsToDrawInFullscreen(DeviceProfile dp) { - return TaskView.useFullThumbnail(dp) ? 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 d58bb7c719..aa37fddb8b 100644 --- a/quickstep/src/com/android/quickstep/views/TaskView.java +++ b/quickstep/src/com/android/quickstep/views/TaskView.java @@ -19,19 +19,18 @@ package com.android.quickstep.views; import static android.view.Display.DEFAULT_DISPLAY; import static android.widget.Toast.LENGTH_SHORT; -import static com.android.launcher3.AbstractFloatingView.TYPE_TASK_MENU; -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; @@ -43,21 +42,21 @@ import android.annotation.IdRes; import android.app.ActivityOptions; import android.content.Context; import android.content.Intent; -import android.graphics.Outline; import android.graphics.PointF; import android.graphics.Rect; 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; -import android.view.ViewOutlineProvider; import android.view.accessibility.AccessibilityNodeInfo; import android.view.animation.Interpolator; import android.widget.FrameLayout; @@ -67,7 +66,6 @@ import androidx.annotation.IntDef; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.DeviceProfile; import com.android.launcher3.LauncherSettings; import com.android.launcher3.R; @@ -77,10 +75,11 @@ import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.popup.SystemShortcut; import com.android.launcher3.statemanager.StatefulActivity; import com.android.launcher3.testing.TestLogging; -import com.android.launcher3.testing.TestProtocol; +import com.android.launcher3.testing.shared.TestProtocol; 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; @@ -89,7 +88,6 @@ import com.android.launcher3.util.ViewPool.Reusable; import com.android.quickstep.RecentsModel; import com.android.quickstep.RemoteAnimationTargets; import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle; -import com.android.quickstep.SystemUiProxy; import com.android.quickstep.TaskIconCache; import com.android.quickstep.TaskOverlayFactory; import com.android.quickstep.TaskThumbnailCache; @@ -97,15 +95,14 @@ import com.android.quickstep.TaskUtils; 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; @@ -123,6 +120,8 @@ public class TaskView extends FrameLayout implements Reusable { 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; @@ -136,43 +135,19 @@ public class TaskView extends FrameLayout implements Reusable { @IntDef({FLAG_UPDATE_ALL, FLAG_UPDATE_ICON, FLAG_UPDATE_THUMBNAIL}) public @interface TaskDataChanges {} - /** The maximum amount that a task view can be scrimmed, dimmed or tinted. */ - public static final float MAX_PAGE_SCRIM_ALPHA = 0.4f; - - /** - * Should the TaskView display clip off the left inset in RecentsView. - */ - public static boolean clipLeft(DeviceProfile deviceProfile) { - return false; - } - - /** - * Should the TaskView display clip off the top inset in RecentsView. - */ - public static boolean clipTop(DeviceProfile deviceProfile) { - return false; - } - /** - * Should the TaskView display clip off the right inset in RecentsView. + * Type of task view */ - public static boolean clipRight(DeviceProfile deviceProfile) { - return false; - } - - /** - * Should the TaskView display clip off the bottom inset in RecentsView. - */ - public static boolean clipBottom(DeviceProfile deviceProfile) { - return deviceProfile.isTablet; + @Retention(SOURCE) + @IntDef({Type.SINGLE, Type.GROUPED, Type.DESKTOP}) + public @interface Type { + int SINGLE = 1; + int GROUPED = 2; + int DESKTOP = 3; } - /** - * Should the TaskView scale down to fit whole thumbnail in fullscreen. - */ - public static boolean useFullThumbnail(DeviceProfile deviceProfile) { - return deviceProfile.isTablet && !deviceProfile.isTaskbarPresentInApps; - } + /** The maximum amount that a task view can be scrimmed, dimmed or tinted. */ + public static final float MAX_PAGE_SCRIM_ALPHA = 0.4f; private static final float EDGE_SCALE_DOWN_FACTOR_CAROUSEL = 0.03f; private static final float EDGE_SCALE_DOWN_FACTOR_GRID = 0.00f; @@ -197,7 +172,7 @@ public class TaskView extends FrameLayout implements Reusable { new FloatProperty<TaskView>("focusTransition") { @Override public void setValue(TaskView taskView, float v) { - taskView.setIconAndDimTransitionProgress(v, false /* invert */); + taskView.setIconsAndBannersTransitionProgress(v, false /* invert */); } @Override @@ -362,15 +337,14 @@ public class TaskView extends FrameLayout implements Reusable { } }; - private final TaskOutlineProvider mOutlineProvider; - @Nullable protected Task mTask; protected TaskThumbnailView mSnapshotView; protected IconView mIconView; protected final DigitalWellBeingToast mDigitalWellBeingToast; - private float mFullscreenProgress; + protected float mFullscreenProgress; private float mGridProgress; + protected float mTaskThumbnailSplashAlpha; private float mNonGridScale = 1; private float mDismissScale = 1; protected final FullscreenDrawParams mCurrentFullscreenParams; @@ -410,8 +384,8 @@ public class TaskView extends FrameLayout implements Reusable { /** * 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; @@ -426,11 +400,10 @@ public class TaskView extends FrameLayout implements Reusable { private final float[] mIconCenterCoords = new float[2]; - private final PointF mLastTouchDownPosition = new PointF(); + protected final PointF mLastTouchDownPosition = new PointF(); private boolean mIsClickableAsLiveTile = true; - public TaskView(Context context) { this(context, null); } @@ -446,10 +419,6 @@ public class TaskView extends FrameLayout implements Reusable { mCurrentFullscreenParams = new FullscreenDrawParams(context); mDigitalWellBeingToast = new DigitalWellBeingToast(mActivity, this); - - mOutlineProvider = new TaskOutlineProvider(getContext(), mCurrentFullscreenParams, - mActivity.getDeviceProfile().overviewTaskThumbnailTopMarginPx); - setOutlineProvider(mOutlineProvider); } public void setTaskViewId(int id) { @@ -531,7 +500,7 @@ public class TaskView extends FrameLayout implements Reusable { return; } mModalness = modalness; - mIconView.setAlpha(comp(modalness)); + mIconView.setAlpha(1 - modalness); mDigitalWellBeingToast.updateBannerOffset(modalness, mCurrentFullscreenParams.mCurrentDrawnInsets.top + mCurrentFullscreenParams.mCurrentDrawnInsets.bottom); @@ -557,6 +526,52 @@ public class TaskView extends FrameLayout implements Reusable { 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; } @@ -567,6 +582,13 @@ public class TaskView extends FrameLayout implements Reusable { } /** + * 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 */ @@ -605,6 +627,18 @@ public class TaskView extends FrameLayout implements Reusable { @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()); } @@ -627,17 +661,21 @@ public class TaskView extends FrameLayout implements Reusable { * @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; } @@ -656,7 +694,7 @@ public class TaskView extends FrameLayout implements Reusable { 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 @@ -691,13 +729,14 @@ public class TaskView extends FrameLayout implements Reusable { 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(); } + opts.setDisableStartingWindow(mSnapshotView.shouldShowSplashView()); Task.TaskKey key = mTask.key; UI_HELPER_EXECUTOR.execute(() -> { if (!ActivityManagerWrapper.getInstance().startActivityFromRecents(key, opts)) { @@ -716,20 +755,32 @@ public class TaskView extends FrameLayout implements Reusable { } /** + * 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; } - // Reset the minimized state since we force-toggled the minimized state when entering - // overview, but never actually finished the recents animation - SystemUiProxy.INSTANCE.get(getContext()).setSplitScreenMinimized(false); - mIsClickableAsLiveTile = false; RemoteAnimationTargets targets; if (remoteTargetHandles.length == 1) { @@ -737,14 +788,14 @@ public class TaskView extends FrameLayout implements Reusable { } 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); @@ -752,11 +803,16 @@ public class TaskView extends FrameLayout implements Reusable { 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, @@ -779,12 +835,23 @@ public class TaskView extends FrameLayout implements Reusable { 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(); } } @@ -912,10 +979,9 @@ public class TaskView extends FrameLayout implements Reusable { 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); @@ -941,7 +1007,11 @@ public class TaskView extends FrameLayout implements Reusable { 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; } @@ -985,7 +1055,7 @@ public class TaskView extends FrameLayout implements Reusable { if (mIconAndDimAnimator != null) { mIconAndDimAnimator.cancel(); } - setIconAndDimTransitionProgress(iconScale, invert); + setIconsAndBannersTransitionProgress(iconScale, invert); } protected void resetPersistentViewTransforms() { @@ -1102,6 +1172,18 @@ public class TaskView extends FrameLayout implements Reusable { return scale; } + /** + * Updates alpha of task thumbnail splash on swipe up/down. + */ + public void setTaskThumbnailSplashAlpha(float taskThumbnailSplashAlpha) { + mTaskThumbnailSplashAlpha = taskThumbnailSplashAlpha; + applyThumbnailSplashAlpha(); + } + + protected void applyThumbnailSplashAlpha() { + mSnapshotView.setSplashAlpha(mTaskThumbnailSplashAlpha); + } + private void setSplitSelectTranslationX(float x) { mSplitSelectTranslationX = x; applyTranslationX(); @@ -1250,7 +1332,7 @@ public class TaskView extends FrameLayout implements Reusable { DISMISS_TRANSLATION_X, DISMISS_TRANSLATION_Y); } - public FloatProperty<TaskView> getSecondaryDissmissTranslationProperty() { + public FloatProperty<TaskView> getSecondaryDismissTranslationProperty() { return getPagedOrientationHandler().getSecondaryValue( DISMISS_TRANSLATION_X, DISMISS_TRANSLATION_Y); } @@ -1289,33 +1371,6 @@ public class TaskView extends FrameLayout implements Reusable { mEndQuickswitchCuj = endQuickswitchCuj; } - private static final class TaskOutlineProvider extends ViewOutlineProvider { - - private int mMarginTop; - private FullscreenDrawParams mFullscreenParams; - - TaskOutlineProvider(Context context, FullscreenDrawParams fullscreenParams, int topMargin) { - mMarginTop = topMargin; - mFullscreenParams = fullscreenParams; - } - - public void updateParams(FullscreenDrawParams params, int topMargin) { - mFullscreenParams = params; - mMarginTop = topMargin; - } - - @Override - public void getOutline(View view, Outline outline) { - RectF insets = mFullscreenParams.mCurrentDrawnInsets; - float scale = mFullscreenParams.mScale; - outline.setRoundRect(0, - (int) (mMarginTop * scale), - (int) ((insets.left + view.getWidth() + insets.right) * scale), - (int) ((insets.top + view.getHeight() + insets.bottom) * scale), - mFullscreenParams.mCurrentDrawnCornerRadius); - } - } - private int getExpectedViewHeight(View view) { int expectedHeight; int h = view.getLayoutParams().height; @@ -1343,7 +1398,7 @@ public class TaskView extends FrameLayout implements Reusable { continue; } for (SystemShortcut s : TaskOverlayFactory.getEnabledShortcuts(this, - mActivity.getDeviceProfile(), taskContainer)) { + taskContainer)) { info.addAction(s.createAccessibilityAction(context)); } } @@ -1381,7 +1436,7 @@ public class TaskView extends FrameLayout implements Reusable { continue; } for (SystemShortcut s : TaskOverlayFactory.getEnabledShortcuts(this, - mActivity.getDeviceProfile(), taskContainer)) { + taskContainer)) { if (s.hasHandlerForAction(action)) { s.onClick(this); return true; @@ -1420,12 +1475,13 @@ public class TaskView extends FrameLayout implements Reusable { mIconView.setVisibility(progress < 1 ? VISIBLE : INVISIBLE); mSnapshotView.getTaskOverlay().setFullscreenProgress(progress); - updateSnapshotRadius(); + // 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); + } - mOutlineProvider.updateParams( - mCurrentFullscreenParams, - mActivity.getDeviceProfile().overviewTaskThumbnailTopMarginPx); - invalidateOutline(); + updateSnapshotRadius(); } protected void updateSnapshotRadius() { @@ -1537,8 +1593,8 @@ public class TaskView extends FrameLayout implements Reusable { } public void initiateSplitSelect(SplitPositionOption splitPositionOption) { - AbstractFloatingView.closeOpenViews(mActivity, false, TYPE_TASK_MENU); - getRecentsView().initiateSplitSelect(this, splitPositionOption.stagePosition); + getRecentsView().initiateSplitSelect(this, splitPositionOption.stagePosition, + getLogEventForPosition(splitPositionOption.stagePosition)); } /** @@ -1557,6 +1613,19 @@ public class TaskView extends FrameLayout implements Reusable { } /** + * 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 { @@ -1569,9 +1638,12 @@ public class TaskView extends FrameLayout implements Reusable { /** 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; } @@ -1581,20 +1653,17 @@ public class TaskView extends FrameLayout implements Reusable { */ 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; float currentInsetsRight = insets.right * fullscreenProgress; - float insetsBottom = insets.bottom; - if (dp.isTaskbarPresentInApps) { - insetsBottom = Math.max(0, insetsBottom - dp.taskbarSize); - } - mCurrentDrawnInsets.set(currentInsetsLeft, insets.top * fullscreenProgress, - currentInsetsRight, insetsBottom * fullscreenProgress); - float fullscreenCornerRadius = dp.isMultiWindowMode ? 0 : mWindowCornerRadius; + float currentInsetsBottom = insets.bottom * fullscreenProgress; + mCurrentDrawnInsets.set( + currentInsetsLeft, currentInsetsTop, currentInsetsRight, currentInsetsBottom); mCurrentDrawnCornerRadius = - Utilities.mapRange(fullscreenProgress, mCornerRadius, fullscreenCornerRadius) + Utilities.mapRange(fullscreenProgress, mCornerRadius, mWindowCornerRadius) / parentScale / taskViewScale; // We scaled the thumbnail to fit the content (excluding insets) within task view width. @@ -1603,6 +1672,18 @@ public class TaskView extends FrameLayout implements Reusable { 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 c1b3beb475..83341cb868 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.appwidget.AppWidgetProviderInfo; 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 @@ public final class WidgetsPredicationUpdateTaskTest { 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 final class WidgetsPredicationUpdateTaskTest { 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 @@ public final class WidgetsPredicationUpdateTaskTest { .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/TaskbarNavButtonControllerTest.java b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java index d8be30728e..962261940c 100644 --- a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java +++ b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java @@ -18,11 +18,13 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_S 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; import android.os.Handler; +import android.view.View; import androidx.test.runner.AndroidJUnit4; @@ -58,6 +60,8 @@ public class TaskbarNavButtonControllerTest { TaskbarControllers mockTaskbarControllers; @Mock TaskbarActivityContext mockTaskbarActivityContext; + @Mock + View mockView; private TaskbarNavButtonController mNavButtonController; @@ -76,110 +80,118 @@ public class TaskbarNavButtonControllerTest { @Test public void testPressBack() { - mNavButtonController.onButtonClick(BUTTON_BACK); + mNavButtonController.onButtonClick(BUTTON_BACK, mockView); verify(mockSystemUiProxy, times(1)).onBackPressed(); } @Test public void testPressImeSwitcher() { - mNavButtonController.onButtonClick(BUTTON_IME_SWITCH); + mNavButtonController.onButtonClick(BUTTON_IME_SWITCH, mockView); verify(mockSystemUiProxy, times(1)).onImeSwitcherPressed(); } @Test public void testPressA11yShortClick() { - mNavButtonController.onButtonClick(BUTTON_A11Y); + mNavButtonController.onButtonClick(BUTTON_A11Y, mockView); verify(mockSystemUiProxy, times(1)) .notifyAccessibilityButtonClicked(DISPLAY_ID); } @Test public void testPressA11yLongClick() { - mNavButtonController.onButtonLongClick(BUTTON_A11Y); + mNavButtonController.onButtonLongClick(BUTTON_A11Y, mockView); verify(mockSystemUiProxy, times(1)).notifyAccessibilityButtonLongClicked(); } @Test - public void testLongPressHome() { - mNavButtonController.onButtonLongClick(BUTTON_HOME); + 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); + mNavButtonController.onButtonClick(BUTTON_HOME, mockView); verify(mockCommandHelper, times(1)).addCommand(TYPE_HOME); } @Test public void testPressRecents() { - mNavButtonController.onButtonClick(BUTTON_RECENTS); + mNavButtonController.onButtonClick(BUTTON_RECENTS, mockView); verify(mockCommandHelper, times(1)).addCommand(TYPE_TOGGLE); } @Test public void testPressRecentsWithScreenPinned() { mNavButtonController.updateSysuiFlags(SYSUI_STATE_SCREEN_PINNING); - mNavButtonController.onButtonClick(BUTTON_RECENTS); + mNavButtonController.onButtonClick(BUTTON_RECENTS, mockView); verify(mockCommandHelper, times(0)).addCommand(TYPE_TOGGLE); } @Test public void testLongPressBackRecentsNotPinned() { - mNavButtonController.onButtonLongClick(BUTTON_RECENTS); - mNavButtonController.onButtonLongClick(BUTTON_BACK); + mNavButtonController.onButtonLongClick(BUTTON_RECENTS, mockView); + mNavButtonController.onButtonLongClick(BUTTON_BACK, mockView); verify(mockSystemUiProxy, times(0)).stopScreenPinning(); } @Test public void testLongPressBackRecentsPinned() { mNavButtonController.updateSysuiFlags(SYSUI_STATE_SCREEN_PINNING); - mNavButtonController.onButtonLongClick(BUTTON_RECENTS); - mNavButtonController.onButtonLongClick(BUTTON_BACK); + mNavButtonController.onButtonLongClick(BUTTON_RECENTS, mockView); + mNavButtonController.onButtonLongClick(BUTTON_BACK, mockView); verify(mockSystemUiProxy, times(1)).stopScreenPinning(); } @Test public void testLongPressBackRecentsTooLongPinned() { mNavButtonController.updateSysuiFlags(SYSUI_STATE_SCREEN_PINNING); - mNavButtonController.onButtonLongClick(BUTTON_RECENTS); + mNavButtonController.onButtonLongClick(BUTTON_RECENTS, mockView); try { Thread.sleep(SCREEN_PIN_LONG_PRESS_THRESHOLD + 5); } catch (InterruptedException e) { e.printStackTrace(); } - mNavButtonController.onButtonLongClick(BUTTON_BACK); + mNavButtonController.onButtonLongClick(BUTTON_BACK, mockView); verify(mockSystemUiProxy, times(0)).stopScreenPinning(); } @Test public void testLongPressBackRecentsMultipleAttemptPinned() { mNavButtonController.updateSysuiFlags(SYSUI_STATE_SCREEN_PINNING); - mNavButtonController.onButtonLongClick(BUTTON_RECENTS); + mNavButtonController.onButtonLongClick(BUTTON_RECENTS, mockView); try { Thread.sleep(SCREEN_PIN_LONG_PRESS_THRESHOLD + 5); } catch (InterruptedException e) { e.printStackTrace(); } - mNavButtonController.onButtonLongClick(BUTTON_BACK); + mNavButtonController.onButtonLongClick(BUTTON_BACK, mockView); verify(mockSystemUiProxy, times(0)).stopScreenPinning(); // Try again w/in threshold - mNavButtonController.onButtonLongClick(BUTTON_RECENTS); - mNavButtonController.onButtonLongClick(BUTTON_BACK); + mNavButtonController.onButtonLongClick(BUTTON_RECENTS, mockView); + mNavButtonController.onButtonLongClick(BUTTON_BACK, mockView); verify(mockSystemUiProxy, times(1)).stopScreenPinning(); } @Test public void testLongPressHomeScreenPinned() { mNavButtonController.updateSysuiFlags(SYSUI_STATE_SCREEN_PINNING); - mNavButtonController.onButtonLongClick(BUTTON_HOME); + mNavButtonController.onButtonLongClick(BUTTON_HOME, mockView); verify(mockSystemUiProxy, times(0)).startAssistant(any()); } @Test public void testNoCallsToNullLogger() { - mNavButtonController.onButtonClick(BUTTON_HOME); + mNavButtonController.onButtonClick(BUTTON_HOME, mockView); verify(mockStatsLogManager, times(0)).logger(); verify(mockStatsLogger, times(0)).log(any()); } @@ -187,9 +199,9 @@ public class TaskbarNavButtonControllerTest { @Test public void testNoCallsAfterNullingOut() { mNavButtonController.init(mockTaskbarControllers); - mNavButtonController.onButtonClick(BUTTON_HOME); + mNavButtonController.onButtonClick(BUTTON_HOME, mockView); mNavButtonController.onDestroy(); - mNavButtonController.onButtonClick(BUTTON_HOME); + mNavButtonController.onButtonClick(BUTTON_HOME, mockView); verify(mockStatsLogger, times(1)).log(LAUNCHER_TASKBAR_HOME_BUTTON_TAP); verify(mockStatsLogger, times(0)).log(LAUNCHER_TASKBAR_HOME_BUTTON_LONGPRESS); } @@ -197,7 +209,7 @@ public class TaskbarNavButtonControllerTest { @Test public void testLogOnTap() { mNavButtonController.init(mockTaskbarControllers); - mNavButtonController.onButtonClick(BUTTON_HOME); + mNavButtonController.onButtonClick(BUTTON_HOME, mockView); verify(mockStatsLogger, times(1)).log(LAUNCHER_TASKBAR_HOME_BUTTON_TAP); verify(mockStatsLogger, times(0)).log(LAUNCHER_TASKBAR_HOME_BUTTON_LONGPRESS); } @@ -205,7 +217,7 @@ public class TaskbarNavButtonControllerTest { @Test public void testLogOnLongpress() { mNavButtonController.init(mockTaskbarControllers); - mNavButtonController.onButtonLongClick(BUTTON_HOME); + mNavButtonController.onButtonLongClick(BUTTON_HOME, mockView); verify(mockStatsLogger, times(1)).log(LAUNCHER_TASKBAR_HOME_BUTTON_LONGPRESS); verify(mockStatsLogger, times(0)).log(LAUNCHER_TASKBAR_HOME_BUTTON_TAP); } @@ -213,11 +225,11 @@ public class TaskbarNavButtonControllerTest { @Test public void testBackOverviewLogOnLongpress() { mNavButtonController.init(mockTaskbarControllers); - mNavButtonController.onButtonLongClick(BUTTON_RECENTS); + mNavButtonController.onButtonLongClick(BUTTON_RECENTS, mockView); verify(mockStatsLogger, times(1)).log(LAUNCHER_TASKBAR_OVERVIEW_BUTTON_LONGPRESS); verify(mockStatsLogger, times(0)).log(LAUNCHER_TASKBAR_OVERVIEW_BUTTON_TAP); - mNavButtonController.onButtonLongClick(BUTTON_BACK); + mNavButtonController.onButtonLongClick(BUTTON_BACK, mockView); verify(mockStatsLogger, times(1)).log(LAUNCHER_TASKBAR_BACK_BUTTON_LONGPRESS); verify(mockStatsLogger, times(0)).log(LAUNCHER_TASKBAR_BACK_BUTTON_TAP); } 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 0000000000..58f0949213 --- /dev/null +++ b/quickstep/tests/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactoryTest.kt @@ -0,0 +1,148 @@ +package com.android.launcher3.taskbar.navbutton + +import android.content.res.Resources +import android.view.View +import android.view.ViewGroup +import android.widget.FrameLayout +import android.widget.ImageView +import android.widget.LinearLayout +import androidx.test.runner.AndroidJUnit4 +import com.android.launcher3.DeviceProfile +import com.android.launcher3.taskbar.TaskbarManager +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import com.android.launcher3.R +import org.junit.Assume.assumeTrue +import org.mockito.Mockito.`when` as whenever +import org.mockito.MockitoAnnotations +import java.lang.IllegalStateException + +@RunWith(AndroidJUnit4::class) +class NavButtonLayoutFactoryTest { + + @Mock + lateinit var mockDeviceProfile: DeviceProfile + @Mock + lateinit var mockParentButtonContainer: FrameLayout + @Mock + lateinit var mockNavLayout: LinearLayout + @Mock + lateinit var mockStartContextualLayout: ViewGroup + @Mock + lateinit var mockEndContextualLayout: ViewGroup + @Mock + lateinit var mockResources: Resources + @Mock + lateinit var mockBackButton: ImageView + @Mock + lateinit var mockRecentsButton: ImageView + @Mock + lateinit var mockHomeButton: ImageView + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + + // Init end nav buttons + whenever(mockNavLayout.childCount).thenReturn(3) + whenever(mockNavLayout.findViewById<View>(R.id.back)).thenReturn(mockBackButton) + whenever(mockNavLayout.findViewById<View>(R.id.home)).thenReturn(mockHomeButton) + whenever(mockNavLayout.findViewById<View>(R.id.recent_apps)).thenReturn(mockRecentsButton) + + // Init top level layout + whenever(mockParentButtonContainer.findViewById<LinearLayout>(R.id.end_nav_buttons)) + .thenReturn(mockNavLayout) + whenever(mockParentButtonContainer.findViewById<ViewGroup>(R.id.end_contextual_buttons)) + .thenReturn(mockEndContextualLayout) + whenever(mockParentButtonContainer.findViewById<ViewGroup>(R.id.start_contextual_buttons)) + .thenReturn(mockStartContextualLayout) + } + + @Test + fun getKidsLayoutter() { + assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW) + mockDeviceProfile.isTaskbarPresent = true + val layoutter: NavButtonLayoutFactory.NavButtonLayoutter = + getLayoutter(isKidsMode = true, isInSetup = false, isThreeButtonNav = false, + phoneMode = false) + assert(layoutter is KidsNavLayoutter) + } + + @Test + fun getSetupLayoutter() { + assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW) + mockDeviceProfile.isTaskbarPresent = true + val layoutter: NavButtonLayoutFactory.NavButtonLayoutter = + getLayoutter(isKidsMode = false, isInSetup = true, isThreeButtonNav = false, + phoneMode = false) + assert(layoutter is SetupNavLayoutter) + } + + @Test + fun getTaskbarNavLayoutter() { + assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW) + mockDeviceProfile.isTaskbarPresent = true + val layoutter: NavButtonLayoutFactory.NavButtonLayoutter = + getLayoutter(isKidsMode = false, isInSetup = false, isThreeButtonNav = false, + phoneMode = false) + assert(layoutter is TaskbarNavLayoutter) + } + + @Test(expected = IllegalStateException::class) + fun noValidLayoutForLargeScreenTaskbarNotPresent() { + assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW) + mockDeviceProfile.isTaskbarPresent = false + getLayoutter(isKidsMode = false, isInSetup = false, isThreeButtonNav = false, + phoneMode = false) + } + + @Test + fun getTaskbarPortraitLayoutter() { + assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW) + mockDeviceProfile.isTaskbarPresent = false + val layoutter: NavButtonLayoutFactory.NavButtonLayoutter = + getLayoutter(isKidsMode = false, isInSetup = false, isThreeButtonNav = true, + phoneMode = true) + assert(layoutter is PhonePortraitNavLayoutter) + } + + @Test + fun getTaskbarLandscapeLayoutter() { + assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW) + mockDeviceProfile.isTaskbarPresent = false + setDeviceProfileLandscape() + val layoutter: NavButtonLayoutFactory.NavButtonLayoutter = + getLayoutter(isKidsMode = false, isInSetup = false, isThreeButtonNav = true, + phoneMode = true) + assert(layoutter is PhoneLandscapeNavLayoutter) + } + + @Test(expected = IllegalStateException::class) + fun noValidLayoutForPhoneGestureNav() { + assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW) + mockDeviceProfile.isTaskbarPresent = false + getLayoutter(isKidsMode = false, isInSetup = false, isThreeButtonNav = false, + phoneMode = true) + } + + private fun setDeviceProfileLandscape() { + // Use reflection to modify landscape field + val landscapeField = mockDeviceProfile.javaClass.getDeclaredField("isLandscape") + landscapeField.isAccessible = true + landscapeField.set(mockDeviceProfile, true) + } + + private fun getLayoutter(isKidsMode: Boolean, isInSetup: Boolean, + isThreeButtonNav: Boolean, phoneMode: Boolean): + NavButtonLayoutFactory.NavButtonLayoutter { + return NavButtonLayoutFactory.getUiLayoutter( + deviceProfile = mockDeviceProfile, + navButtonsView = mockParentButtonContainer, + resources = mockResources, + isKidsMode = isKidsMode, isInSetup = isInSetup, + isThreeButtonNav = isThreeButtonNav, phoneMode = phoneMode + ) + } +}
\ No newline at end of file diff --git a/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java b/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java index 09f0858618..2c5825fd0b 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; @@ -35,12 +33,13 @@ import org.junit.rules.TestRule; * Base class for all instrumentation tests that deal with Quickstep. */ public abstract class AbstractQuickStepTest extends AbstractLauncherUiTest { - static final boolean ENABLE_SHELL_TRANSITIONS = + public static final boolean ENABLE_SHELL_TRANSITIONS = SystemProperties.getBoolean("persist.wm.debug.shell_transit", false); @Override protected TestRule getRulesInsideActivityMonitor() { return RuleChain. outerRule(new NavigationModeSwitchRule(mLauncher)). + around(new TaskbarModeSwitchRule(mLauncher)). around(super.getRulesInsideActivityMonitor()); } @@ -79,8 +78,7 @@ public abstract class AbstractQuickStepTest extends AbstractLauncherUiTest { 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/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java index f7600ff9bb..47bef7b5b2 100644 --- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java +++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java @@ -63,6 +63,7 @@ import com.android.quickstep.views.RecentsView; 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; @@ -167,6 +168,7 @@ public class FallbackRecentsTest { // b/143488140 //@NavigationModeSwitch + @Ignore @Test public void goToOverviewFromHome() { mDevice.pressHome(); @@ -178,6 +180,7 @@ public class FallbackRecentsTest { // b/143488140 //@NavigationModeSwitch + @Ignore @Test public void goToOverviewFromApp() { startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR)); @@ -213,6 +216,7 @@ public class FallbackRecentsTest { // b/143488140 //@NavigationModeSwitch + @Ignore @Test public void testOverview() { startAppFast(getAppPackageName()); 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 0000000000..5ec935fee9 --- /dev/null +++ b/quickstep/tests/src/com/android/quickstep/FullscreenDrawParamsTest.kt @@ -0,0 +1,181 @@ +/* + * 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 org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.mock +import kotlin.math.roundToInt + +/** + * Test for FullscreenDrawParams class. + */ +@SmallTest +@RunWith(AndroidJUnit4::class) +class FullscreenDrawParamsTest : DeviceProfileBaseTest() { + + private val TASK_SCALE = 0.7f + private var mThumbnailData: ThumbnailData = mock(ThumbnailData::class.java) + + private val mPreviewPositionHelper = PreviewPositionHelper() + private lateinit var params: FullscreenDrawParams + + @Before + fun setup() { + params = FullscreenDrawParams(context) + } + + @Test + fun setFullProgress_currentDrawnInsets_clipTaskbarSizeFromBottomForTablets() { + initializeVarsForTablet() + val dp = newDP() + val previewRect = Rect(0, 0, 100, 100) + val canvasWidth = (dp.widthPx * TASK_SCALE).roundToInt() + val canvasHeight = (dp.heightPx * TASK_SCALE).roundToInt() + val currentRotation = 0 + val isRtl = false + + mPreviewPositionHelper.updateThumbnailMatrix(previewRect, mThumbnailData, canvasWidth, + canvasHeight, dp.widthPx, dp.heightPx, dp.taskbarSize, dp.isTablet, currentRotation, + isRtl) + params.setProgress(/* fullscreenProgress= */ 1.0f, /* parentScale= */ 1.0f, + /* taskViewScale= */ 1.0f, /* previewWidth= */ 0, dp, mPreviewPositionHelper) + + val expectedClippedInsets = RectF(0f, 0f, 0f, dp.taskbarSize * TASK_SCALE) + assertThat(params.mCurrentDrawnInsets) + .isEqualTo(expectedClippedInsets) + } + + @Test + fun setFullProgress_currentDrawnInsets_clipTaskbarSizeFromBottomForTablets_splitPortrait() { + initializeVarsForTablet() + val dp = newDP() + val previewRect = Rect(0, 0, 100, 100) + val canvasWidth = (dp.widthPx * TASK_SCALE).roundToInt() + val canvasHeight = (dp.heightPx * TASK_SCALE / 2).roundToInt() + val currentRotation = 0 + val isRtl = false + // portrait/vertical split apps + val dividerSize = 10 + val splitBounds = SplitBounds( + Rect(0, 0, dp.widthPx, (dp.heightPx - dividerSize) / 2), + Rect(0, (dp.heightPx + dividerSize) / 2, dp.widthPx, dp.heightPx), + 0 /*lefTopTaskId*/, 0 /*rightBottomTaskId*/) + mPreviewPositionHelper.setSplitBounds(splitBounds, STAGE_POSITION_BOTTOM_OR_RIGHT) + + mPreviewPositionHelper.updateThumbnailMatrix(previewRect, mThumbnailData, canvasWidth, + canvasHeight, dp.widthPx, dp.heightPx, dp.taskbarSize, dp.isTablet, currentRotation, + isRtl) + params.setProgress(/* fullscreenProgress= */ 1.0f, /* parentScale= */ 1.0f, + /* taskViewScale= */ 1.0f, /* previewWidth= */ 0, dp, mPreviewPositionHelper) + + // 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) + } +}
\ No newline at end of file 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 0000000000..4837c6c868 --- /dev/null +++ b/quickstep/tests/src/com/android/quickstep/HotseatWidthCalculationTest.kt @@ -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.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 e5e2cf3337..eded1c961f 100644 --- a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java +++ b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java @@ -51,7 +51,7 @@ import java.util.concurrent.TimeUnit; /** * 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 9e5d9585af..9c240f08c7 100644 --- a/quickstep/tests/src/com/android/quickstep/OrientationTouchTransformerTest.java +++ b/quickstep/tests/src/com/android/quickstep/OrientationTouchTransformerTest.java @@ -19,7 +19,7 @@ package com.android.quickstep; 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; @@ -35,14 +35,13 @@ import android.graphics.Rect; import android.util.ArrayMap; import android.util.DisplayMetrics; import android.util.Size; -import android.view.Display; import android.view.MotionEvent; import android.view.Surface; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import com.android.launcher3.ResourceUtils; +import com.android.launcher3.testing.shared.ResourceUtils; import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.RotationUtils; import com.android.launcher3.util.WindowBounds; @@ -290,15 +289,17 @@ public class OrientationTouchTransformerTest { private DisplayController.Info createDisplayInfo(Size screenSize, int rotation) { Point displaySize = new Point(screenSize.getWidth(), screenSize.getHeight()); RotationUtils.rotateSize(displaySize, rotation); - CachedDisplayInfo cdi = new CachedDisplayInfo(displaySize, rotation); - WindowBounds wm = new WindowBounds( + CachedDisplayInfo cachedDisplayInfo = new CachedDisplayInfo(displaySize, rotation); + WindowBounds windowBounds = new WindowBounds( new Rect(0, 0, displaySize.x, displaySize.y), new Rect()); WindowManagerProxy wmProxy = mock(WindowManagerProxy.class); - doReturn(cdi).when(wmProxy).getDisplayInfo(any(), any()); - doReturn(wm).when(wmProxy).getRealBounds(any(), any(), any()); + doReturn(cachedDisplayInfo).when(wmProxy).getDisplayInfo(any()); + doReturn(windowBounds).when(wmProxy).getRealBounds(any(), any()); + ArrayMap<CachedDisplayInfo, WindowBounds[]> internalDisplayBounds = new ArrayMap<>(); + doReturn(internalDisplayBounds).when(wmProxy).estimateInternalDisplayBounds(any()); return new DisplayController.Info( - getApplicationContext(), mock(Display.class), wmProxy, new ArrayMap<>()); + getApplicationContext(), wmProxy, new ArrayMap<>()); } private float generateTouchRegionHeight(Size screenSize, int rotation) { diff --git a/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java b/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java index 4e497163b5..ed5526f410 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.verify; 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 class RecentTasksListTest { 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 @@ public class RecentTasksListTest { @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 @@ public class RecentTasksListTest { 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 6ec62695af..df5303f4f4 100644 --- a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java +++ b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java @@ -16,8 +16,6 @@ package com.android.quickstep; -import android.content.Intent; - import androidx.test.filters.LargeTest; import androidx.test.runner.AndroidJUnit4; @@ -26,6 +24,7 @@ import com.android.launcher3.util.RaceConditionReproducer; import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; @@ -43,7 +42,7 @@ public class StartLauncherViaGestureTests extends AbstractQuickStepTest { // b/143488140 mLauncher.goHome(); // Start an activity where the gestures start. - startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR)); + startTestActivity(2); } private void runTest(String... eventSequence) { @@ -58,6 +57,7 @@ public class StartLauncherViaGestureTests extends AbstractQuickStepTest { eventProcessor.finishIteration(); } + @Ignore @Test @NavigationModeSwitch public void testStressPressHome() { @@ -70,6 +70,7 @@ public class StartLauncherViaGestureTests extends AbstractQuickStepTest { } } + @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 4bf247c75d..9f34775761 100644 --- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java +++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java @@ -44,6 +44,7 @@ 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; @@ -179,6 +180,35 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { actionsView.clickAndDismissScreenshot(); } + @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()); + + startTestActivity(2); + startTestActivity(3); + + mLauncher.goHome().switchToOverview().getOverviewActions() + .clickSplit() + .getTestActivityTask(2) + .open(); + } + private int getCurrentOverviewPage(Launcher launcher) { return launcher.<RecentsView>getOverviewPanel().getCurrentPage(); } @@ -195,16 +225,20 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { 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 @@ -228,6 +262,19 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { 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 { @@ -254,13 +301,11 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { 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.", @@ -269,15 +314,39 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { 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)), @@ -310,6 +379,7 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL); } + @Ignore @Test @PortraitLandscape public void testOverviewForTablet() throws Exception { diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java b/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java index 1df9c02ee8..0b8bc10fec 100644 --- a/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java +++ b/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java @@ -17,6 +17,8 @@ package com.android.quickstep; 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 androidx.test.runner.AndroidJUnit4; 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 @@ public class TaplTestsTaskbar extends AbstractQuickStepTest { 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 @@ public class TaplTestsTaskbar extends AbstractQuickStepTest { @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 @@ public class TaplTestsTaskbar extends AbstractQuickStepTest { @Test @ScreenRecord // b/231615831 @PortraitLandscape + @TaskbarModeSwitch public void testLaunchShortcutInSplitscreen() throws Exception { getTaskbar().getAppIcon(TEST_APP_NAME) .openDeepShortcutMenu() @@ -104,16 +123,19 @@ public class TaplTestsTaskbar extends AbstractQuickStepTest { } @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 @@ public class TaplTestsTaskbar extends AbstractQuickStepTest { @Test @ScreenRecord // b/231615831 @PortraitLandscape + @TaskbarModeSwitch public void testLaunchAppInSplitscreen_FromTaskbarAllApps() throws Exception { getTaskbar().openAllApps() .getAppIcon(TEST_APP_NAME) @@ -134,6 +157,7 @@ public class TaplTestsTaskbar extends AbstractQuickStepTest { @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/TaskbarModeSwitchRule.java b/quickstep/tests/src/com/android/quickstep/TaskbarModeSwitchRule.java new file mode 100644 index 0000000000..9e41f74186 --- /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 7d414f4910..83602be72e 100644 --- a/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java +++ b/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java @@ -23,10 +23,8 @@ import android.graphics.Point; import android.graphics.Rect; import android.graphics.RectF; import android.util.ArrayMap; -import android.util.Pair; -import android.view.Display; +import android.view.RemoteAnimationTarget; import android.view.Surface; -import android.view.SurfaceControl; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; @@ -36,6 +34,7 @@ import com.android.launcher3.InvariantDeviceProfile; 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; @@ -43,8 +42,7 @@ import com.android.launcher3.util.window.CachedDisplayInfo; 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; @@ -148,7 +146,7 @@ public class TaskViewSimulatorTest { int rotation = mDisplaySize.x > mDisplaySize.y ? Surface.ROTATION_90 : Surface.ROTATION_0; CachedDisplayInfo cdi = - new CachedDisplayInfo("test-display", mDisplaySize, rotation , new Rect()); + new CachedDisplayInfo(mDisplaySize, rotation, new Rect()); WindowBounds wm = new WindowBounds( new Rect(0, 0, mDisplaySize.x, mDisplaySize.y), mDisplayInsets); @@ -164,15 +162,16 @@ public class TaskViewSimulatorTest { } WindowManagerProxy wmProxy = mock(WindowManagerProxy.class); - doReturn(cdi).when(wmProxy).getDisplayInfo(any(), any()); - doReturn(wm).when(wmProxy).getRealBounds(any(), any(), any()); + doReturn(cdi).when(wmProxy).getDisplayInfo(any()); + doReturn(wm).when(wmProxy).getRealBounds(any(), any()); + doReturn(NavigationMode.NO_BUTTON).when(wmProxy).getNavigationMode(any()); - ArrayMap<String, Pair<CachedDisplayInfo, WindowBounds[]>> perDisplayBoundsCache = + ArrayMap<CachedDisplayInfo, WindowBounds[]> perDisplayBoundsCache = new ArrayMap<>(); - perDisplayBoundsCache.put(cdi.id, Pair.create(cdi.normalize(), allBounds)); + perDisplayBoundsCache.put(cdi.normalize(), allBounds); DisplayController.Info mockInfo = new Info( - helper.sandboxContext, mock(Display.class), wmProxy, perDisplayBoundsCache); + helper.sandboxContext, wmProxy, perDisplayBoundsCache); DisplayController controller = DisplayController.INSTANCE.get(helper.sandboxContext); @@ -207,17 +206,21 @@ public class TaskViewSimulatorTest { } @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 83237b49e5..54b95aee8d 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 index 10aeee6e02..9213274d5c 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_block_shadow.xml b/res/color-night-v31/transient_taskbar_background.xml index 045fe8d4c5..40f6494933 100644 --- a/res/drawable/ic_block_shadow.xml +++ b/res/color-night-v31/transient_taskbar_background.xml @@ -1,11 +1,11 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2020 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_block_no_shadow" - 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 be7010b80b..0000000000 --- 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_remove_shadow.xml b/res/color-v31/transient_taskbar_background.xml index 48abc10b3b..bce947da79 100644 --- a/res/drawable/ic_remove_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_remove_no_shadow" - 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 648824ac1e..0000000000 --- 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 1cf97f61f7..5d5130086a 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 e4ea940763..b95e468c18 100644 --- a/res/drawable/bg_all_apps_searchbox.xml +++ b/res/drawable/bg_all_apps_searchbox.xml @@ -15,5 +15,5 @@ --> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <solid android:color="?attr/allappsHeaderProtectionColor" /> - <corners android:radius="58dp" /> + <corners android:radius="@dimen/rounded_button_radius" /> </shape>
\ No newline at end of file diff --git a/res/drawable/bg_work_apps_paused_action_button.xml b/res/drawable/bg_work_apps_paused_action_button.xml new file mode 100644 index 0000000000..74d469322a --- /dev/null +++ b/res/drawable/bg_work_apps_paused_action_button.xml @@ -0,0 +1,31 @@ +<?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. +--> +<ripple xmlns:android="http://schemas.android.com/apk/res/android" + android:color="@color/accent_ripple_color"> + <item android:id="@android:id/mask"> + <shape android:shape="rectangle"> + <corners android:radius="@dimen/rounded_button_radius" /> + <solid android:color="@android:color/white" /> + </shape> + </item> + + <item android:id="@android:id/background"> + <shape android:shape="rectangle"> + <solid android:color="?android:attr/colorControlHighlight" /> + <corners android:radius="@dimen/rounded_button_radius" /> + </shape> + </item> +</ripple>
\ 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 7f3fe14fb1..0000000000 --- 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 d226ac6000..0000000000 --- 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 5966d9969f..0000000000 --- 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 b18f8bc473..0000000000 --- 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 8eb4d90126..0000000000 --- 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 5770d3cf19..4f0b6a8fbf 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_select_windows.xml b/res/drawable/ic_select_windows.xml new file mode 100644 index 0000000000..cba0fde614 --- /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/page_indicator.xml b/res/drawable/page_indicator.xml new file mode 100644 index 0000000000..c0ccc49250 --- /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/drawable/work_apps_toggle_background.xml b/res/drawable/work_apps_toggle_background.xml index a47c8fef7c..6ad6c821db 100644 --- a/res/drawable/work_apps_toggle_background.xml +++ b/res/drawable/work_apps_toggle_background.xml @@ -13,16 +13,8 @@ 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:state_enabled="false"> - <shape android:shape="rectangle"> - <corners android:radius="@dimen/work_fab_radius" /> - <solid android:color="?android:attr/colorControlHighlight" /> - <padding - android:left="@dimen/work_profile_footer_padding" - android:right="@dimen/work_profile_footer_padding" /> - </shape> - </item> +<ripple xmlns:android="http://schemas.android.com/apk/res/android" + android:color="@color/accent_ripple_color"> <item> <shape android:shape="rectangle"> <corners android:radius="@dimen/work_fab_radius" /> @@ -32,4 +24,4 @@ android:right="@dimen/work_profile_footer_padding" /> </shape> </item> -</selector> +</ripple> diff --git a/res/layout/all_apps.xml b/res/layout/all_apps.xml index d0d82d48bd..655c75d1b5 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 12b6b7bb55..b0157c9c3e 100644 --- a/res/layout/all_apps_bottom_sheet_background.xml +++ b/res/layout/all_apps_bottom_sheet_background.xml @@ -16,13 +16,12 @@ <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" android:layout_width="match_parent" - android:layout_height="36dp" /> + android:layout_height="@dimen/bottom_sheet_handle_area_height" /> <View android:id="@+id/bottom_sheet_handle" diff --git a/res/layout/all_apps_content.xml b/res/layout/all_apps_content.xml new file mode 100644 index 0000000000..925f4d963b --- /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 11143fb3e1..e04b207a3e 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 6f2dd3d21e..0000000000 --- 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 82b0b8d74e..95ebd94d0e 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 0000000000..d5fe51e734 --- /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 754a9e251b..6d8f3613a9 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="0dp" android:focusableInTouchMode="true" android:gravity="center_vertical" android:hint="@string/all_apps_search_bar_hint" android:imeOptions="actionSearch|flagNoExtractUi" + android:importantForAutofill="no" android:inputType="text|textNoSuggestions|textCapWords" android:maxLines="1" android:paddingVertical="12dp" diff --git a/res/layout/search_results_rv_layout.xml b/res/layout/search_results_rv_layout.xml index 567cb5f4fc..9127521090 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 1c73165e35..f48f3c0aa4 100644 --- a/res/layout/secondary_launcher.xml +++ b/res/layout/secondary_launcher.xml @@ -18,6 +18,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/drag_layer" + android:clipChildren="false" android:padding="@dimen/dynamic_grid_edge_margin"> <GridView @@ -41,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" @@ -52,87 +52,5 @@ android:saveEnabled="false" android:layout_gravity="bottom|end" android:background="@drawable/round_rect_primary" - android:elevation="2dp" - 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" > - - <com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip - android:id="@+id/tabs" - android:layout_width="match_parent" - android:layout_height="@dimen/all_apps_header_pill_height" - android:orientation="horizontal" - style="@style/TextHeadline"> - - <Button - android:id="@+id/tab_personal" - android:layout_width="0dp" - android:layout_height="match_parent" - android:layout_weight="1" - android:background="?android:attr/selectableItemBackground" - android:text="@string/all_apps_personal_tab" - android:textAllCaps="true" - android:textColor="@color/all_apps_tab_text" - android:textSize="14sp" /> - - <Button - android:id="@+id/tab_work" - android:layout_width="0dp" - android:layout_height="match_parent" - android:layout_weight="1" - android:background="?android:attr/selectableItemBackground" - android:text="@string/all_apps_work_tab" - android:textAllCaps="true" - android:textColor="@color/all_apps_tab_text" - android:textSize="14sp" /> - </com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip> - </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="wrap_content" - android:layout_centerHorizontal="true" - android:layout_gravity="top|center_horizontal" - android:background="@drawable/bg_all_apps_searchbox" - android:elevation="0dp" - android:focusableInTouchMode="true" - android:gravity="center_vertical" - android:hint="@string/all_apps_search_bar_hint" - android:imeOptions="actionSearch|flagNoExtractUi" - android:inputType="text|textNoSuggestions|textCapWords" - android:maxLines="1" - android:paddingVertical="12dp" - android:paddingStart="12dp" - android:paddingEnd="12dp" - android:drawablePadding="8dp" - android:drawableStart="@drawable/ic_allapps_search" - android:saveEnabled="false" - android:scrollHorizontally="true" - android:singleLine="true" - android:textColor="?android:attr/textColorSecondary" - android:textColorHint="@drawable/all_apps_search_hint" - android:textSize="20sp" /> - - <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 11eea6072c..5518dc88a3 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 dfe226a8bd..3635c73a68 100644 --- a/res/layout/widgets_full_sheet_paged_view.xml +++ b/res/layout/widgets_full_sheet_paged_view.xml @@ -16,37 +16,35 @@ <merge xmlns:android="http://schemas.android.com/apk/res/android" xmlns:launcher="http://schemas.android.com/apk/res-auto"> - <com.android.launcher3.workprofile.PersonalWorkPagedView + <com.android.launcher3.widget.picker.WidgetPagedView android:id="@+id/widgets_view_pager" android:layout_width="match_parent" android:layout_height="match_parent" android:clipToPadding="false" android:layout_below="@id/collapse_handle" android:descendantFocusability="afterDescendants" - launcher:pageIndicator="@+id/tabs"> + android:paddingHorizontal="@dimen/widget_list_horizontal_margin" + launcher:pageIndicator="@+id/tabs" > <com.android.launcher3.widget.picker.WidgetsRecyclerView android:id="@+id/primary_widgets_list_view" android:layout_width="match_parent" android:layout_height="match_parent" - android:paddingHorizontal="@dimen/widget_list_horizontal_margin" android:clipToPadding="false" /> <com.android.launcher3.widget.picker.WidgetsRecyclerView android:id="@+id/work_widgets_list_view" android:layout_width="match_parent" android:layout_height="match_parent" - android:paddingHorizontal="@dimen/widget_list_horizontal_margin" android:clipToPadding="false" /> - </com.android.launcher3.workprofile.PersonalWorkPagedView> + </com.android.launcher3.widget.picker.WidgetPagedView> <!-- SearchAndRecommendationsView contains the tab layout as well --> - <com.android.launcher3.widget.picker.SearchAndRecommendationsView + <com.android.launcher3.views.StickyHeaderLayout android:id="@+id/search_and_recommendations_container" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin" android:layout_below="@id/collapse_handle" android:paddingBottom="0dp" android:orientation="vertical"> @@ -58,6 +56,7 @@ android:gravity="center_horizontal" android:textSize="24sp" android:layout_marginTop="24dp" + android:paddingHorizontal="@dimen/widget_list_horizontal_margin" android:textColor="?android:attr/textColorSecondary" android:text="@string/widget_button_text"/> @@ -68,7 +67,8 @@ android:elevation="0.1dp" android:background="?android:attr/colorBackground" android:paddingBottom="8dp" - android:clipToPadding="false"> + android:paddingHorizontal="@dimen/widget_list_horizontal_margin" + launcher:layout_sticky="true"> <include layout="@layout/widgets_search_bar" /> </FrameLayout> @@ -79,6 +79,7 @@ android:layout_marginTop="8dp" android:background="@drawable/widgets_recommendation_background" android:paddingVertical="@dimen/recommended_widgets_table_vertical_padding" + android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin" android:visibility="gone" /> <com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip @@ -88,10 +89,10 @@ 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"> + style="@style/TextHeadline" + launcher:layout_sticky="true"> <Button android:id="@+id/tab_personal" @@ -120,5 +121,5 @@ style="?android:attr/borderlessButtonStyle" /> </com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip> - </com.android.launcher3.widget.picker.SearchAndRecommendationsView> + </com.android.launcher3.views.StickyHeaderLayout> </merge>
\ No newline at end of file diff --git a/res/layout/widgets_full_sheet_recyclerview.xml b/res/layout/widgets_full_sheet_recyclerview.xml index 6a5d6cb416..b2a3a0d3d7 100644 --- a/res/layout/widgets_full_sheet_recyclerview.xml +++ b/res/layout/widgets_full_sheet_recyclerview.xml @@ -13,23 +13,24 @@ See the License for the specific language governing permissions and limitations under the License. --> -<merge xmlns:android="http://schemas.android.com/apk/res/android"> +<merge xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:launcher="http://schemas.android.com/apk/res-auto" > <com.android.launcher3.widget.picker.WidgetsRecyclerView android:id="@+id/primary_widgets_list_view" android:layout_below="@id/collapse_handle" android:layout_width="match_parent" android:layout_height="match_parent" - android:paddingHorizontal="@dimen/widget_list_horizontal_margin" + android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin" android:clipToPadding="false" /> <!-- SearchAndRecommendationsView without the tab layout as well --> - <com.android.launcher3.widget.picker.SearchAndRecommendationsView + <com.android.launcher3.views.StickyHeaderLayout android:id="@+id/search_and_recommendations_container" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin" android:layout_below="@id/collapse_handle" android:paddingBottom="16dp" + android:paddingHorizontal="@dimen/widget_list_horizontal_margin" android:orientation="vertical"> <TextView @@ -49,7 +50,8 @@ android:elevation="0.1dp" android:background="?android:attr/colorBackground" android:paddingBottom="8dp" - android:clipToPadding="false"> + android:clipToPadding="false" + launcher:layout_sticky="true" > <include layout="@layout/widgets_search_bar" /> </FrameLayout> @@ -61,6 +63,6 @@ android:background="@drawable/widgets_recommendation_background" android:paddingVertical="@dimen/recommended_widgets_table_vertical_padding" android:visibility="gone" /> - </com.android.launcher3.widget.picker.SearchAndRecommendationsView> + </com.android.launcher3.views.StickyHeaderLayout> </merge>
\ No newline at end of file diff --git a/res/layout/widgets_list_row_header.xml b/res/layout/widgets_list_row_header.xml index 3cdc2e844b..35bea279c0 100644 --- a/res/layout/widgets_list_row_header.xml +++ b/res/layout/widgets_list_row_header.xml @@ -19,7 +19,6 @@ android:id="@+id/widgets_list_header" android:layout_width="match_parent" android:layout_height="wrap_content" - android:paddingVertical="@dimen/widget_list_header_view_vertical_padding" android:orientation="horizontal" android:importantForAccessibility="yes" android:focusable="true" diff --git a/res/layout/widgets_search_bar.xml b/res/layout/widgets_search_bar.xml index 9178a75076..6d44865ef0 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_apps_paused.xml b/res/layout/work_apps_paused.xml index 79bce70287..f614d9b75f 100644 --- a/res/layout/work_apps_paused.xml +++ b/res/layout/work_apps_paused.xml @@ -48,7 +48,7 @@ android:textColor="?attr/workProfileOverlayTextColor" android:text="@string/work_apps_enable_btn_text" android:textAlignment="center" - android:background="@drawable/rounded_action_button" + android:background="@drawable/bg_work_apps_paused_action_button" android:paddingStart="16dp" android:paddingEnd="16dp" android:textSize="14sp" /> diff --git a/res/layout/work_mode_fab.xml b/res/layout/work_mode_fab.xml index d2fa5fa066..c116c12bad 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 55139b3382..500fce2a95 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Item is verwyder"</string> <string name="undo" msgid="4151576204245173321">"Ontdoen"</string> <string name="action_move" msgid="4339390619886385032">"Skuif item"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"Skuif na ry <xliff:g id="NUMBER_0">%1$s</xliff:g> kolom <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Skuif na ry <xliff:g id="NUMBER_0">%1$s</xliff:g> kolom <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">"Skuif na posisie <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"Skuif na gunstelingposisie <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="item_moved" msgid="4606538322571412879">"Item geskuif"</string> diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml index 6b6147b883..b7d4a7de07 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> @@ -81,7 +80,7 @@ <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="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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"ንጥል ነገር ተንቀሳቅሷል"</string> <string name="undo" msgid="4151576204245173321">"ቀልብስ"</string> <string name="action_move" msgid="4339390619886385032">"ንጥልን አንቀሳቅስ"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"ወደ ረድፍ <xliff:g id="NUMBER_0">%1$s</xliff:g> ዓምድ <xliff:g id="NUMBER_1">%2$s</xliff:g> አንቀሳቅስ"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"በ<xliff:g id="STRING">%3$s</xliff:g> ውስጥ ወደ ረድፍ <xliff:g id="NUMBER_0">%1$s</xliff:g> ዓምድ <xliff:g id="NUMBER_1">%2$s</xliff:g> ይውሰዱ"</string> <string name="move_to_position" msgid="6750008980455459790">"ወደ አቀማመጥ <xliff:g id="NUMBER">%1$s</xliff:g> አንቀሳቅስ"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"ወደ ተወዳጆች አቀማመጥ <xliff:g id="NUMBER">%1$s</xliff:g> አንቀሳቅስ"</string> <string name="item_moved" msgid="4606538322571412879">"ንጥል ተንቀሳቅሷል"</string> diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml index 0e48179b6e..42c51de9e2 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"تمّت إزالة العنصر."</string> <string name="undo" msgid="4151576204245173321">"تراجع"</string> <string name="action_move" msgid="4339390619886385032">"نقل العنصر"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"نقل إلى الصف <xliff:g id="NUMBER_0">%1$s</xliff:g> العمود <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"انتقل إلى الصف <xliff:g id="NUMBER_0">%1$s</xliff:g> العمود <xliff:g id="NUMBER_1">%2$s</xliff:g> في <xliff:g id="STRING">%3$s</xliff:g>."</string> <string name="move_to_position" msgid="6750008980455459790">"نقل إلى الموضع <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"نقل إلى الموضع المفضل <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="item_moved" msgid="4606538322571412879">"تم نقل العنصر"</string> diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml index 5fc8ca6fd2..67998d53a4 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"বস্তুটো আঁতৰোৱা হ’ল"</string> <string name="undo" msgid="4151576204245173321">"আনডু কৰক"</string> <string name="action_move" msgid="4339390619886385032">"বস্তু স্থানান্তৰ কৰক"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"শাৰী <xliff:g id="NUMBER_0">%1$s</xliff:g> স্তম্ভ <xliff:g id="NUMBER_1">%2$s</xliff:g>লৈ স্থানান্তৰিত কৰক"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g>ত <xliff:g id="NUMBER_0">%1$s</xliff:g> নম্বৰ শাৰী <xliff:g id="NUMBER_1">%2$s</xliff:g> নম্বৰ স্তম্ভলৈ লৈ যাওক"</string> <string name="move_to_position" msgid="6750008980455459790">"পছন্দৰ অৱস্থান <xliff:g id="NUMBER">%1$s</xliff:g>লৈ স্থানান্তৰিত কৰক"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"পছন্দৰ অৱস্থান <xliff:g id="NUMBER">%1$s</xliff:g>লৈ স্থানান্তৰিত কৰক"</string> <string name="item_moved" msgid="4606538322571412879">"বস্তুটো স্থানান্তৰ কৰা হ’ল"</string> diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml index ad724247ee..2e0740573e 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Element silindi"</string> <string name="undo" msgid="4151576204245173321">"Ləğv edin"</string> <string name="action_move" msgid="4339390619886385032">"Elementi köçürün"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"Sıra <xliff:g id="NUMBER_0">%1$s</xliff:g> sütun <xliff:g id="NUMBER_1">%2$s</xliff:g> köçürün"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="NUMBER_0">%1$s</xliff:g> saylı sətir, <xliff:g id="NUMBER_1">%2$s</xliff:g> saylı sütuna (<xliff:g id="STRING">%3$s</xliff:g>) köçürün"</string> <string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g> mövqeyinə köçürün"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"<xliff:g id="NUMBER">%1$s</xliff:g> sevimlilər mövqeyinə köçürün"</string> <string name="item_moved" msgid="4606538322571412879">"Elementin yeri dəyişildi"</string> diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml index d7e85d939a..cc6e908efb 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Stavka je uklonjena"</string> <string name="undo" msgid="4151576204245173321">"Opozovi"</string> <string name="action_move" msgid="4339390619886385032">"Premesti stavku"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"Premesti u red <xliff:g id="NUMBER_0">%1$s</xliff:g> i kolonu <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Premestite u red <xliff:g id="NUMBER_0">%1$s</xliff:g> kolonu <xliff:g id="NUMBER_1">%2$s</xliff:g> na <xliff:g id="STRING">%3$s</xliff:g>"</string> <string name="move_to_position" msgid="6750008980455459790">"Premesti na <xliff:g id="NUMBER">%1$s</xliff:g>. poziciju"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"Premesti na <xliff:g id="NUMBER">%1$s</xliff:g>. poziciju u omiljenim"</string> <string name="item_moved" msgid="4606538322571412879">"Stavka je premeštena"</string> diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml index a845f9aa46..6886b7085d 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Элемент выдалены"</string> <string name="undo" msgid="4151576204245173321">"Адрабіць"</string> <string name="action_move" msgid="4339390619886385032">"Перамясціць элемент"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"Перамясціць у радок <xliff:g id="NUMBER_0">%1$s</xliff:g> слупок <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Перайсці да радка <xliff:g id="NUMBER_0">%1$s</xliff:g> у слупку <xliff:g id="NUMBER_1">%2$s</xliff:g> на старонцы <xliff:g id="STRING">%3$s</xliff:g>"</string> <string name="move_to_position" msgid="6750008980455459790">"Перамясціць у пазіцыю <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"Перамясціць у абранае, у пазіцыю <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="item_moved" msgid="4606538322571412879">"Элемент перамешчаны"</string> diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml index 0c7fcd33bd..549c8eab6a 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Елементът е премахнат"</string> <string name="undo" msgid="4151576204245173321">"Отмяна"</string> <string name="action_move" msgid="4339390619886385032">"Преместване на елемента"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"Преместване към ред <xliff:g id="NUMBER_0">%1$s</xliff:g>, колона <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Преместване на <xliff:g id="STRING">%3$s</xliff:g> – ред <xliff:g id="NUMBER_0">%1$s</xliff:g>, колона <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string> <string name="move_to_position" msgid="6750008980455459790">"Преместване към позиция <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"Преместване към позиция <xliff:g id="NUMBER">%1$s</xliff:g> в любимите"</string> <string name="item_moved" msgid="4606538322571412879">"Елементът е преместен"</string> diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml index 5c8c34209d..9683182c73 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"আইটেম সরানো হয়েছে"</string> <string name="undo" msgid="4151576204245173321">"ফিরিয়ে আনুন"</string> <string name="action_move" msgid="4339390619886385032">"আইটেম সরান"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"সারি <xliff:g id="NUMBER_0">%1$s</xliff:g> কলাম <xliff:g id="NUMBER_1">%2$s</xliff:g> এ সরান"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g>-এ সারি <xliff:g id="NUMBER_0">%1$s</xliff:g> কলাম <xliff:g id="NUMBER_1">%2$s</xliff:g> সরান"</string> <string name="move_to_position" msgid="6750008980455459790">"অবস্থানে সরান <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"পছন্দসই অবস্থানে সরান <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="item_moved" msgid="4606538322571412879">"আইটেম সরানো হয়েছে"</string> diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml index 173518f75f..f99525f7ad 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Stavka je uklonjena"</string> <string name="undo" msgid="4151576204245173321">"Poništi"</string> <string name="action_move" msgid="4339390619886385032">"Premjesti stavku"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"Pomjeri stavku u red <xliff:g id="NUMBER_0">%1$s</xliff:g> kolonu <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Premještanje u red <xliff:g id="NUMBER_0">%1$s</xliff:g>, kolonu <xliff:g id="NUMBER_1">%2$s</xliff:g> na <xliff:g id="STRING">%3$s</xliff:g>"</string> <string name="move_to_position" msgid="6750008980455459790">"Pomjeri stavku na poziciju <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"Pomjeri stavku na poziciju <xliff:g id="NUMBER">%1$s</xliff:g> među omiljenim"</string> <string name="item_moved" msgid="4606538322571412879">"Stavka je premještena"</string> diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml index e6a4af2419..d8ede1577d 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"S\'ha suprimit l\'element"</string> <string name="undo" msgid="4151576204245173321">"Desfés"</string> <string name="action_move" msgid="4339390619886385032">"Desplaça l\'element"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"Desplaça l\'element a la fila <xliff:g id="NUMBER_0">%1$s</xliff:g> i la columna <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Mou a la fila <xliff:g id="NUMBER_0">%1$s</xliff:g>, columna <xliff:g id="NUMBER_1">%2$s</xliff:g> a <xliff:g id="STRING">%3$s</xliff:g>"</string> <string name="move_to_position" msgid="6750008980455459790">"Desplaça l\'element a la posició <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"Desplaça l\'element a la posició de preferits <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="item_moved" msgid="4606538322571412879">"Element desplaçat"</string> diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml index 8b3ef2d55a..600d66f0a8 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Položka byla odstraněna"</string> <string name="undo" msgid="4151576204245173321">"Zpět"</string> <string name="action_move" msgid="4339390619886385032">"Přesunout položku"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"Přesunout na řádek <xliff:g id="NUMBER_0">%1$s</xliff:g> do sloupce <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Přesunout na řádek <xliff:g id="NUMBER_0">%1$s</xliff:g>, sloupec <xliff:g id="NUMBER_1">%2$s</xliff:g> na ploše <xliff:g id="STRING">%3$s</xliff:g>"</string> <string name="move_to_position" msgid="6750008980455459790">"Přesunout na pozici <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"Přesunout do oblíbených položek na pozici <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="item_moved" msgid="4606538322571412879">"Položka byla přesunuta"</string> diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml index 816046567e..4da7b848b2 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Elementet er fjernet"</string> <string name="undo" msgid="4151576204245173321">"Fortryd"</string> <string name="action_move" msgid="4339390619886385032">"Flyt element"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"Flyt til række <xliff:g id="NUMBER_0">%1$s</xliff:g>, kolonne <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Flyt til række <xliff:g id="NUMBER_0">%1$s</xliff:g> kolonne <xliff:g id="NUMBER_1">%2$s</xliff:g> i <xliff:g id="STRING">%3$s</xliff:g>"</string> <string name="move_to_position" msgid="6750008980455459790">"Flyt til position <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"Flyt til foretrukne position <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="item_moved" msgid="4606538322571412879">"Elementet blev flyttet"</string> diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml index 912e7facd7..3c2514f784 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Element entfernt"</string> <string name="undo" msgid="4151576204245173321">"Rückgängig"</string> <string name="action_move" msgid="4339390619886385032">"Element verschieben"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"In Zeile <xliff:g id="NUMBER_0">%1$s</xliff:g>, Spalte <xliff:g id="NUMBER_1">%2$s</xliff:g> verschoben"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"In <xliff:g id="STRING">%3$s</xliff:g> zu Zeile <xliff:g id="NUMBER_0">%1$s</xliff:g> Spalte <xliff:g id="NUMBER_1">%2$s</xliff:g> bewegen"</string> <string name="move_to_position" msgid="6750008980455459790">"Auf Position <xliff:g id="NUMBER">%1$s</xliff:g> verschoben"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"Auf Favoritenposition <xliff:g id="NUMBER">%1$s</xliff:g> verschoben"</string> <string name="item_moved" msgid="4606538322571412879">"Element verschoben"</string> diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml index 0f613dec7b..ae9a8fd3ee 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Το στοιχείο καταργήθηκε"</string> <string name="undo" msgid="4151576204245173321">"Αναίρεση"</string> <string name="action_move" msgid="4339390619886385032">"Μετακίνηση στοιχείου"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"Μετακίνηση στη σειρά <xliff:g id="NUMBER_0">%1$s</xliff:g>, στήλη <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Μετακίνηση στη σειρά <xliff:g id="NUMBER_0">%1$s</xliff:g> στήλη <xliff:g id="NUMBER_1">%2$s</xliff:g> στην <xliff:g id="STRING">%3$s</xliff:g>"</string> <string name="move_to_position" msgid="6750008980455459790">"Μετακίνηση στη θέση <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"Μετακίνηση στη θέση <xliff:g id="NUMBER">%1$s</xliff:g> στα αγαπημένα"</string> <string name="item_moved" msgid="4606538322571412879">"Το στοιχείο καταργήθηκε"</string> diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml index a78c7f869c..4f15eb5777 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Item removed"</string> <string name="undo" msgid="4151576204245173321">"Undo"</string> <string name="action_move" msgid="4339390619886385032">"Move item"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"Move to row <xliff:g id="NUMBER_0">%1$s</xliff:g> column <xliff:g id="NUMBER_1">%2$s</xliff:g>"</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="item_moved" msgid="4606538322571412879">"Item moved"</string> diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml index a78c7f869c..aa29d8dc2f 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> @@ -137,9 +136,9 @@ <string name="item_removed" msgid="851119963877842327">"Item removed"</string> <string name="undo" msgid="4151576204245173321">"Undo"</string> <string name="action_move" msgid="4339390619886385032">"Move item"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"Move to row <xliff:g id="NUMBER_0">%1$s</xliff:g> column <xliff:g id="NUMBER_1">%2$s</xliff:g>"</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 a78c7f869c..4f15eb5777 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Item removed"</string> <string name="undo" msgid="4151576204245173321">"Undo"</string> <string name="action_move" msgid="4339390619886385032">"Move item"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"Move to row <xliff:g id="NUMBER_0">%1$s</xliff:g> column <xliff:g id="NUMBER_1">%2$s</xliff:g>"</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="item_moved" msgid="4606538322571412879">"Item moved"</string> diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml index a78c7f869c..4f15eb5777 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Item removed"</string> <string name="undo" msgid="4151576204245173321">"Undo"</string> <string name="action_move" msgid="4339390619886385032">"Move item"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"Move to row <xliff:g id="NUMBER_0">%1$s</xliff:g> column <xliff:g id="NUMBER_1">%2$s</xliff:g>"</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="item_moved" msgid="4606538322571412879">"Item moved"</string> diff --git a/res/values-en-rXC/strings.xml b/res/values-en-rXC/strings.xml index d6951ad819..e644c23c23 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Item removed"</string> <string name="undo" msgid="4151576204245173321">"Undo"</string> <string name="action_move" msgid="4339390619886385032">"Move item"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"Move to row <xliff:g id="NUMBER_0">%1$s</xliff:g> column <xliff:g id="NUMBER_1">%2$s</xliff:g>"</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 favorites position <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="item_moved" msgid="4606538322571412879">"Item moved"</string> diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml index 3897974178..0bc8f4f0b8 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Se eliminó el elemento."</string> <string name="undo" msgid="4151576204245173321">"Deshacer"</string> <string name="action_move" msgid="4339390619886385032">"Mover elemento"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"Mover a fila <xliff:g id="NUMBER_0">%1$s</xliff:g>, columna <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Mover a la fila <xliff:g id="NUMBER_0">%1$s</xliff:g>, columna <xliff:g id="NUMBER_1">%2$s</xliff:g> en <xliff:g id="STRING">%3$s</xliff:g>"</string> <string name="move_to_position" msgid="6750008980455459790">"Mover a la posición número <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"Mover a la posición de favoritos número <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="item_moved" msgid="4606538322571412879">"Elemento movido"</string> diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml index 823e5a7d3c..4045ad3365 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Elemento quitado"</string> <string name="undo" msgid="4151576204245173321">"Deshacer"</string> <string name="action_move" msgid="4339390619886385032">"Mover elemento"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"Mover a la fila <xliff:g id="NUMBER_0">%1$s</xliff:g>, columna <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Mover a la fila <xliff:g id="NUMBER_0">%1$s</xliff:g>, columna <xliff:g id="NUMBER_1">%2$s</xliff:g> en <xliff:g id="STRING">%3$s</xliff:g>"</string> <string name="move_to_position" msgid="6750008980455459790">"Mover a la posición número <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"Mover a la posición número <xliff:g id="NUMBER">%1$s</xliff:g> de favoritos"</string> <string name="item_moved" msgid="4606538322571412879">"Elemento movido"</string> diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml index bd2190aa10..effd1400c6 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Üksus eemaldati"</string> <string name="undo" msgid="4151576204245173321">"Võta tagasi"</string> <string name="action_move" msgid="4339390619886385032">"Teisalda üksus"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"Teisaldamine <xliff:g id="NUMBER_0">%1$s</xliff:g>. rea <xliff:g id="NUMBER_1">%2$s</xliff:g>. veergu"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Teisalda reale <xliff:g id="NUMBER_0">%1$s</xliff:g> veerus <xliff:g id="NUMBER_1">%2$s</xliff:g> kohas <xliff:g id="STRING">%3$s</xliff:g>"</string> <string name="move_to_position" msgid="6750008980455459790">"Teisaldamine <xliff:g id="NUMBER">%1$s</xliff:g>. positsioonile"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"Teisaldamine lemmikute <xliff:g id="NUMBER">%1$s</xliff:g>. positsioonile"</string> <string name="item_moved" msgid="4606538322571412879">"Üksus teisaldati"</string> diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml index c1eaea8d57..8b944fe509 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Kendu da elementua"</string> <string name="undo" msgid="4151576204245173321">"Desegin"</string> <string name="action_move" msgid="4339390619886385032">"Mugitu elementua"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"Eraman <xliff:g id="NUMBER_0">%1$s</xliff:g>. errenkadara, <xliff:g id="NUMBER_1">%2$s</xliff:g>. zutabera"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Joan <xliff:g id="NUMBER_0">%1$s</xliff:g>garren errenkadako <xliff:g id="NUMBER_1">%2$s</xliff:g>garren zutabera, <xliff:g id="STRING">%3$s</xliff:g>"</string> <string name="move_to_position" msgid="6750008980455459790">"Eraman <xliff:g id="NUMBER">%1$s</xliff:g>. postura"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"Eraman gogokoen <xliff:g id="NUMBER">%1$s</xliff:g>. postura"</string> <string name="item_moved" msgid="4606538322571412879">"Elementua mugitu da"</string> diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml index d23eac21c0..a1acda04df 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"مورد حذف شد"</string> <string name="undo" msgid="4151576204245173321">"واگرد"</string> <string name="action_move" msgid="4339390619886385032">"انتقال مورد"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"انتقال به سطر <xliff:g id="NUMBER_0">%1$s</xliff:g> ستون <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"انتقال به ردیف <xliff:g id="NUMBER_0">%1$s</xliff:g> ستون <xliff:g id="NUMBER_1">%2$s</xliff:g> در <xliff:g id="STRING">%3$s</xliff:g>"</string> <string name="move_to_position" msgid="6750008980455459790">"انتقال به موقعیت <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"انتقال به موقعیت دلخواه <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="item_moved" msgid="4606538322571412879">"مورد منتقل شد"</string> diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml index ccc22f0de7..89e41068e2 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Kohde poistettiin"</string> <string name="undo" msgid="4151576204245173321">"Kumoa"</string> <string name="action_move" msgid="4339390619886385032">"Siirrä kohde"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"Siirrä rivin <xliff:g id="NUMBER_0">%1$s</xliff:g> sarakkeeseen <xliff:g id="NUMBER_1">%2$s</xliff:g>."</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Siirrä riviin <xliff:g id="NUMBER_0">%1$s</xliff:g> sarakkeeseen <xliff:g id="NUMBER_1">%2$s</xliff:g> näytöllä <xliff:g id="STRING">%3$s</xliff:g>"</string> <string name="move_to_position" msgid="6750008980455459790">"Siirrä kohtaan <xliff:g id="NUMBER">%1$s</xliff:g>."</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"Siirrä suosikkien kohtaan <xliff:g id="NUMBER">%1$s</xliff:g>."</string> <string name="item_moved" msgid="4606538322571412879">"Kohde on siirretty."</string> diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml index 11bcae146a..448569df05 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Élément retiré"</string> <string name="undo" msgid="4151576204245173321">"Annuler"</string> <string name="action_move" msgid="4339390619886385032">"Déplacer l\'élément"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"Déplacer vers rangée <xliff:g id="NUMBER_0">%1$s</xliff:g> colonne <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Déplacer à la ligne <xliff:g id="NUMBER_0">%1$s</xliff:g> colonne <xliff:g id="NUMBER_1">%2$s</xliff:g> dans <xliff:g id="STRING">%3$s</xliff:g>"</string> <string name="move_to_position" msgid="6750008980455459790">"Déplacer vers la position <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"Déplacer vers la position <xliff:g id="NUMBER">%1$s</xliff:g> dans les favoris"</string> <string name="item_moved" msgid="4606538322571412879">"Élément déplacé"</string> diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml index 9b30b1efaf..b371067d15 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Élément supprimé"</string> <string name="undo" msgid="4151576204245173321">"Annuler"</string> <string name="action_move" msgid="4339390619886385032">"Déplacer l\'élément"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"Déplacer vers la ligne <xliff:g id="NUMBER_0">%1$s</xliff:g>, colonne <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Déplacer vers la ligne <xliff:g id="NUMBER_0">%1$s</xliff:g>, colonne <xliff:g id="NUMBER_1">%2$s</xliff:g> dans <xliff:g id="STRING">%3$s</xliff:g>"</string> <string name="move_to_position" msgid="6750008980455459790">"Déplacer vers la position <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"Déplacer vers la position <xliff:g id="NUMBER">%1$s</xliff:g> dans les favoris"</string> <string name="item_moved" msgid="4606538322571412879">"Élément déplacé"</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 013011aafd..045d8d7325 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Quitouse o elemento"</string> <string name="undo" msgid="4151576204245173321">"Desfacer"</string> <string name="action_move" msgid="4339390619886385032">"Mover elemento"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"Mover á fila <xliff:g id="NUMBER_0">%1$s</xliff:g> columna <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Mover á fila <xliff:g id="NUMBER_0">%1$s</xliff:g>, columna <xliff:g id="NUMBER_1">%2$s</xliff:g> de <xliff:g id="STRING">%3$s</xliff:g>"</string> <string name="move_to_position" msgid="6750008980455459790">"Mover á posición <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"Mover á posición dos favoritos <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="item_moved" msgid="4606538322571412879">"Moveuse o elemento"</string> diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml index 2d504e3b01..72885708da 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"આઇટમ કાઢી નાખી"</string> <string name="undo" msgid="4151576204245173321">"રદ કરો"</string> <string name="action_move" msgid="4339390619886385032">"આઇટમ ખસેડો"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"<xliff:g id="NUMBER_0">%1$s</xliff:g> પંક્તિ <xliff:g id="NUMBER_1">%2$s</xliff:g> કૉલમ પર ખસેડો"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g>માં પંક્તિ <xliff:g id="NUMBER_0">%1$s</xliff:g> કૉલમ <xliff:g id="NUMBER_1">%2$s</xliff:g> પર આ આઇટમને ખસેડો"</string> <string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g> સ્થિતિ પર ખસેડો"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"મનપસંદ સ્થિતિ <xliff:g id="NUMBER">%1$s</xliff:g> પર ખસેડો"</string> <string name="item_moved" msgid="4606538322571412879">"આઇટમ ખસેડી"</string> diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml index de6ad1c39e..e90e63a480 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"आइटम हटाया गया"</string> <string name="undo" msgid="4151576204245173321">"पहले जैसा करें"</string> <string name="action_move" msgid="4339390619886385032">"आइटम ले जाएं"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"पंक्ति <xliff:g id="NUMBER_0">%1$s</xliff:g> स्तंभ <xliff:g id="NUMBER_1">%2$s</xliff:g> पर ले जाएं"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g> में, पंक्ति <xliff:g id="NUMBER_0">%1$s</xliff:g> कॉलम <xliff:g id="NUMBER_1">%2$s</xliff:g> पर जाएं"</string> <string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g> स्थिति पर ले जाएं"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"<xliff:g id="NUMBER">%1$s</xliff:g> की पसंदीदा स्थिति पर ले जाएं"</string> <string name="item_moved" msgid="4606538322571412879">"आइटम ले जाया गया"</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 0e58e530eb..a1369e8133 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Stavka je uklonjena"</string> <string name="undo" msgid="4151576204245173321">"Poništi"</string> <string name="action_move" msgid="4339390619886385032">"Premještanje stavke"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"Premještanje u redak <xliff:g id="NUMBER_0">%1$s</xliff:g>, stupac <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Premjesti u redak <xliff:g id="NUMBER_0">%1$s</xliff:g> stupac <xliff:g id="NUMBER_1">%2$s</xliff:g> na <xliff:g id="STRING">%3$s</xliff:g>"</string> <string name="move_to_position" msgid="6750008980455459790">"Premještanje na položaj <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"Premještanje na položaj favorita <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="item_moved" msgid="4606538322571412879">"Stavka premještena"</string> diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml index 30095d75cf..a5d4835368 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Elem eltávolítva"</string> <string name="undo" msgid="4151576204245173321">"Mégse"</string> <string name="action_move" msgid="4339390619886385032">"Elem mozgatása"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"Áthelyezés ide: <xliff:g id="NUMBER_0">%1$s</xliff:g>. sor, <xliff:g id="NUMBER_1">%2$s</xliff:g>. oszlop"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Áthelyezés a(z) <xliff:g id="NUMBER_0">%1$s</xliff:g>. sorba és a(z) <xliff:g id="NUMBER_1">%2$s</xliff:g>. oszlopba itt: <xliff:g id="STRING">%3$s</xliff:g>"</string> <string name="move_to_position" msgid="6750008980455459790">"Áthelyezés a(z) <xliff:g id="NUMBER">%1$s</xliff:g>. pozícióba"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"Áthelyezés a kedvencek <xliff:g id="NUMBER">%1$s</xliff:g>. pozíciójába"</string> <string name="item_moved" msgid="4606538322571412879">"Elem áthelyezve"</string> diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml index 3b9761ffd4..b8f16437a1 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Տարրը հեռացվեց"</string> <string name="undo" msgid="4151576204245173321">"Հետարկել"</string> <string name="action_move" msgid="4339390619886385032">"Տեղափոխել տարրը"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"Տեղափոխել տող <xliff:g id="NUMBER_0">%1$s</xliff:g> սյունակ <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Տեղափոխել շարք <xliff:g id="NUMBER_0">%1$s</xliff:g>, սյունակ <xliff:g id="NUMBER_1">%2$s</xliff:g> (<xliff:g id="STRING">%3$s</xliff:g>)"</string> <string name="move_to_position" msgid="6750008980455459790">"Տեղափոխել դիրք <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"Տեղափոխել նախընտրած դիրք՝ <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="item_moved" msgid="4606538322571412879">"Տարրը տեղափոխվեց"</string> diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml index 02f4860aa8..e46026e800 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Item dihapus"</string> <string name="undo" msgid="4151576204245173321">"Urungkan"</string> <string name="action_move" msgid="4339390619886385032">"Pindahkan item"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"Pindahkan ke baris <xliff:g id="NUMBER_0">%1$s</xliff:g> kolom <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Pindahkan ke baris <xliff:g id="NUMBER_0">%1$s</xliff:g> kolom <xliff:g id="NUMBER_1">%2$s</xliff:g> di <xliff:g id="STRING">%3$s</xliff:g>"</string> <string name="move_to_position" msgid="6750008980455459790">"PIndahkan ke posisi <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"Pindahkan ke posisi favorit <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="item_moved" msgid="4606538322571412879">"Item dipindahkan"</string> diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml index 17cf2e4ad1..92be25b8f4 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Atriði fjarlægt"</string> <string name="undo" msgid="4151576204245173321">"Afturkalla"</string> <string name="action_move" msgid="4339390619886385032">"Færa atriði"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"Færa í línu <xliff:g id="NUMBER_0">%1$s</xliff:g>, dálk <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Færðu þig í línu <xliff:g id="NUMBER_0">%1$s</xliff:g>, dálk <xliff:g id="NUMBER_1">%2$s</xliff:g> í <xliff:g id="STRING">%3$s</xliff:g>"</string> <string name="move_to_position" msgid="6750008980455459790">"Færa í stöðu <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"Færa í stöðu <xliff:g id="NUMBER">%1$s</xliff:g> á festisvæði"</string> <string name="item_moved" msgid="4606538322571412879">"Atriði fært"</string> diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml index edff814091..b5b7addfd1 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Elemento rimosso"</string> <string name="undo" msgid="4151576204245173321">"Annulla"</string> <string name="action_move" msgid="4339390619886385032">"Sposta elemento"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"Sposta a riga <xliff:g id="NUMBER_0">%1$s</xliff:g>, colonna <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Spostati alla riga <xliff:g id="NUMBER_0">%1$s</xliff:g> colonna <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">"Sposta nella posizione <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"Sposta nella posizione <xliff:g id="NUMBER">%1$s</xliff:g> dei preferiti"</string> <string name="item_moved" msgid="4606538322571412879">"Elemento spostato"</string> diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml index 7de8f08414..d79c022c7c 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"הפריט הוסר"</string> <string name="undo" msgid="4151576204245173321">"ביטול"</string> <string name="action_move" msgid="4339390619886385032">"העברת הפריט"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"העברה אל שורה <xliff:g id="NUMBER_0">%1$s</xliff:g> עמודה <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"צריך לעבור לשורה <xliff:g id="NUMBER_0">%1$s</xliff:g> ולטור <xliff:g id="NUMBER_1">%2$s</xliff:g> ב-<xliff:g id="STRING">%3$s</xliff:g>"</string> <string name="move_to_position" msgid="6750008980455459790">"העברה אל מיקום <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"העברה אל מיקום <xliff:g id="NUMBER">%1$s</xliff:g> במועדפים"</string> <string name="item_moved" msgid="4606538322571412879">"הפריט הועבר"</string> diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml index d7cbb07de8..1a442a89dd 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"アイテムを削除しました"</string> <string name="undo" msgid="4151576204245173321">"元に戻す"</string> <string name="action_move" msgid="4339390619886385032">"アイテムを移動"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"行<xliff:g id="NUMBER_0">%1$s</xliff:g>、列<xliff:g id="NUMBER_1">%2$s</xliff:g>に移動"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g> の行 <xliff:g id="NUMBER_0">%1$s</xliff:g>、列 <xliff:g id="NUMBER_1">%2$s</xliff:g> に移動します"</string> <string name="move_to_position" msgid="6750008980455459790">"位置<xliff:g id="NUMBER">%1$s</xliff:g>に移動"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"お気に入りの位置<xliff:g id="NUMBER">%1$s</xliff:g>に移動"</string> <string name="item_moved" msgid="4606538322571412879">"アイテムを移動しました"</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 ba5be87921..8437dd876c 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"ერთეული წაიშალა"</string> <string name="undo" msgid="4151576204245173321">"მოქმედების გაუქმება"</string> <string name="action_move" msgid="4339390619886385032">"ერთეულის გადაადგილება"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"გადატანა რიგში <xliff:g id="NUMBER_0">%1$s</xliff:g> სვეტში <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"გადაიტანეთ მწკრივი #<xliff:g id="NUMBER_0">%1$s</xliff:g> სვეტი #<xliff:g id="NUMBER_1">%2$s</xliff:g> <xliff:g id="STRING">%3$s</xliff:g>-ში"</string> <string name="move_to_position" msgid="6750008980455459790">"გადატანა <xliff:g id="NUMBER">%1$s</xliff:g> პოზიციაზე"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"გადატანა რჩეულთა პოზიციაზე <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="item_moved" msgid="4606538322571412879">"ერთეული გადაადგილდა"</string> diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml index 83b5a669b6..ec3ee18314 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Элемент жойылды"</string> <string name="undo" msgid="4151576204245173321">"Қайтару"</string> <string name="action_move" msgid="4339390619886385032">"Элементті жылжыту"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"<xliff:g id="NUMBER_0">%1$s</xliff:g>-жол, <xliff:g id="NUMBER_1">%2$s</xliff:g>-бағанға жылжыту"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g> экранында <xliff:g id="NUMBER_0">%1$s</xliff:g> жолын, <xliff:g id="NUMBER_1">%2$s</xliff:g> бағанын жылжытыңыз."</string> <string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g>-орынға жылжыту"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"<xliff:g id="NUMBER">%1$s</xliff:g> нөмірлі таңдаулы орынға жылжыту"</string> <string name="item_moved" msgid="4606538322571412879">"Элемент жылжытылды"</string> diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml index 73ec4df969..f1c1d089bd 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"បានដកធាតុចេញ"</string> <string name="undo" msgid="4151576204245173321">"ត្រឡប់វិញ"</string> <string name="action_move" msgid="4339390619886385032">"ផ្លាស់ទីធាតុ"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"ផ្លាស់ទីទៅជួរដេកទី <xliff:g id="NUMBER_0">%1$s</xliff:g> ជួរឈរទី <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"ផ្លាស់ទៅជួរដេក <xliff:g id="NUMBER_0">%1$s</xliff:g> ជួរឈរ <xliff:g id="NUMBER_1">%2$s</xliff:g> ក្នុង <xliff:g id="STRING">%3$s</xliff:g>"</string> <string name="move_to_position" msgid="6750008980455459790">"ផ្លាស់ទីទៅទីតាំង <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"ផ្លាស់ទីទៅការចូលចិត្តទីតាំងទី <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="item_moved" msgid="4606538322571412879">"បានផ្លាស់ទីធាតុ"</string> diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml index 1e6f39e4af..72eaeac80e 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"ಐಟಂ ತೆಗೆದುಹಾಕಲಾಗಿದೆ"</string> <string name="undo" msgid="4151576204245173321">"ರದ್ದುಮಾಡಿ"</string> <string name="action_move" msgid="4339390619886385032">"ಐಟಂ ಸರಿಸಿ"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"<xliff:g id="NUMBER_0">%1$s</xliff:g> ಸಾಲು <xliff:g id="NUMBER_1">%2$s</xliff:g> ಕಾಲಮ್ಗೆ ಸರಿಸಿ"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g> ನಲ್ಲಿ ಸಾಲು <xliff:g id="NUMBER_0">%1$s</xliff:g> ಅನ್ನು ಕಾಲಮ್ <xliff:g id="NUMBER_1">%2$s</xliff:g> ಗೆ ಸರಿಸಿ"</string> <string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g> ಸ್ಥಾನಕ್ಕೆ ಸರಿಸಿ"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"ಮೆಚ್ಚಿನ <xliff:g id="NUMBER">%1$s</xliff:g> ಸ್ಥಾನಕ್ಕೆ ಸರಿಸಿ"</string> <string name="item_moved" msgid="4606538322571412879">"ಐಟಂ ಸರಿಸಲಾಗಿದೆ"</string> diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml index d5ec664bf9..f6b8ff71e4 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"항목 삭제됨"</string> <string name="undo" msgid="4151576204245173321">"실행취소"</string> <string name="action_move" msgid="4339390619886385032">"항목 이동"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"<xliff:g id="NUMBER_0">%1$s</xliff:g>행 <xliff:g id="NUMBER_1">%2$s</xliff:g>열로 이동"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g>의 <xliff:g id="NUMBER_0">%1$s</xliff:g>행 <xliff:g id="NUMBER_1">%2$s</xliff:g>열로 이동"</string> <string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g>번 위치로 이동"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"즐겨찾는 <xliff:g id="NUMBER">%1$s</xliff:g>번 위치로 이동"</string> <string name="item_moved" msgid="4606538322571412879">"항목을 이동했습니다."</string> diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml index ea4a7d2a85..3a58b85d8a 100644 --- a/res/values-ky/strings.xml +++ b/res/values-ky/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">"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> @@ -86,7 +85,7 @@ <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> телефон чалууларды аткарууга уруксаты жок"</string> <string name="gadget_error_text" msgid="740356548025791839">"Виджет жүктөлбөй жатат"</string> <string name="gadget_setup_text" msgid="8348374825537681407">"Виджеттин жөндөөлөрү"</string> - <string name="gadget_complete_setup_text" msgid="309040266978007925">"Жөндөп бүтүү үчүн таптап коюңуз"</string> + <string name="gadget_complete_setup_text" msgid="309040266978007925">"Аягына чейин тууралоо үчүн басып коюңуз"</string> <string name="uninstall_system_app_text" msgid="4172046090762920660">"Бул системдик колдонмо жана аны чечкенге болбойт."</string> <string name="folder_hint_text" msgid="5174843001373488816">"Аталышын түзөтүү"</string> <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> өчүрүлгөн"</string> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Жоюлду"</string> <string name="undo" msgid="4151576204245173321">"Кайтаруу"</string> <string name="action_move" msgid="4339390619886385032">"Муну жылдыруу"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"<xliff:g id="NUMBER_0">%1$s</xliff:g> катарга <xliff:g id="NUMBER_1">%2$s</xliff:g> тилкеге жылдыруу"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g> ичиндеги <xliff:g id="NUMBER_1">%2$s</xliff:g>-тилкенин <xliff:g id="NUMBER_0">%1$s</xliff:g>-cабына жылдыруу"</string> <string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g> орунга жылдыруу"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"Тандалмаларга <xliff:g id="NUMBER">%1$s</xliff:g> жылдыруу"</string> <string name="item_moved" msgid="4606538322571412879">"Нерсе жылдырылды"</string> diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml index 7291cf2a34..d8b55f8471 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"ເອົາລາຍການອອກໄປແລ້ວ"</string> <string name="undo" msgid="4151576204245173321">"ຍົກເລີກ"</string> <string name="action_move" msgid="4339390619886385032">"ຍ້າຍລາຍການ"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"ຍ້າຍໄປໃສ່ແຖວ <xliff:g id="NUMBER_0">%1$s</xliff:g> ຖັນ <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"ຍ້າຍໄປແຖວ <xliff:g id="NUMBER_0">%1$s</xliff:g> ຖັນ <xliff:g id="NUMBER_1">%2$s</xliff:g> ໃນ <xliff:g id="STRING">%3$s</xliff:g>"</string> <string name="move_to_position" msgid="6750008980455459790">"ຍ້າຍໄປໃສ່ຕຳແໜ່ງ <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"ຍ້າຍໄປໃສ່ຕຳແໜ່ງທີ່ມັກ <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="item_moved" msgid="4606538322571412879">"ຍ້າຍລາຍການແລ້ວ"</string> diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml index ddff9d4e06..e68362d26a 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Elementas perkeltas"</string> <string name="undo" msgid="4151576204245173321">"Anuliuoti"</string> <string name="action_move" msgid="4339390619886385032">"Perkelti elementą"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"Perkelti į <xliff:g id="NUMBER_0">%1$s</xliff:g> eilutę, <xliff:g id="NUMBER_1">%2$s</xliff:g> stulpelį"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Perkelti į <xliff:g id="NUMBER_0">%1$s</xliff:g> eilutės <xliff:g id="NUMBER_1">%2$s</xliff:g> stulpelį, esantį <xliff:g id="STRING">%3$s</xliff:g>"</string> <string name="move_to_position" msgid="6750008980455459790">"Perkelti į <xliff:g id="NUMBER">%1$s</xliff:g> poziciją"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"Perkelti į <xliff:g id="NUMBER">%1$s</xliff:g> mėgstamiausių poziciją"</string> <string name="item_moved" msgid="4606538322571412879">"Elementas perkeltas"</string> diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml index a5b7c66e2a..75f6d1ad2e 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Vienums noņemts"</string> <string name="undo" msgid="4151576204245173321">"Atsaukt"</string> <string name="action_move" msgid="4339390619886385032">"Pārvietot vienumu"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"Pārvietot uz <xliff:g id="NUMBER_0">%1$s</xliff:g>. rindu, <xliff:g id="NUMBER_1">%2$s</xliff:g>. kolonnu"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Pārvietot uz rindu numur <xliff:g id="NUMBER_0">%1$s</xliff:g>, kolonnu numur <xliff:g id="NUMBER_1">%2$s</xliff:g> šajā ekrānā: <xliff:g id="STRING">%3$s</xliff:g>"</string> <string name="move_to_position" msgid="6750008980455459790">"Pārvietot uz <xliff:g id="NUMBER">%1$s</xliff:g>. pozīciju"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"Pārvietot uz <xliff:g id="NUMBER">%1$s</xliff:g>. izlases pozīciju"</string> <string name="item_moved" msgid="4606538322571412879">"Vienums pārvietots"</string> diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml index 32c3fc0072..fef9bbe106 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Ставката е отстранета"</string> <string name="undo" msgid="4151576204245173321">"Врати"</string> <string name="action_move" msgid="4339390619886385032">"Премести ја ставката"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"Премести во ред <xliff:g id="NUMBER_0">%1$s</xliff:g> колона <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Преместете во редица <xliff:g id="NUMBER_0">%1$s</xliff:g>, колона <xliff:g id="NUMBER_1">%2$s</xliff:g> во <xliff:g id="STRING">%3$s</xliff:g>"</string> <string name="move_to_position" msgid="6750008980455459790">"Премести на место <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"Премести на место <xliff:g id="NUMBER">%1$s</xliff:g> во омилени"</string> <string name="item_moved" msgid="4606538322571412879">"Ставката е преместена"</string> diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml index d25f9dd633..802ea81418 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"ഇനം നീക്കംചെയ്തു"</string> <string name="undo" msgid="4151576204245173321">"പഴയപടിയാക്കുക"</string> <string name="action_move" msgid="4339390619886385032">"ഇനം നീക്കുക"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"വരി <xliff:g id="NUMBER_0">%1$s</xliff:g> നിര <xliff:g id="NUMBER_1">%2$s</xliff:g>-ലേക്ക് നീക്കുക"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g> എന്നതിലെ <xliff:g id="NUMBER_1">%2$s</xliff:g>-ാം കോളത്തിലെ <xliff:g id="NUMBER_0">%1$s</xliff:g>-ാം വരിയിലേക്ക് നീക്കുക"</string> <string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g>-ലേക്ക് നീക്കുക"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"ഇഷ്ടമുള്ള <xliff:g id="NUMBER">%1$s</xliff:g> സ്ഥാനത്തേക്ക് നീക്കുക"</string> <string name="item_moved" msgid="4606538322571412879">"ഇനം നീക്കി"</string> diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml index 0723c25fa9..f091cb817f 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Зүйлийг устгалаа"</string> <string name="undo" msgid="4151576204245173321">"Болих"</string> <string name="action_move" msgid="4339390619886385032">"Зөөх"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"<xliff:g id="NUMBER_0">%1$s</xliff:g> мөр <xliff:g id="NUMBER_1">%2$s</xliff:g> баганад зөөх"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g> дахь <xliff:g id="NUMBER_0">%1$s</xliff:g>-р мөр, <xliff:g id="NUMBER_1">%2$s</xliff:g>-р багана руу зөөх"</string> <string name="move_to_position" msgid="6750008980455459790">"Байршил <xliff:g id="NUMBER">%1$s</xliff:g>-д зөөх"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"Дуртай байршил болох <xliff:g id="NUMBER">%1$s</xliff:g>-д зөөх"</string> <string name="item_moved" msgid="4606538322571412879">"Зөөвөрлөсөн зүйл"</string> diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml index 06c1da78b2..b553c90590 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"आयटम काढला"</string> <string name="undo" msgid="4151576204245173321">"पूर्ववत करा"</string> <string name="action_move" msgid="4339390619886385032">"आयटम हलवा"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"पंक्ति <xliff:g id="NUMBER_0">%1$s</xliff:g> स्तंभ <xliff:g id="NUMBER_1">%2$s</xliff:g> मध्ये हलवा"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g> मधील <xliff:g id="NUMBER_0">%1$s</xliff:g> पंक्ती <xliff:g id="NUMBER_1">%2$s</xliff:g> स्तंभ यावर हलवा"</string> <string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g> स्थानावर हलवा"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"आवडत्या <xliff:g id="NUMBER">%1$s</xliff:g> स्थानावर हलवा"</string> <string name="item_moved" msgid="4606538322571412879">"आयटम हलविला"</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 d123d14c4d..edd05302d4 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Item dialih keluar"</string> <string name="undo" msgid="4151576204245173321">"Buat asal"</string> <string name="action_move" msgid="4339390619886385032">"Alihkan Item"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"Alihkan ke baris <xliff:g id="NUMBER_0">%1$s</xliff:g> lajur <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Alihkan ke baris <xliff:g id="NUMBER_0">%1$s</xliff:g> lajur <xliff:g id="NUMBER_1">%2$s</xliff:g> dalam <xliff:g id="STRING">%3$s</xliff:g>"</string> <string name="move_to_position" msgid="6750008980455459790">"Alihkan ke kedudukan <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"Alihkan ke kedudukan kegemaran <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="item_moved" msgid="4606538322571412879">"Item dialihkan"</string> diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml index aef6169fe1..69157bc330 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"ဖယ်ရှားပြီးပြီ"</string> <string name="undo" msgid="4151576204245173321">"နောက်ပြန်ရန်"</string> <string name="action_move" msgid="4339390619886385032">"၎င်းအား ရွှေ့ပါ"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"အတန်း <xliff:g id="NUMBER_0">%1$s</xliff:g> အတိုင် <xliff:g id="NUMBER_1">%2$s</xliff:g> သို့ ရွှေ့ပါ"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g> တွင် အတန်း <xliff:g id="NUMBER_0">%1$s</xliff:g> ကော်လံ <xliff:g id="NUMBER_1">%2$s</xliff:g> သို့ ရွှေ့နိုင်သည်"</string> <string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g> သို့ နေရာရွှေ့ပါ"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"စိတ်ကြိုက်နေရာ <xliff:g id="NUMBER">%1$s</xliff:g> သို့ ရွှေ့ပါ"</string> <string name="item_moved" msgid="4606538322571412879">"၎င်းအားရွှေ့ပြီး"</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 eba195c8bd..3e08d1000d 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Elementet er fjernet"</string> <string name="undo" msgid="4151576204245173321">"Angre"</string> <string name="action_move" msgid="4339390619886385032">"Flytt elementet"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"Flytt til rad <xliff:g id="NUMBER_0">%1$s</xliff:g>, kolonne <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Flytt til rad <xliff:g id="NUMBER_0">%1$s</xliff:g> kolonne <xliff:g id="NUMBER_1">%2$s</xliff:g> i <xliff:g id="STRING">%3$s</xliff:g>"</string> <string name="move_to_position" msgid="6750008980455459790">"Flytt til posisjon <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"Flytt til posisjonen for favoritter <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="item_moved" msgid="4606538322571412879">"Elementet er flyttet"</string> diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml index 49d92ac726..6d48969f76 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> @@ -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">"गृह स्क्रिन %1$d को %2$d"</string> - <string name="workspace_new_page" msgid="257366611030256142">"नयाँ गृह स्क्रिन पृष्ठ"</string> + <string name="workspace_scroll_format" msgid="8458889198184077399">"होम स्क्रिन %1$d को %2$d"</string> + <string name="workspace_new_page" msgid="257366611030256142">"नयाँ होम स्क्रिन पृष्ठ"</string> <string name="folder_opened" msgid="94695026776264709">"फोल्डर खुल्यो <xliff:g id="WIDTH">%1$d</xliff:g> बाट <xliff:g id="HEIGHT">%2$d</xliff:g>"</string> <string name="folder_tap_to_close" msgid="4625795376335528256">"फोल्डरलाई बन्द गर्न ट्याप गर्नुहोस्"</string> <string name="folder_tap_to_rename" msgid="4017685068016979677">"पुनःनामाकरणलाई सुरक्षित गर्न ट्याप गर्नुहोस्"</string> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"वस्तु हटाइयो"</string> <string name="undo" msgid="4151576204245173321">"अन्डू गर्नुहोस्"</string> <string name="action_move" msgid="4339390619886385032">"वस्तु सार्नुहोस्"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"पङ्क्ति <xliff:g id="NUMBER_0">%1$s</xliff:g> स्तम्भ <xliff:g id="NUMBER_1">%2$s</xliff:g> मा सार्नुहोस्"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"यो वस्तु सारेर <xliff:g id="STRING">%3$s</xliff:g> मा रहेको रो <xliff:g id="NUMBER_0">%1$s</xliff:g> कोलम <xliff:g id="NUMBER_1">%2$s</xliff:g> मा लैजानुहोस्"</string> <string name="move_to_position" msgid="6750008980455459790">"स्थिति <xliff:g id="NUMBER">%1$s</xliff:g> मा सार्नुहोस्"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"मन पर्ने स्थिति <xliff:g id="NUMBER">%1$s</xliff:g> मा सार्नुहोस्"</string> <string name="item_moved" msgid="4606538322571412879">"वस्तु सारियो"</string> diff --git a/res/values-night-v31/colors.xml b/res/values-night-v31/colors.xml index eefe8c5f81..54d6d8876f 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 ce272ceade..17fe419a8c 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 e19b36140e..4b203b730a 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Item verwijderd"</string> <string name="undo" msgid="4151576204245173321">"Ongedaan maken"</string> <string name="action_move" msgid="4339390619886385032">"Item verplaatsen"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"Verplaatsen naar rij <xliff:g id="NUMBER_0">%1$s</xliff:g>, kolom <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Verplaatsen naar rij <xliff:g id="NUMBER_0">%1$s</xliff:g> kolom <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">"Verplaatsen naar positie <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"Verplaatsen naar positie <xliff:g id="NUMBER">%1$s</xliff:g> voor favorieten"</string> <string name="item_moved" msgid="4606538322571412879">"Item verplaatst"</string> diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml index 2506200641..c64e9b2aac 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,48 +52,47 @@ <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> <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="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}ର #ଟି ବିଜ୍ଞପ୍ତି ଅଛି}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,13 +130,13 @@ <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> - <string name="move_to_empty_cell" msgid="2833711483015685619">"ଧାଡ଼ି <xliff:g id="NUMBER_0">%1$s</xliff:g> ସ୍ତମ୍ଭ <xliff:g id="NUMBER_1">%2$s</xliff:g>କୁ ନିଅନ୍ତୁ"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g>ରେ ଧାଡି <xliff:g id="NUMBER_0">%1$s</xliff:g> ସ୍ତମ୍ଭ <xliff:g id="NUMBER_1">%2$s</xliff:g>କୁ ମୁଭ କରନ୍ତୁ"</string> <string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g> ସ୍ଥିତିକୁ ନିଅନ୍ତୁ"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"ପସନ୍ଦର ସ୍ଥିତି <xliff:g id="NUMBER">%1$s</xliff:g>କୁ ନିଅନ୍ତୁ"</string> <string name="item_moved" msgid="4606538322571412879">"ଆଇଟମ୍ ଘୁଞ୍ଚେଇ ଦିଆଗଲା"</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 3df68868a5..8e55b87e3e 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"ਆਈਟਮ ਹਟਾਈ ਗਈ"</string> <string name="undo" msgid="4151576204245173321">"ਅਣਕੀਤਾ ਕਰੋ"</string> <string name="action_move" msgid="4339390619886385032">"ਆਈਟਮ ਨੂੰ ਮੂਵ ਕਰੋ"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"ਕਤਾਰ <xliff:g id="NUMBER_0">%1$s</xliff:g> ਕਾਲਮ <xliff:g id="NUMBER_1">%2$s</xliff:g> ਵਿੱਚ ਮੂਵ ਕਰੋ"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g> ਵਿੱਚ ਕਤਾਰ <xliff:g id="NUMBER_0">%1$s</xliff:g> ਦੇ ਕਾਲਮ <xliff:g id="NUMBER_1">%2$s</xliff:g> \'ਤੇ ਜਾਓ"</string> <string name="move_to_position" msgid="6750008980455459790">"ਸਥਿਤੀ <xliff:g id="NUMBER">%1$s</xliff:g> ਵਿੱਚ ਮੂਵ ਕਰੋ"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"ਮਨਪਸੰਦ ਸਥਿਤੀ <xliff:g id="NUMBER">%1$s</xliff:g> ਵਿੱਚ ਮੂਵ ਕਰੋ"</string> <string name="item_moved" msgid="4606538322571412879">"ਆਈਟਮ ਮੂਵ ਕੀਤੀ ਗਈ"</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 869e91b68f..86699499a5 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Element został usunięty"</string> <string name="undo" msgid="4151576204245173321">"Cofnij"</string> <string name="action_move" msgid="4339390619886385032">"Przenieś element"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"Przenieś do wiersza <xliff:g id="NUMBER_0">%1$s</xliff:g> w kolumnie <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Przenieś do wiersza <xliff:g id="NUMBER_0">%1$s</xliff:g> w kolumnie <xliff:g id="NUMBER_1">%2$s</xliff:g>, komórka: <xliff:g id="STRING">%3$s</xliff:g>"</string> <string name="move_to_position" msgid="6750008980455459790">"Przenieś do pozycji <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"Przenieś do pozycji ulubionych: <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="item_moved" msgid="4606538322571412879">"Element został przeniesiony"</string> diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml index 1512754d30..bb1fb36b77 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Item removido"</string> <string name="undo" msgid="4151576204245173321">"Anular"</string> <string name="action_move" msgid="4339390619886385032">"Mover item"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"Mover para a linha <xliff:g id="NUMBER_0">%1$s</xliff:g>, coluna <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Mova para a linha <xliff:g id="NUMBER_0">%1$s</xliff:g>, coluna <xliff:g id="NUMBER_1">%2$s</xliff:g> em <xliff:g id="STRING">%3$s</xliff:g>"</string> <string name="move_to_position" msgid="6750008980455459790">"Mover para a posição <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"Mover para a posição <xliff:g id="NUMBER">%1$s</xliff:g> dos favoritos"</string> <string name="item_moved" msgid="4606538322571412879">"Item movido"</string> @@ -168,7 +167,7 @@ <string name="work_apps_paused_content_description" msgid="5149623040804051095">"As apps de trabalho estão desativadas. As apps de trabalho não podem enviar-lhe notificações, utilizar a bateria ou aceder à sua localização"</string> <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"As apps de trabalho têm um emblema e estão visíveis para o seu administrador de TI"</string> <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"OK"</string> - <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Colocar apps de trabalho em pausa"</string> + <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Pausar apps de trabalho"</string> <string name="work_apps_enable_btn_text" msgid="1156432622148413741">"Ativar apps de trabalho"</string> <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtrar"</string> <string name="search_pref_screen_title" msgid="3258959643336315962">"Pesquise no telemóvel"</string> diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml index 2dcb98f804..98e8607185 100644 --- a/res/values-pt/strings.xml +++ b/res/values-pt/strings.xml @@ -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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Item removido"</string> <string name="undo" msgid="4151576204245173321">"Desfazer"</string> <string name="action_move" msgid="4339390619886385032">"Mover item"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"Mover para a linha <xliff:g id="NUMBER_0">%1$s</xliff:g>, coluna <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Mover para a linha <xliff:g id="NUMBER_0">%1$s</xliff:g>, coluna <xliff:g id="NUMBER_1">%2$s</xliff:g> na <xliff:g id="STRING">%3$s</xliff:g>"</string> <string name="move_to_position" msgid="6750008980455459790">"Mover para a posição <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"Mover para a posição <xliff:g id="NUMBER">%1$s</xliff:g> dos favoritos"</string> <string name="item_moved" msgid="4606538322571412879">"Item movido"</string> diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml index ee9f492f78..794609e25e 100644 --- a/res/values-ro/strings.xml +++ b/res/values-ro/strings.xml @@ -28,75 +28,74 @@ <string name="shortcut_not_available" msgid="2536503539825726397">"Comanda rapidă nu este disponibilă"</string> <string name="home_screen" msgid="5629429142036709174">"Pagina de pornire"</string> <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Ecran împărțit"</string> - <string name="split_screen_position_top" msgid="1504965011158689649">"Împărțiți în sus"</string> - <string name="split_screen_position_left" msgid="7537793098851830883">"Împărțiți în stânga"</string> - <string name="split_screen_position_right" msgid="1569377524925193369">"Împărțiți în dreapta"</string> + <string name="split_screen_position_top" msgid="1504965011158689649">"Împarte în sus"</string> + <string name="split_screen_position_left" msgid="7537793098851830883">"Împarte în stânga"</string> + <string name="split_screen_position_right" msgid="1569377524925193369">"Împarte în dreapta"</string> <string name="split_app_info_accessibility" msgid="5475288491241414932">"Informații despre aplicație pentru %1$s"</string> - <string name="long_press_widget_to_add" msgid="3587712543577675817">"Atingeți și țineți apăsat pentru a muta un widget."</string> - <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Atingeți de două ori și țineți apăsat pentru a muta un widget sau folosiți acțiuni personalizate."</string> + <string name="long_press_widget_to_add" msgid="3587712543577675817">"Atinge și ține apăsat pentru a muta un widget."</string> + <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Atinge de două ori și ține apăsat pentru a muta un widget sau folosește acțiuni personalizate."</string> <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string> <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d lățime și %2$d înălțime"</string> <string name="widget_preview_context_description" msgid="9045841361655787574">"Widgetul <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string> - <string name="add_item_request_drag_hint" msgid="8730547755622776606">"Atingeți lung widgetul pentru a-l muta pe ecranul de pornire"</string> - <string name="add_to_home_screen" msgid="9168649446635919791">"Adăugați pe ecranul de pornire"</string> + <string name="add_item_request_drag_hint" msgid="8730547755622776606">"Atinge lung widgetul pentru a-l muta pe ecranul de pornire"</string> + <string name="add_to_home_screen" msgid="9168649446635919791">"Adaugă pe ecranul de pornire"</string> <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Widgetul <xliff:g id="WIDGET_NAME">%1$s</xliff:g> a fost adăugat pe ecranul de pornire"</string> <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}few{# widgeturi}other{# de widgeturi}}"</string> <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# comandă rapidă}few{# comenzi rapide}other{# de comenzi rapide}}"</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">"Widgeturi"</string> <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Căutare"</string> - <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Ștergeți textul din caseta de căutare"</string> + <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Șterge textul din caseta de căutare"</string> <string name="no_widgets_available" msgid="4337693382501046170">"Widgeturile și comenzile rapide nu sunt disponibile"</string> <string name="no_search_results" msgid="3787956167293097509">"Nu au fost găsite widgeturi sau comenzi rapide"</string> <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Personale"</string> <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Serviciu"</string> <string name="widget_category_conversations" msgid="8894438636213590446">"Conversații"</string> - <string name="widget_education_header" msgid="4874760613775913787">"Informații utile la îndemâna dvs."</string> - <string name="widget_education_content" msgid="1731667670753497052">"Pentru a primi informații fără să deschideți aplicațiile, puteți adăuga widgeturi pe ecranul de pornire"</string> - <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Atingeți ca să schimbați setările pentru widgeturi"</string> + <string name="widget_education_header" msgid="4874760613775913787">"Informații utile la îndemâna ta"</string> + <string name="widget_education_content" msgid="1731667670753497052">"Pentru a primi informații fără să deschizi aplicațiile, poți adăuga widgeturi pe ecranul de pornire"</string> + <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Atinge ca să schimbi setările pentru widgeturi"</string> <string name="widget_education_close_button" msgid="8676165703104836580">"OK"</string> - <string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Modificați setările pentru widgeturi"</string> - <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Căutați aplicații"</string> + <string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Modifică setările pentru widgeturi"</string> + <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Caută aplicații"</string> <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">"Căutați 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> - <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Atingeți și țineți apăsat pentru a muta comanda rapidă."</string> - <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Atingeți de două ori și țineți apăsat pentru a muta o comandă rapidă sau folosiți acțiuni personalizate."</string> + <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Atinge și ține apăsat ca să muți comanda rapidă."</string> + <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Atinge de două ori și ține apăsat pentru a muta o comandă rapidă sau folosește acțiuni personalizate."</string> <string name="out_of_space" msgid="6455557115204099579">"Nu există spațiu liber pe acest ecran de pornire"</string> <string name="hotseat_out_of_space" msgid="7448809638125333693">"Spațiu epuizat în bara Preferate"</string> <string name="all_apps_button_label" msgid="8130441508702294465">"Lista de aplicații"</string> <string name="all_apps_search_results" msgid="5889367432531296759">"Rezultatele căutării"</string> <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Lista de aplicații personale"</string> <string name="all_apps_button_work_label" msgid="7270707118948892488">"Lista de aplicații de serviciu"</string> - <string name="remove_drop_target_label" msgid="7812859488053230776">"Eliminați"</string> - <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Dezinstalați"</string> + <string name="remove_drop_target_label" msgid="7812859488053230776">"Elimină"</string> + <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Dezinstalează"</string> <string name="app_info_drop_target_label" msgid="692894985365717661">"Informații despre aplicații"</string> - <string name="install_drop_target_label" msgid="2539096853673231757">"Instalați"</string> + <string name="install_drop_target_label" msgid="2539096853673231757">"Instalează"</string> <string name="dismiss_prediction_label" msgid="3357562989568808658">"Nu sugera aplicația"</string> <string name="pin_prediction" msgid="4196423321649756498">"Fixează predicția"</string> <string name="permlab_install_shortcut" msgid="5632423390354674437">"instalează comenzi rapide"</string> <string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite unei aplicații să adauge comenzi rapide fără intervenția utilizatorului."</string> - <string name="permlab_read_settings" msgid="5136500343007704955">"citiți setările și comenzile rapide de pe ecranul de pornire"</string> + <string name="permlab_read_settings" msgid="5136500343007704955">"citește setările și comenzile rapide de pe ecranul de pornire"</string> <string name="permdesc_read_settings" msgid="4208061150510996676">"Permite aplicației să citească setările și comenzile rapide de pe ecranul de pornire."</string> - <string name="permlab_write_settings" msgid="4820028712156303762">"scrieți setările și comenzile rapide de pe ecranul de pornire"</string> + <string name="permlab_write_settings" msgid="4820028712156303762">"scrie setările și comenzile rapide de pe ecranul de pornire"</string> <string name="permdesc_write_settings" msgid="726859348127868466">"Permite aplicației să modifice setările și comenzile rapide de pe ecranul de pornire."</string> <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> nu are permisiunea de a apela"</string> <string name="gadget_error_text" msgid="740356548025791839">"Widgetul nu poate fi încărcat"</string> <string name="gadget_setup_text" msgid="8348374825537681407">"Setări pentru widget"</string> - <string name="gadget_complete_setup_text" msgid="309040266978007925">"Atingeți pentru a finaliza configurarea"</string> + <string name="gadget_complete_setup_text" msgid="309040266978007925">"Atinge pentru a finaliza configurarea"</string> <string name="uninstall_system_app_text" msgid="4172046090762920660">"Aceasta este o aplicație de sistem și nu poate fi dezinstalată."</string> - <string name="folder_hint_text" msgid="5174843001373488816">"Modificați numele"</string> + <string name="folder_hint_text" msgid="5174843001373488816">"Modifică numele"</string> <string name="disabled_app_label" msgid="6673129024321402780">"S-a dezactivat <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="dotted_app_label" msgid="1865617679843363410">"{count,plural, =1{{app_name} are # notificare}few{{app_name} are # notificări}other{{app_name} are # de notificări}}"</string> <string name="default_scroll_format" msgid="7475544710230993317">"Pagina %1$d din %2$d"</string> <string name="workspace_scroll_format" msgid="8458889198184077399">"Ecranul de pornire %1$d din %2$d"</string> <string name="workspace_new_page" msgid="257366611030256142">"Pagină nouă pe ecranul de pornire"</string> <string name="folder_opened" msgid="94695026776264709">"Dosar deschis, <xliff:g id="WIDTH">%1$d</xliff:g> pe <xliff:g id="HEIGHT">%2$d</xliff:g>"</string> - <string name="folder_tap_to_close" msgid="4625795376335528256">"Atingeți pentru a închide dosarul"</string> - <string name="folder_tap_to_rename" msgid="4017685068016979677">"Atingeți pentru a salva noul nume"</string> + <string name="folder_tap_to_close" msgid="4625795376335528256">"Atinge pentru a închide dosarul"</string> + <string name="folder_tap_to_rename" msgid="4017685068016979677">"Atinge pentru a salva noul nume"</string> <string name="folder_closed" msgid="4100806530910930934">"Dosar închis"</string> <string name="folder_renamed" msgid="1794088362165669656">"Dosar redenumit <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="folder_name_format_exact" msgid="8626242716117004803">"Dosar: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> elemente"</string> @@ -105,58 +104,58 @@ <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Imagine de fundal și stil"</string> <string name="settings_button_text" msgid="8873672322605444408">"Setări ecran de pornire"</string> <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Dezactivată de administrator"</string> - <string name="allow_rotation_title" msgid="7222049633713050106">"Permiteți rotirea ecranului de pornire"</string> + <string name="allow_rotation_title" msgid="7222049633713050106">"Permite rotirea ecranului de pornire"</string> <string name="allow_rotation_desc" msgid="8662546029078692509">"Când telefonul este rotit"</string> <string name="notification_dots_title" msgid="9062440428204120317">"Puncte de notificare"</string> <string name="notification_dots_desc_on" msgid="1679848116452218908">"Activate"</string> <string name="notification_dots_desc_off" msgid="1760796511504341095">"Dezactivate"</string> <string name="title_missing_notification_access" msgid="7503287056163941064">"Este necesar accesul la notificări"</string> - <string name="msg_missing_notification_access" msgid="281113995110910548">"Pentru a afișa punctele de notificare, activați notificările din aplicație pentru <xliff:g id="NAME">%1$s</xliff:g>"</string> - <string name="title_change_settings" msgid="1376365968844349552">"Modificați setările"</string> - <string name="notification_dots_service_title" msgid="4284221181793592871">"Afișați punctele de notificare"</string> + <string name="msg_missing_notification_access" msgid="281113995110910548">"Pentru a afișa punctele de notificare, activează notificările din aplicație pentru <xliff:g id="NAME">%1$s</xliff:g>"</string> + <string name="title_change_settings" msgid="1376365968844349552">"Modifică setările"</string> + <string name="notification_dots_service_title" msgid="4284221181793592871">"Afișează punctele de notificare"</string> <string name="developer_options_title" msgid="700788437593726194">"Opțiuni dezvoltator"</string> - <string name="auto_add_shortcuts_label" msgid="4926805029653694105">"Adăugați pictograme de aplicații pe ecranul de pornire"</string> + <string name="auto_add_shortcuts_label" msgid="4926805029653694105">"Adaugă pictograme de aplicații pe ecranul de pornire"</string> <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Pentru aplicații noi"</string> <string name="package_state_unknown" msgid="7592128424511031410">"Necunoscut"</string> - <string name="abandoned_clean_this" msgid="7610119707847920412">"Eliminați"</string> - <string name="abandoned_search" msgid="891119232568284442">"Căutați"</string> + <string name="abandoned_clean_this" msgid="7610119707847920412">"Elimină"</string> + <string name="abandoned_search" msgid="891119232568284442">"Caută"</string> <string name="abandoned_promises_title" msgid="7096178467971716750">"Aplicația nu este instalată"</string> - <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Aplicația pentru această pictogramă nu este instalată. Puteți să ștergeți pictograma sau să căutați aplicația și s-o instalați manual."</string> + <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Aplicația pentru această pictogramă nu este instalată. Poți să ștergi pictograma sau să cauți aplicația și s-o instalezi manual."</string> <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> se instalează, <xliff:g id="PROGRESS">%2$s</xliff:g> finalizat"</string> <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> se descarcă (finalizat <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> așteaptă instalarea"</string> <string name="dialog_update_title" msgid="114234265740994042">"Este necesară actualizarea aplicației"</string> - <string name="dialog_update_message" msgid="4176784553982226114">"Aplicația pentru această pictogramă nu este actualizată. Puteți să actualizați manual ca să reactivați comanda rapidă sau să eliminați pictograma."</string> - <string name="dialog_update" msgid="2178028071796141234">"Actualizați"</string> - <string name="dialog_remove" msgid="6510806469849709407">"Eliminați"</string> + <string name="dialog_update_message" msgid="4176784553982226114">"Aplicația pentru această pictogramă nu este actualizată. Poți să actualizezi manual ca să reactivezi comanda rapidă sau să elimini pictograma."</string> + <string name="dialog_update" msgid="2178028071796141234">"Actualizează"</string> + <string name="dialog_remove" msgid="6510806469849709407">"Elimină"</string> <string name="widgets_list" msgid="796804551140113767">"Listă de widgeturi"</string> <string name="widgets_list_closed" msgid="6141506579418771922">"Lista de widgeturi este închisă"</string> - <string name="action_add_to_workspace" msgid="215894119683164916">"Adăugați pe ecranul de pornire"</string> - <string name="action_move_here" msgid="2170188780612570250">"Mutați elementul aici"</string> + <string name="action_add_to_workspace" msgid="215894119683164916">"Adaugă pe ecranul de pornire"</string> + <string name="action_move_here" msgid="2170188780612570250">"Mută elementul aici"</string> <string name="item_added_to_workspace" msgid="4211073925752213539">"Element adăugat pe ecranul de pornire"</string> <string name="item_removed" msgid="851119963877842327">"Element eliminat"</string> - <string name="undo" msgid="4151576204245173321">"Anulați"</string> - <string name="action_move" msgid="4339390619886385032">"Mutați elementul"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"Mutați pe rândul <xliff:g id="NUMBER_0">%1$s</xliff:g>, coloana <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string> - <string name="move_to_position" msgid="6750008980455459790">"Mutați pe poziția <xliff:g id="NUMBER">%1$s</xliff:g>"</string> - <string name="move_to_hotseat_position" msgid="6295412897075147808">"Mutați în preferate, pe poziția <xliff:g id="NUMBER">%1$s</xliff:g>"</string> + <string name="undo" msgid="4151576204245173321">"Anulează"</string> + <string name="action_move" msgid="4339390619886385032">"Mută elementul"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Mută în rândul <xliff:g id="NUMBER_0">%1$s</xliff:g> coloana <xliff:g id="NUMBER_1">%2$s</xliff:g> din <xliff:g id="STRING">%3$s</xliff:g>"</string> + <string name="move_to_position" msgid="6750008980455459790">"Mută pe poziția <xliff:g id="NUMBER">%1$s</xliff:g>"</string> + <string name="move_to_hotseat_position" msgid="6295412897075147808">"Mută în preferate, pe poziția <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="item_moved" msgid="4606538322571412879">"Element mutat"</string> - <string name="add_to_folder" msgid="9040534766770853243">"Adăugați în dosar: <xliff:g id="NAME">%1$s</xliff:g>"</string> - <string name="add_to_folder_with_app" msgid="4534929978967147231">"Adăugați în dosarul cu <xliff:g id="NAME">%1$s</xliff:g>"</string> + <string name="add_to_folder" msgid="9040534766770853243">"Adaugă în dosar: <xliff:g id="NAME">%1$s</xliff:g>"</string> + <string name="add_to_folder_with_app" msgid="4534929978967147231">"Adaugă în dosarul cu <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="added_to_folder" msgid="4793259502305558003">"Element adăugat în dosar"</string> - <string name="create_folder_with" msgid="4050141361160214248">"Creați dosar cu: <xliff:g id="NAME">%1$s</xliff:g>"</string> + <string name="create_folder_with" msgid="4050141361160214248">"Creează dosar cu: <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="folder_created" msgid="6409794597405184510">"Dosar creat"</string> - <string name="action_move_to_workspace" msgid="39528912300293768">"Mutați pe ecranul de pornire"</string> - <string name="action_resize" msgid="1802976324781771067">"Redimensionați"</string> - <string name="action_increase_width" msgid="8773715375078513326">"Creșteți lățimea"</string> - <string name="action_increase_height" msgid="459390020612501122">"Creșteți înălțimea"</string> - <string name="action_decrease_width" msgid="1374549771083094654">"Reduceți lățimea"</string> - <string name="action_decrease_height" msgid="282377193880900022">"Reduceți înălțimea"</string> + <string name="action_move_to_workspace" msgid="39528912300293768">"Mută pe ecranul de pornire"</string> + <string name="action_resize" msgid="1802976324781771067">"Redimensionează"</string> + <string name="action_increase_width" msgid="8773715375078513326">"Crește lățimea"</string> + <string name="action_increase_height" msgid="459390020612501122">"Crește înălțimea"</string> + <string name="action_decrease_width" msgid="1374549771083094654">"Redu lățimea"</string> + <string name="action_decrease_height" msgid="282377193880900022">"Redu înălțimea"</string> <string name="widget_resized" msgid="9130327887929620">"Widgetul a fost redimensionat la lățimea <xliff:g id="NUMBER_0">%1$s</xliff:g> și înălțimea <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string> <string name="action_deep_shortcut" msgid="2864038805849372848">"Comenzi rapide"</string> <string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"Comenzi rapide și notificări"</string> - <string name="action_dismiss_notification" msgid="5909461085055959187">"Închideți"</string> - <string name="accessibility_close" msgid="2277148124685870734">"Închideți"</string> + <string name="action_dismiss_notification" msgid="5909461085055959187">"Închide"</string> + <string name="accessibility_close" msgid="2277148124685870734">"Închide"</string> <string name="notification_dismissed" msgid="6002233469409822874">"Notificare închisă"</string> <string name="all_apps_personal_tab" msgid="4190252696685155002">"Personale"</string> <string name="all_apps_work_tab" msgid="4884822796154055118">"Profesionale"</string> @@ -164,14 +163,14 @@ <string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Aplicațiile pentru lucru sunt marcate și vizibile pentru administratorul IT"</string> <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string> <string name="work_apps_paused_title" msgid="3040901117349444598">"Aplicațiile pentru lucru sunt întrerupte"</string> - <string name="work_apps_paused_body" msgid="261634750995824906">"Aplicațiile pentru lucru nu pot să vă trimită notificări, să folosească bateria sau să vă acceseze locația"</string> - <string name="work_apps_paused_content_description" msgid="5149623040804051095">"Aplicațiile pentru lucru sunt dezactivate. Acestea nu pot să vă trimită notificări, să folosească bateria sau să vă acceseze locația."</string> + <string name="work_apps_paused_body" msgid="261634750995824906">"Aplicațiile pentru lucru nu pot să-ți trimită notificări, să folosească bateria sau să-ți acceseze locația"</string> + <string name="work_apps_paused_content_description" msgid="5149623040804051095">"Aplicațiile pentru lucru sunt dezactivate. Acestea nu pot să-ți trimită notificări, să folosească bateria sau să-ți acceseze locația."</string> <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Aplicațiile pentru lucru sunt marcate și vizibile pentru administratorul IT"</string> <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"OK"</string> - <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Întrerupeți aplicațiile pentru lucru"</string> - <string name="work_apps_enable_btn_text" msgid="1156432622148413741">"Activați aplicațiile pentru lucru"</string> + <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Întrerupe aplicațiile pentru lucru"</string> + <string name="work_apps_enable_btn_text" msgid="1156432622148413741">"Activează aplicațiile pentru lucru"</string> <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtru"</string> - <string name="search_pref_screen_title" msgid="3258959643336315962">"Căutați pe telefon"</string> - <string name="search_pref_screen_title_tablet" msgid="5220319680451343959">"Căutați pe tabletă"</string> + <string name="search_pref_screen_title" msgid="3258959643336315962">"Caută pe telefon"</string> + <string name="search_pref_screen_title_tablet" msgid="5220319680451343959">"Caută pe tabletă"</string> <string name="remote_action_failed" msgid="1383965239183576790">"Eșuare: <xliff:g id="WHAT">%1$s</xliff:g>"</string> </resources> diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml index 9733ac6c4c..598732155e 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Объект убран."</string> <string name="undo" msgid="4151576204245173321">"Отменить"</string> <string name="action_move" msgid="4339390619886385032">"Переместить элемент"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"Переместить в ячейку <xliff:g id="NUMBER_0">%1$s</xliff:g> <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Переместите в строку <xliff:g id="NUMBER_0">%1$s</xliff:g> столбца <xliff:g id="NUMBER_1">%2$s</xliff:g> на экране \"<xliff:g id="STRING">%3$s</xliff:g>\""</string> <string name="move_to_position" msgid="6750008980455459790">"Переместить в позицию <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"Переместить в Избранное (<xliff:g id="NUMBER">%1$s</xliff:g>)"</string> <string name="item_moved" msgid="4606538322571412879">"Элемент перемещен."</string> diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml index e160e81a6c..e030432ced 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"අයිතමය ඉවත් කරන ලදි"</string> <string name="undo" msgid="4151576204245173321">"අස් කරන්න"</string> <string name="action_move" msgid="4339390619886385032">"අයිතමය ගෙනයන්න"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"පේළිය <xliff:g id="NUMBER_0">%1$s</xliff:g> තීරුව <xliff:g id="NUMBER_1">%2$s</xliff:g> වෙත ගෙන යන්න"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g> තුළ <xliff:g id="NUMBER_0">%1$s</xliff:g> තීරුවේ<xliff:g id="NUMBER_1">%2$s</xliff:g> පේළියට යන්න"</string> <string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g> ස්ථානය වෙත ගෙන යන්න"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"ප්රියතම ස්ථානය <xliff:g id="NUMBER">%1$s</xliff:g> වෙත ගෙන යන්න"</string> <string name="item_moved" msgid="4606538322571412879">"අයිතමය ගෙන යන ලදි"</string> diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml index 8582baf21b..69fa362567 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Položka bola odstránená"</string> <string name="undo" msgid="4151576204245173321">"Späť"</string> <string name="action_move" msgid="4339390619886385032">"Presunúť položku"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"Presunúť do stĺpca <xliff:g id="NUMBER_1">%2$s</xliff:g> v riadku <xliff:g id="NUMBER_0">%1$s</xliff:g>"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Premiestnite položku do <xliff:g id="NUMBER_0">%1$s</xliff:g>. riadka <xliff:g id="NUMBER_1">%2$s</xliff:g>. stĺpca na obrazovke <xliff:g id="STRING">%3$s</xliff:g>"</string> <string name="move_to_position" msgid="6750008980455459790">"Presunúť na <xliff:g id="NUMBER">%1$s</xliff:g>. miesto"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"Presunúť na <xliff:g id="NUMBER">%1$s</xliff:g>. miesto v obľúbených položkách"</string> <string name="item_moved" msgid="4606538322571412879">"Položka bola presunutá"</string> diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml index 0762cf7238..b1f93e84d8 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Element je bil odstranjen."</string> <string name="undo" msgid="4151576204245173321">"Razveljavi"</string> <string name="action_move" msgid="4339390619886385032">"Premik elementa"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"Premik v <xliff:g id="NUMBER_0">%1$s</xliff:g>. vrstico <xliff:g id="NUMBER_1">%2$s</xliff:g>. stolpca"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Premik v vrstico <xliff:g id="NUMBER_0">%1$s</xliff:g> v stolpcu <xliff:g id="NUMBER_1">%2$s</xliff:g> na »<xliff:g id="STRING">%3$s</xliff:g>«"</string> <string name="move_to_position" msgid="6750008980455459790">"Premk na mesto št. <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"Premik na mesto priljubljenih št. <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="item_moved" msgid="4606538322571412879">"Element je premaknjen"</string> diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml index 821bd50c42..99bcbaa68d 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Artikulli u hoq"</string> <string name="undo" msgid="4151576204245173321">"Zhbëj"</string> <string name="action_move" msgid="4339390619886385032">"Zhvendose artikullin"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"Zhvendos te rreshti <xliff:g id="NUMBER_0">%1$s</xliff:g>, kolona <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Kalo te rreshti <xliff:g id="NUMBER_0">%1$s</xliff:g> kolona <xliff:g id="NUMBER_1">%2$s</xliff:g> në <xliff:g id="STRING">%3$s</xliff:g>"</string> <string name="move_to_position" msgid="6750008980455459790">"Zhvendos te pozicioni <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"Zhvendos te pozicioni <xliff:g id="NUMBER">%1$s</xliff:g> i preferencave"</string> <string name="item_moved" msgid="4606538322571412879">"Artikulli u zhvendos"</string> diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml index 85cc281c63..460a73e6e6 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Ставка је уклоњена"</string> <string name="undo" msgid="4151576204245173321">"Опозови"</string> <string name="action_move" msgid="4339390619886385032">"Премести ставку"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"Премести у ред <xliff:g id="NUMBER_0">%1$s</xliff:g> и колону <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Преместите у ред <xliff:g id="NUMBER_0">%1$s</xliff:g> колону <xliff:g id="NUMBER_1">%2$s</xliff:g> на <xliff:g id="STRING">%3$s</xliff:g>"</string> <string name="move_to_position" msgid="6750008980455459790">"Премести на <xliff:g id="NUMBER">%1$s</xliff:g>. позицију"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"Премести на <xliff:g id="NUMBER">%1$s</xliff:g>. позицију у омиљеним"</string> <string name="item_moved" msgid="4606538322571412879">"Ставка је премештена"</string> diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml index 7525eec085..6ec274b79c 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Objektet har tagits bort"</string> <string name="undo" msgid="4151576204245173321">"Ångra"</string> <string name="action_move" msgid="4339390619886385032">"Flytta objekt"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"Flytta till rad <xliff:g id="NUMBER_0">%1$s</xliff:g>, kolumn <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Flytta till rad <xliff:g id="NUMBER_0">%1$s</xliff:g> kolumn <xliff:g id="NUMBER_1">%2$s</xliff:g> i <xliff:g id="STRING">%3$s</xliff:g>"</string> <string name="move_to_position" msgid="6750008980455459790">"Flytta till plats <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"Flytta till favoritplats <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="item_moved" msgid="4606538322571412879">"Objektet har flyttats"</string> diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml index 3ae5dcfa61..147c6e13c6 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Kipengee kimeondolewa"</string> <string name="undo" msgid="4151576204245173321">"Tendua"</string> <string name="action_move" msgid="4339390619886385032">"Hamisha kipengee"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"Hamishia safu mlalo <xliff:g id="NUMBER_0">%1$s</xliff:g> safu wima <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Hamishia safu mlalo <xliff:g id="NUMBER_0">%1$s</xliff:g> safu wima <xliff:g id="NUMBER_1">%2$s</xliff:g> kwenye <xliff:g id="STRING">%3$s</xliff:g>"</string> <string name="move_to_position" msgid="6750008980455459790">"Hamishia nafasi ya <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"Hamishia nafasi inayopendwa ya <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="item_moved" msgid="4606538322571412879">"Kipengee kimesogezwa"</string> diff --git a/res/values-sw600dp/config.xml b/res/values-sw600dp/config.xml index 072b92d5f2..e718d9cea5 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 7b2ed8bc65..09b2d6f83f 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 fa2b789676..d8eacf5551 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> @@ -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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"அகற்றப்பட்டது"</string> <string name="undo" msgid="4151576204245173321">"செயல்தவிர்"</string> <string name="action_move" msgid="4339390619886385032">"நகர்த்து"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"<xliff:g id="NUMBER_0">%1$s</xliff:g> வரிசை, <xliff:g id="NUMBER_1">%2$s</xliff:g> நெடுவரிசைக்கு நகர்த்து"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g> என்பதில் <xliff:g id="NUMBER_0">%1$s</xliff:g>வது வரிசை <xliff:g id="NUMBER_1">%2$s</xliff:g>வது நெடுவரிசைக்கு நகர்த்தப்படும்"</string> <string name="move_to_position" msgid="6750008980455459790">"நிலை <xliff:g id="NUMBER">%1$s</xliff:g>க்கு நகர்த்து"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"விரும்பும் நிலை <xliff:g id="NUMBER">%1$s</xliff:g>க்கு நகர்த்து"</string> <string name="item_moved" msgid="4606538322571412879">"உருப்படி நகர்த்தப்பட்டது"</string> diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml index 663e44faf4..7fddcf4da0 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> @@ -71,11 +70,11 @@ <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="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="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> @@ -118,7 +117,7 @@ <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> + <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> @@ -137,25 +136,25 @@ <string name="item_removed" msgid="851119963877842327">"ఐటెమ్ తీసివేయబడింది"</string> <string name="undo" msgid="4151576204245173321">"చర్య రద్దు"</string> <string name="action_move" msgid="4339390619886385032">"అంశాన్ని తరలించు"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"అడ్డు వరుస <xliff:g id="NUMBER_0">%1$s</xliff:g> నిలువు వరుస <xliff:g id="NUMBER_1">%2$s</xliff:g>కి తరలించు"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g> లో <xliff:g id="NUMBER_0">%1$s</xliff:g> అడ్డు వరుస <xliff:g id="NUMBER_1">%2$s</xliff:g>నిలువు వరుసకు తరలించండి"</string> <string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g>వ స్థానానికి తరలించు"</string> <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> + <string name="action_dismiss_notification" msgid="5909461085055959187">"తీసివేయండి"</string> <string name="accessibility_close" msgid="2277148124685870734">"మూసివేస్తుంది"</string> <string name="notification_dismissed" msgid="6002233469409822874">"నోటిఫికేషన్ తీసివేయబడింది"</string> <string name="all_apps_personal_tab" msgid="4190252696685155002">"వ్యక్తిగతం"</string> diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml index 07e5796734..ffeb20c0d1 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"นำรายการออกแล้ว"</string> <string name="undo" msgid="4151576204245173321">"เลิกทำ"</string> <string name="action_move" msgid="4339390619886385032">"ย้ายรายการ"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"ย้ายไปที่แถว <xliff:g id="NUMBER_0">%1$s</xliff:g> คอลัมน์ <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"ย้ายไปยังแถว <xliff:g id="NUMBER_0">%1$s</xliff:g> คอลัมน์ <xliff:g id="NUMBER_1">%2$s</xliff:g> ใน <xliff:g id="STRING">%3$s</xliff:g>"</string> <string name="move_to_position" msgid="6750008980455459790">"ย้ายไปยังตำแหน่ง <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"ย้ายไปยังตำแหน่งที่ <xliff:g id="NUMBER">%1$s</xliff:g> ของรายการโปรด"</string> <string name="item_moved" msgid="4606538322571412879">"ย้ายรายการแล้ว"</string> diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml index f7158b4fa6..a4cd808e4e 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Naalis na ang item"</string> <string name="undo" msgid="4151576204245173321">"I-undo"</string> <string name="action_move" msgid="4339390619886385032">"Ilipat ang item"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"Ilipat sa row <xliff:g id="NUMBER_0">%1$s</xliff:g> column <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Pumunta sa row <xliff:g id="NUMBER_0">%1$s</xliff:g> column <xliff:g id="NUMBER_1">%2$s</xliff:g> sa <xliff:g id="STRING">%3$s</xliff:g>"</string> <string name="move_to_position" msgid="6750008980455459790">"Ilipat sa posisyon <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"Ilipat sa posisyon <xliff:g id="NUMBER">%1$s</xliff:g> sa mga paborito"</string> <string name="item_moved" msgid="4606538322571412879">"Nalipat ang item"</string> diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml index b12b9b14ee..c30c2e5f46 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Öğe silindi"</string> <string name="undo" msgid="4151576204245173321">"Geri al"</string> <string name="action_move" msgid="4339390619886385032">"Öğeyi taşı"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"<xliff:g id="NUMBER_0">%1$s</xliff:g>. satır <xliff:g id="NUMBER_1">%2$s</xliff:g>. sütuna taşı"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g> sayfasında <xliff:g id="NUMBER_0">%1$s</xliff:g>. satır <xliff:g id="NUMBER_1">%2$s</xliff:g>. sütuna taşı"</string> <string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g>. sıraya taşı"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"Favorilerde <xliff:g id="NUMBER">%1$s</xliff:g>. sıraya taşı"</string> <string name="item_moved" msgid="4606538322571412879">"Öğe taşındı"</string> diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml index 2b43a4db68..2f79f23a4f 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Елемент вилучено"</string> <string name="undo" msgid="4151576204245173321">"Відмінити"</string> <string name="action_move" msgid="4339390619886385032">"Перемістити елемент"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"Перемістити в рядок <xliff:g id="NUMBER_0">%1$s</xliff:g>, стовпець <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Перенести: <xliff:g id="STRING">%3$s</xliff:g>, рядок <xliff:g id="NUMBER_0">%1$s</xliff:g>, стовпець <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string> <string name="move_to_position" msgid="6750008980455459790">"Перемістити на <xliff:g id="NUMBER">%1$s</xliff:g> місце"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"Перемістити у вибране на <xliff:g id="NUMBER">%1$s</xliff:g> місце"</string> <string name="item_moved" msgid="4606538322571412879">"Елемент переміщено"</string> diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml index 70a853fcfc..3d90187e6c 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"آئٹم ہٹا دیا گیا"</string> <string name="undo" msgid="4151576204245173321">"کالعدم کریں"</string> <string name="action_move" msgid="4339390619886385032">"آئٹم منتقل کریں"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"قطار <xliff:g id="NUMBER_0">%1$s</xliff:g> کالم <xliff:g id="NUMBER_1">%2$s</xliff:g> میں منتقل کریں"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g> میں قطار <xliff:g id="NUMBER_0">%1$s</xliff:g> کالم <xliff:g id="NUMBER_1">%2$s</xliff:g> میں منتقل کریں"</string> <string name="move_to_position" msgid="6750008980455459790">"پوزیشن <xliff:g id="NUMBER">%1$s</xliff:g> میں منتقل کریں"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"پسندیدہ پوزیشن <xliff:g id="NUMBER">%1$s</xliff:g> میں منتقل کریں"</string> <string name="item_moved" msgid="4606538322571412879">"آئٹم منتقل کر دیا گیا"</string> diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml index 57a446bb31..04d07ec4e5 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Element olib tashlandi"</string> <string name="undo" msgid="4151576204245173321">"Qaytarish"</string> <string name="action_move" msgid="4339390619886385032">"Obyektni ko‘chirib o‘tkazish"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"<xliff:g id="NUMBER_0">%1$s</xliff:g> <xliff:g id="NUMBER_1">%2$s</xliff:g> katakka olish"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g> ichidagi <xliff:g id="NUMBER_0">%1$s</xliff:g>-qator <xliff:g id="NUMBER_1">%2$s</xliff:g>-ustuniga oʻting"</string> <string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g>-joyga olish"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"Sevimlilarga olish (<xliff:g id="NUMBER">%1$s</xliff:g>)"</string> <string name="item_moved" msgid="4606538322571412879">"Element ko‘chirib o‘tkazildi"</string> diff --git a/res/values-v31/colors.xml b/res/values-v31/colors.xml index 85a66783f2..44073864db 100644 --- a/res/values-v31/colors.xml +++ b/res/values-v31/colors.xml @@ -56,4 +56,11 @@ <color name="workspace_accent_color_light">@android:color/system_accent1_100</color> <color name="workspace_accent_color_dark">@android:color/system_accent2_600</color> + + <color name="preload_icon_accent_color_light">@android:color/system_accent1_600</color> + <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 2c81ae7f2c..2329992335 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Đã xóa mục"</string> <string name="undo" msgid="4151576204245173321">"Hủy"</string> <string name="action_move" msgid="4339390619886385032">"Di chuyển mục"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"Di chuyển đến hàng <xliff:g id="NUMBER_0">%1$s</xliff:g> cột <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Di chuyển đến hàng <xliff:g id="NUMBER_0">%1$s</xliff:g> cột <xliff:g id="NUMBER_1">%2$s</xliff:g> tại <xliff:g id="STRING">%3$s</xliff:g>"</string> <string name="move_to_position" msgid="6750008980455459790">"Di chuyển tới vị trí <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"Di chuyển tới vị trí mục yêu thích <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="item_moved" msgid="4606538322571412879">"Đã di chuyển mục"</string> diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml index 7c104e2717..929cffa3f4 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"项目已移除"</string> <string name="undo" msgid="4151576204245173321">"撤消"</string> <string name="action_move" msgid="4339390619886385032">"移动项目"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"移至第 <xliff:g id="NUMBER_0">%1$s</xliff:g> 行第 <xliff:g id="NUMBER_1">%2$s</xliff:g> 列"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"移至<xliff:g id="STRING">%3$s</xliff:g>中的第 <xliff:g id="NUMBER_0">%1$s</xliff:g> 行第 <xliff:g id="NUMBER_1">%2$s</xliff:g> 列"</string> <string name="move_to_position" msgid="6750008980455459790">"移至第 <xliff:g id="NUMBER">%1$s</xliff:g> 个位置"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"移至收藏夹第 <xliff:g id="NUMBER">%1$s</xliff:g> 个位置"</string> <string name="item_moved" msgid="4606538322571412879">"已移动项目"</string> diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml index ed288d631d..1aea757286 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"項目已移除"</string> <string name="undo" msgid="4151576204245173321">"復原"</string> <string name="action_move" msgid="4339390619886385032">"移動項目"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"移動至第 <xliff:g id="NUMBER_0">%1$s</xliff:g> 行第 <xliff:g id="NUMBER_1">%2$s</xliff:g> 列"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"移去 <xliff:g id="STRING">%3$s</xliff:g> 嘅第 <xliff:g id="NUMBER_0">%1$s</xliff:g> 列,第 <xliff:g id="NUMBER_1">%2$s</xliff:g> 欄"</string> <string name="move_to_position" msgid="6750008980455459790">"移動至位置 <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"移動至喜愛的位置 <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="item_moved" msgid="4606538322571412879">"已移動項目"</string> diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml index 3690c3a729..25a7d21e40 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"已移除項目"</string> <string name="undo" msgid="4151576204245173321">"復原"</string> <string name="action_move" msgid="4339390619886385032">"移動項目"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"移至第 <xliff:g id="NUMBER_0">%1$s</xliff:g> 列第 <xliff:g id="NUMBER_1">%2$s</xliff:g> 欄"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"移動至 <xliff:g id="STRING">%3$s</xliff:g> 的第 <xliff:g id="NUMBER_0">%1$s</xliff:g> 列,第 <xliff:g id="NUMBER_1">%2$s</xliff:g> 欄"</string> <string name="move_to_position" msgid="6750008980455459790">"移至位置 <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"移至收藏位置 <xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="item_moved" msgid="4606538322571412879">"已移動項目"</string> diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml index 26e7dcf103..151ce27806 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> @@ -137,7 +136,7 @@ <string name="item_removed" msgid="851119963877842327">"Into isusiwe"</string> <string name="undo" msgid="4151576204245173321">"Susa"</string> <string name="action_move" msgid="4339390619886385032">"Hambisa into"</string> - <string name="move_to_empty_cell" msgid="2833711483015685619">"Hambisa kurowu engu-<xliff:g id="NUMBER_0">%1$s</xliff:g> ikholomu engu-<xliff:g id="NUMBER_1">%2$s</xliff:g>"</string> + <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Hamba emugqeni <xliff:g id="NUMBER_0">%1$s</xliff:g> ikholomu <xliff:g id="NUMBER_1">%2$s</xliff:g> ku-<xliff:g id="STRING">%3$s</xliff:g>"</string> <string name="move_to_position" msgid="6750008980455459790">"Hambisa kusimo esingu-<xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="move_to_hotseat_position" msgid="6295412897075147808">"Hambisa kusimo sezintandokazi esingu-<xliff:g id="NUMBER">%1$s</xliff:g>"</string> <string name="item_moved" msgid="4606538322571412879">"Into ihanjisiwe"</string> diff --git a/res/values/attrs.xml b/res/values/attrs.xml index 13f20c22c4..08561d5d51 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -53,12 +53,8 @@ <attr name="workProfileOverlayTextColor" format="color" /> <attr name="workspaceAccentColor" format="color" /> <attr name="dropTargetHoverTextColor" 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" /> + <attr name="preloadIconAccentColor" format="color" /> + <attr name="preloadIconBackgroundColor" format="color" /> <!-- BubbleTextView specific attributes. --> <declare-styleable name="BubbleTextView"> @@ -134,6 +130,10 @@ <attr name="layout_ignoreInsets" format="boolean" /> </declare-styleable> + <declare-styleable name="StickyScroller_Layout"> + <attr name="layout_sticky" format="boolean" /> + </declare-styleable> + <declare-styleable name="GridDisplayOption"> <attr name="name" format="string" /> @@ -141,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, @@ -152,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" /> @@ -170,14 +170,17 @@ <!-- defaults to numColumns, if not specified --> <attr name="hotseatColumnSpanTwoPanelPortrait" format="integer" /> + <!-- Spacing to have at the end of the nav buttons in large screen 3 button nav, + defaults to @dimen/taskbar_button_margin_default --> + <attr name="inlineNavButtonsEndSpacing" format="reference" /> + <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 --> @@ -186,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"> @@ -198,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" /> @@ -252,9 +271,10 @@ if not specified --> <attr name="borderSpaceTwoPanelLandscapeVertical" format="float" /> - <!-- These min cell values are only used if GridDisplayOption#isScalable is true --> - <!-- defaults to minCellHeight, if not specified --> + <!-- defaults to minCellHeight if not specified when GridDisplayOption#isScalable is true. + Must be defined when GridDisplayOption#isScalable is false. --> <attr name="allAppsCellHeight" format="float" /> + <!-- These min cell values are only used if GridDisplayOption#isScalable is true --> <!-- defaults to minCellWidth, if not specified --> <attr name="allAppsCellWidth" format="float" /> <!-- defaults to allAppsCellHeight, if not specified --> @@ -273,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" /> @@ -317,14 +339,23 @@ 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 --> + <attr name="hotseatBarBottomSpaceLandscape" format="float" /> + <!-- defaults to hotseatBarBottomSpace, if not specified --> + <attr name="hotseatBarBottomSpaceTwoPanelLandscape" format="float" /> + <!-- defaults to hotseatBarBottomSpace, if not specified --> + <attr name="hotseatBarBottomSpaceTwoPanelPortrait" format="float" /> + + <!-- defaults to res.hotseat_qsb_space_default, if not specified --> + <attr name="hotseatQsbSpace" format="float" /> + <!-- defaults to hotseatQsbSpace, if not specified --> + <attr name="hotseatQsbSpaceLandscape" format="float" /> + <!-- defaults to hotseatQsbSpace, if not specified --> + <attr name="hotseatQsbSpaceTwoPanelLandscape" format="float" /> + <!-- defaults to hotseatQsbSpace, if not specified --> + <attr name="hotseatQsbSpaceTwoPanelPortrait" format="float" /> <attr name="iconImageSize" format="float" /> <!-- defaults to iconImageSize, if not specified --> @@ -354,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 2bc923952d..9d6927b99a 100644 --- a/res/values/colors.xml +++ b/res/values/colors.xml @@ -80,9 +80,10 @@ <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> + <color name="preload_icon_accent_color_dark">#4BB6E8</color> + <color name="preload_icon_background_color_dark">#40484D</color> </resources> diff --git a/res/values/config.xml b/res/values/config.xml index 9aa1f03734..016420bf79 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. --> @@ -85,6 +82,8 @@ <string name="launcher_activity_logic_class" translatable="false"></string> <string name="model_delegate_class" translatable="false"></string> <string name="window_manager_proxy_class" translatable="false"></string> + <string name="secondary_display_predictions_class" translatable="false"></string> + <string name="widget_holder_factory_class" translatable="false"></string> <!-- View ID to use for QSB widget --> <item type="id" name="qsb_widget" /> @@ -130,6 +129,11 @@ <item type="id" name="search_container_all_apps" /> <item type="id" name="search_container_hotseat" /> + <!-- Scalable Grid configuration --> + <!-- This is a float because it is converted to dp later in DeviceProfile --> + <dimen name="hotseat_bar_bottom_space_default">48</dimen> + <dimen name="hotseat_qsb_space_default">0</dimen> + <!-- Recents --> <item type="id" name="overview_panel"/> @@ -151,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> @@ -186,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 8403af46b7..4d2e1b7c6d 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -22,8 +22,6 @@ <dimen name="dynamic_grid_edge_margin">10.77dp</dimen> <dimen name="dynamic_grid_left_right_margin">8dp</dimen> <dimen name="dynamic_grid_icon_drawable_padding">7dp</dimen> - <!-- Minimum space between workspace and hotseat in spring loaded mode --> - <dimen name="dynamic_grid_min_spring_loaded_space">8dp</dimen> <!-- Minimum amount of next page visible in spring loaded mode --> <dimen name="dynamic_grid_spring_loaded_min_next_space_visible">24dp</dimen> @@ -32,10 +30,7 @@ <dimen name="dynamic_grid_cell_padding_x">8dp</dimen> <!-- Hotseat --> - <dimen name="dynamic_grid_hotseat_top_padding">8dp</dimen> - <dimen name="dynamic_grid_hotseat_bottom_padding">2dp</dimen> <dimen name="dynamic_grid_hotseat_bottom_tall_padding">0dp</dimen> - <dimen name="inline_qsb_bottom_margin">0dp</dimen> <dimen name="spring_loaded_hotseat_top_margin">76dp</dimen> <!-- Qsb --> @@ -44,13 +39,10 @@ it is close to the bottom of the screen --> <item name="qsb_center_factor" format="float" type="dimen">0.325</item> - <!-- Extra bottom padding for non-tall devices. --> - <dimen name="dynamic_grid_hotseat_bottom_non_tall_padding">0dp</dimen> - <dimen name="dynamic_grid_hotseat_extra_vertical_size">34dp</dimen> <dimen name="dynamic_grid_hotseat_side_padding">0dp</dimen> <!-- Scalable Grid --> - <dimen name="scalable_grid_qsb_bottom_margin">42dp</dimen> + <dimen name="min_qsb_margin">8dp</dimen> <!-- Workspace page indicator --> <dimen name="workspace_page_indicator_height">24dp</dimen> @@ -116,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> @@ -132,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> @@ -139,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> @@ -157,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> @@ -237,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> @@ -257,12 +252,14 @@ <!-- Folders --> <dimen name="page_indicator_dot_size">8dp</dimen> + <dimen name="page_indicator_size">10dp</dimen> + <dimen name="folder_cell_x_padding">9dp</dimen> <dimen name="folder_cell_y_padding">6dp</dimen> <!-- label text size = workspace text size multiplied by this scale --> <dimen name="folder_label_text_scale">1.14</dimen> - <dimen name="folder_label_height">48dp</dimen> + <dimen name="folder_footer_height_default">56dp</dimen> <dimen name="folder_content_padding_left_right">8dp</dimen> <dimen name="folder_content_padding_top">16dp</dimen> @@ -340,7 +337,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> @@ -364,9 +361,36 @@ <dimen name="taskbar_size">0dp</dimen> <dimen name="taskbar_stashed_size">0dp</dimen> <dimen name="qsb_widget_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="qsb_shadow_height">0dp</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_space_inbetween_phone">0dp</dimen> + <dimen name="taskbar_button_margin_split">0dp</dimen> + <dimen name="taskbar_button_margin_6_5">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> @@ -379,7 +403,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> @@ -391,8 +414,17 @@ <dimen name="split_placeholder_inset">16dp</dimen> <dimen name="split_placeholder_icon_size">44dp</dimen> <dimen name="task_menu_width_grid">216dp</dimen> - - + <dimen name="split_instructions_radius">22dp</dimen> + <dimen name="split_instructions_elevation">1dp</dimen> + <dimen name="split_instructions_horizontal_padding">24dp</dimen> + <dimen name="split_instructions_vertical_padding">12dp</dimen> + <dimen name="split_instructions_bottom_margin_tablet_landscape">32dp</dimen> + <dimen name="split_instructions_bottom_margin_tablet_portrait">44dp</dimen> + <dimen name="split_instructions_bottom_margin_twopanels_landscape">33dp</dimen> + <dimen name="split_instructions_bottom_margin_twopanels_portrait">51dp</dimen> + <dimen name="split_instructions_bottom_margin_phone_landscape">24dp</dimen> + <dimen name="split_instructions_bottom_margin_phone_portrait">60dp</dimen> + <!-- Workspace grid visualization parameters --> <dimen name="grid_visualization_rounding_radius">28dp</dimen> <dimen name="grid_visualization_horizontal_cell_spacing">6dp</dimen> @@ -405,8 +437,16 @@ <!-- Bottom sheet related parameters --> <dimen name="bottom_sheet_extra_top_padding">0dp</dimen> + <dimen name="bottom_sheet_handle_area_height">36dp</dimen> <dimen name="bottom_sheet_handle_width">32dp</dimen> <dimen name="bottom_sheet_handle_height">4dp</dimen> <dimen name="bottom_sheet_handle_margin">16dp</dimen> <dimen name="bottom_sheet_handle_corner_radius">2dp</dimen> + + <!-- 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 af21b27caf..9fc0ff8277 100644 --- a/res/values/id.xml +++ b/res/values/id.xml @@ -16,7 +16,6 @@ --> <resources> <item type="id" name="apps_list_view_work" /> - <item type="id" name="tag_widget_entry" /> <item type="id" name="view_type_widgets_space" /> <item type="id" name="view_type_widgets_list" /> <item type="id" name="view_type_widgets_header" /> diff --git a/res/values/strings.xml b/res/values/strings.xml index 847e4a8625..3eb08bada1 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] --> @@ -344,7 +342,7 @@ <string name="action_move">Move item</string> <!-- Accessibility description to move item to empty cell. --> - <string name="move_to_empty_cell">Move to row <xliff:g id="number" example="1">%1$s</xliff:g> column <xliff:g id="number" example="1">%2$s</xliff:g></string> + <string name="move_to_empty_cell_description">Move to row <xliff:g id="number" example="1">%1$s</xliff:g> column <xliff:g id="number" example="1">%2$s</xliff:g> in <xliff:g id="string" example="Home screen 2 of 4">%3$s</xliff:g></string> <!-- Accessibility description to move item inside a folder. --> <string name="move_to_position">Move to position <xliff:g id="number" example="1">%1$s</xliff:g></string> diff --git a/res/values/styles.xml b/res/values/styles.xml index 93446348fb..ea06caa863 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,24 +61,20 @@ <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> + <item name="preloadIconAccentColor">@color/preload_icon_accent_color_light</item> + <item name="preloadIconBackgroundColor">@color/preload_icon_background_color_light</item> <item name="android:windowTranslucentStatus">false</item> <item name="android:windowTranslucentNavigation">false</item> <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> @@ -101,7 +97,6 @@ <item name="allAppsScrimColor">?android:attr/colorBackgroundFloating</item> <item name="allappsHeaderProtectionColor">@color/popup_color_tertiary_dark</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> @@ -124,11 +119,11 @@ <item name="workProfileOverlayTextColor">@android:color/white</item> <item name="eduHalfSheetBGColor">#DD000000</item> <item name="overviewScrimColor">@color/overview_scrim_dark</item> + <item name="preloadIconAccentColor">@color/preload_icon_accent_color_dark</item> + <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> @@ -204,22 +199,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"> <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item> </style> @@ -273,7 +252,7 @@ </style> <!-- Drop targets --> - <style name="DropTargetButtonBase" parent="@android:style/TextAppearance.DeviceDefault"> + <style name="DropTargetButtonBase" parent="@android:style/TextAppearance.DeviceDefault.Medium"> <item name="android:drawablePadding">@dimen/drop_target_button_drawable_padding</item> <item name="android:padding">14dp</item> <item name="android:textColor">@color/drop_target_text</item> @@ -291,17 +270,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> @@ -327,4 +295,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 0000000000..c560104b9e --- /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 d203ba802f..14503e2832 100644 --- a/res/xml/device_profiles.xml +++ b/res/xml/device_profiles.xml @@ -24,7 +24,9 @@ launcher:numFolderRows="3" launcher:numFolderColumns="3" launcher:numHotseatIcons="4" + launcher:numExtendedHotseatIcons="6" launcher:dbFile="launcher_4_by_5.db" + launcher:inlineNavButtonsEndSpacing="@dimen/taskbar_button_margin_split" launcher:defaultLayoutId="@xml/default_workspace_4x5" launcher:deviceCategory="phone|multi_display" > @@ -87,7 +89,9 @@ launcher:numFolderRows="4" launcher:numFolderColumns="4" launcher:numHotseatIcons="5" + launcher:numExtendedHotseatIcons="6" launcher:dbFile="launcher.db" + launcher:inlineNavButtonsEndSpacing="@dimen/taskbar_button_margin_split" launcher:defaultLayoutId="@xml/default_workspace_5x5" launcher:deviceCategory="phone|multi_display" > @@ -157,6 +161,7 @@ launcher:hotseatColumnSpanLandscape="4" launcher:numAllAppsColumns="6" launcher:isScalable="true" + launcher:inlineNavButtonsEndSpacing="@dimen/taskbar_button_margin_6_5" launcher:devicePaddingId="@xml/paddings_6x5" launcher:dbFile="launcher_6_by_5.db" launcher:defaultLayoutId="@xml/default_workspace_6x5" @@ -187,8 +192,8 @@ 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" /> </grid-option> diff --git a/res/xml/paddings_6x5.xml b/res/xml/paddings_6x5.xml index a72f55412c..2f421b78e3 100644 --- a/res/xml/paddings_6x5.xml +++ b/res/xml/paddings_6x5.xml @@ -17,60 +17,29 @@ <device-paddings xmlns:launcher="http://schemas.android.com/apk/res-auto" > - <!-- Some non default screen sizes --> <device-padding - launcher:maxEmptySpace="30dp"> + launcher:maxEmptySpace="100dp"> <workspaceTopPadding - launcher:a="0.34" + launcher:a="0.31" launcher:b="0"/> <workspaceBottomPadding - launcher:a="0.26" + launcher:a="0.69" launcher:b="0"/> <hotseatBottomPadding - launcher:a="0.4" - launcher:b="0"/> - </device-padding> - - <device-padding - launcher:maxEmptySpace="170dp"> - <workspaceTopPadding - launcher:a="0" - launcher:b="20dp"/> - <workspaceBottomPadding - launcher:a="0.4" - launcher:b="0" - launcher:c="20dp"/> - <hotseatBottomPadding - launcher:a="0.6" - launcher:b="0" - launcher:c="20dp"/> - </device-padding> - - <device-padding - launcher:maxEmptySpace="410dp"> - <workspaceTopPadding launcher:a="0" - launcher:b="112dp"/> - <workspaceBottomPadding - launcher:a="0.4" - launcher:b="0" - launcher:c="112dp"/> - <hotseatBottomPadding - launcher:a="0.6" - launcher:b="0" - launcher:c="112dp"/> + launcher:b="0"/> </device-padding> <device-padding launcher:maxEmptySpace="9999dp"> <workspaceTopPadding - launcher:a="0.40" - launcher:c="36dp"/> + launcher:a="0.48" + launcher:b="0"/> <workspaceBottomPadding - launcher:a="0.60" - launcher:c="36dp"/> + launcher:a="0.52" + launcher:b="0"/> <hotseatBottomPadding launcher:a="0" - launcher:b="36dp"/> + launcher:b="0"/> </device-padding> </device-paddings>
\ No newline at end of file diff --git a/res/xml/paddings_handhelds.xml b/res/xml/paddings_handhelds.xml new file mode 100644 index 0000000000..b9549a6133 --- /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 21dbc5f7e0..28d4a9f7a8 100644 --- a/src/com/android/launcher3/AbstractFloatingView.java +++ b/src/com/android/launcher3/AbstractFloatingView.java @@ -94,6 +94,7 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch public static final int TYPE_TASKBAR_EDUCATION_DIALOG = 1 << 16; public static final int TYPE_TASKBAR_ALL_APPS = 1 << 17; public static final int TYPE_ADD_TO_HOME_CONFIRMATION = 1 << 18; + public static final int TYPE_TASKBAR_OVERLAY_PROXY = 1 << 19; public static final int TYPE_ALL = TYPE_FOLDER | TYPE_ACTION_POPUP | TYPE_WIDGETS_BOTTOM_SHEET | TYPE_WIDGET_RESIZE_FRAME | TYPE_WIDGETS_FULL_SHEET @@ -101,17 +102,15 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch | 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/Alarm.java b/src/com/android/launcher3/Alarm.java index d5b434c647..e4aebf606d 100644 --- a/src/com/android/launcher3/Alarm.java +++ b/src/com/android/launcher3/Alarm.java @@ -30,6 +30,7 @@ public class Alarm implements Runnable{ private Handler mHandler; private OnAlarmListener mAlarmListener; private boolean mAlarmPending = false; + private long mLastSetTimeout; public Alarm() { mHandler = new Handler(); @@ -46,6 +47,7 @@ public class Alarm implements Runnable{ mAlarmPending = true; long oldTriggerTime = mAlarmTriggerTime; mAlarmTriggerTime = currentTime + millisecondsInFuture; + mLastSetTimeout = millisecondsInFuture; // If the previous alarm was set for a longer duration, cancel it. if (mWaitingForCallback && oldTriggerTime > mAlarmTriggerTime) { @@ -84,4 +86,9 @@ public class Alarm implements Runnable{ public boolean alarmPending() { return mAlarmPending; } + + /** Returns the last value passed to {@link #setAlarm(long)} */ + public long getLastSetTimeout() { + return mLastSetTimeout; + } } diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java index a8823b9403..0149dcb0f7 100644 --- a/src/com/android/launcher3/AppWidgetResizeFrame.java +++ b/src/com/android/launcher3/AppWidgetResizeFrame.java @@ -23,6 +23,7 @@ import android.util.AttributeSet; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; +import android.view.ViewGroup; import android.widget.ImageButton; import android.widget.ImageView; @@ -30,6 +31,7 @@ import androidx.annotation.Nullable; 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; @@ -67,22 +69,6 @@ public class AppWidgetResizeFrame extends AbstractFloatingView implements View.O private final View[] mDragHandles = new View[HANDLE_COUNT]; private final List<Rect> mSystemGestureExclusionRects = new ArrayList<>(HANDLE_COUNT); - private final OnAttachStateChangeListener mWidgetViewAttachStateChangeListener = - new OnAttachStateChangeListener() { - @Override - public void onViewAttachedToWindow(View view) { - // Do nothing - } - - @Override - public void onViewDetachedFromWindow(View view) { - // When the app widget view is detached, we should close the resize frame. - // An example is when the dragging starts, the widget view is detached from - // CellLayout and then reattached to DragLayout. - close(false); - } - }; - private LauncherAppWidgetHostView mWidgetView; private CellLayout mCellLayout; @@ -220,11 +206,7 @@ public class AppWidgetResizeFrame extends AbstractFloatingView implements View.O private void setupForWidget(LauncherAppWidgetHostView widgetView, CellLayout cellLayout, DragLayer dragLayer) { mCellLayout = cellLayout; - if (mWidgetView != null) { - mWidgetView.removeOnAttachStateChangeListener(mWidgetViewAttachStateChangeListener); - } mWidgetView = widgetView; - mWidgetView.addOnAttachStateChangeListener(mWidgetViewAttachStateChangeListener); LauncherAppWidgetProviderInfo info = (LauncherAppWidgetProviderInfo) widgetView.getAppWidgetInfo(); mDragLayer = dragLayer; @@ -249,11 +231,11 @@ public class AppWidgetResizeFrame extends AbstractFloatingView implements View.O /* 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(() -> { @@ -266,7 +248,7 @@ public class AppWidgetResizeFrame extends AbstractFloatingView implements View.O } } - 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; @@ -403,6 +385,10 @@ public class AppWidgetResizeFrame extends AbstractFloatingView implements View.O * Based on the current deltas, we determine if and how to resize the widget. */ private void resizeWidgetIfNeeded(boolean onDismiss) { + ViewGroup.LayoutParams wlp = mWidgetView.getLayoutParams(); + if (!(wlp instanceof CellLayoutLayoutParams)) { + return; + } DeviceProfile dp = mLauncher.getDeviceProfile(); float xThreshold = mCellLayout.getCellWidth() + dp.cellLayoutBorderSpacePx.x; float yThreshold = mCellLayout.getCellHeight() + dp.cellLayoutBorderSpacePx.y; @@ -415,7 +401,7 @@ public class AppWidgetResizeFrame extends AbstractFloatingView implements View.O mDirectionVector[0] = 0; mDirectionVector[1] = 0; - CellLayout.LayoutParams lp = (CellLayout.LayoutParams) mWidgetView.getLayoutParams(); + CellLayoutLayoutParams lp = (CellLayoutLayoutParams) wlp; int spanX = lp.cellHSpan; int spanY = lp.cellVSpan; @@ -667,9 +653,6 @@ public class AppWidgetResizeFrame extends AbstractFloatingView implements View.O @Override protected void handleClose(boolean animate) { mDragLayer.removeView(this); - if (mWidgetView != null) { - mWidgetView.removeOnAttachStateChangeListener(mWidgetViewAttachStateChangeListener); - } } private void updateInvalidResizeEffect(CellLayout cellLayout, CellLayout pairedCellLayout, diff --git a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java index 75e89b2944..eb6d096602 100644 --- a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java +++ b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java @@ -2,7 +2,6 @@ package com.android.launcher3; 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.content.Intent; 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.model.data.LauncherAppWidgetInfo; 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 @@ public class AppWidgetsRestoredReceiver extends BroadcastReceiver { 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 @@ public class AppWidgetsRestoredReceiver extends BroadcastReceiver { * 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 @@ public class AppWidgetsRestoredReceiver extends BroadcastReceiver { 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 @@ public class AppWidgetsRestoredReceiver extends BroadcastReceiver { 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 64666b0041..55ede6cecb 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.text.TextUtils; 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.LauncherAppWidgetInfo; 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 @@ public class AutoInstallsLayout { 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 @@ public class AutoInstallsLayout { + " 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 @@ public class AutoInstallsLayout { @Thunk final Context mContext; @Thunk - final AppWidgetHost mAppWidgetHost; + final LauncherWidgetHolder mAppWidgetHolder; protected final LayoutParserCallback mCallback; protected final PackageManager mPackageManager; @@ -174,17 +170,17 @@ public class AutoInstallsLayout { 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 73d3e3301c..61707dff49 100644 --- a/src/com/android/launcher3/BaseActivity.java +++ b/src/com/android/launcher3/BaseActivity.java @@ -25,15 +25,18 @@ import android.content.Context; 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 @@ import java.util.List; /** * 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"; @@ -77,8 +79,8 @@ public abstract class BaseActivity extends Activity implements AppLauncher, new ArrayList<>(); protected DeviceProfile mDeviceProfile; - protected StatsLogManager mStatsLogManager; protected SystemUiController mSystemUiController; + private StatsLogManager mStatsLogManager; public static final int ACTIVITY_STATE_STARTED = 1 << 0; @@ -172,6 +174,19 @@ public abstract class BaseActivity extends Activity implements AppLauncher, } @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (Utilities.ATLEAST_T) { + getOnBackInvokedDispatcher().registerOnBackInvokedCallback( + OnBackInvokedDispatcher.PRIORITY_DEFAULT, + () -> { + onBackPressed(); + TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onBackInvoked"); + }); + } + } + + @Override protected void onStart() { addActivityFlags(ACTIVITY_STATE_STARTED); super.onStart(); @@ -179,8 +194,7 @@ public abstract class BaseActivity extends Activity implements AppLauncher, @Override protected void onResume() { - addActivityFlags(ACTIVITY_STATE_RESUMED | ACTIVITY_STATE_USER_ACTIVE); - removeActivityFlags(ACTIVITY_STATE_USER_WILL_BE_ACTIVE); + setResumed(); super.onResume(); } @@ -211,7 +225,7 @@ public abstract class BaseActivity extends Activity implements AppLauncher, @Override protected void onPause() { - removeActivityFlags(ACTIVITY_STATE_RESUMED | ACTIVITY_STATE_DEFERRED_RESUMED); + setPaused(); super.onPause(); // Reset the overridden sysui flags used for the task-swipe launch animation, we do this @@ -243,6 +257,21 @@ public abstract class BaseActivity extends Activity implements AppLauncher, 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 6de3884b93..ca1fe40ed9 100644 --- a/src/com/android/launcher3/BaseDraggingActivity.java +++ b/src/com/android/launcher3/BaseDraggingActivity.java @@ -34,9 +34,6 @@ import android.view.View; 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 @@ public abstract class BaseDraggingActivity extends BaseActivity 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 5fb892554a..9f54f09bb6 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -607,15 +607,16 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, * Get the icon bounds on the view depending on the layout type. */ public void getIconBounds(int iconSize, Rect outBounds) { - Utilities.setRectToViewCenter(this, iconSize, outBounds); + outBounds.set(0, 0, iconSize, iconSize); if (mLayoutHorizontal) { + int top = (getHeight() - iconSize) / 2; if (mIsRtl) { - outBounds.offsetTo(getWidth() - iconSize - getPaddingRight(), outBounds.top); + outBounds.offsetTo(getWidth() - iconSize - getPaddingRight(), top); } else { - outBounds.offsetTo(getPaddingLeft(), outBounds.top); + outBounds.offsetTo(getPaddingLeft(), top); } } else { - outBounds.offsetTo(outBounds.left, getPaddingTop()); + outBounds.offset((getWidth() - iconSize) / 2, getPaddingTop()); } } @@ -942,6 +943,11 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, 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 3b24df2f18..ee3e278b8d 100644 --- a/src/com/android/launcher3/ButtonDropTarget.java +++ b/src/com/android/launcher3/ButtonDropTarget.java @@ -91,7 +91,7 @@ public abstract class ButtonDropTarget extends TextView 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 @@ public abstract class ButtonDropTarget extends TextView 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 300e7bfb11..ecfd2307c5 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -19,6 +19,7 @@ package com.android.launcher3; 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.ViewGroup; 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 @@ public class CellLayout extends ViewGroup { // 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 @@ public class CellLayout extends ViewGroup { 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 @@ public class CellLayout extends ViewGroup { 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 @@ public class CellLayout extends ViewGroup { 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 @@ public class CellLayout extends ViewGroup { 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 @@ public class CellLayout extends ViewGroup { 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 @@ public class CellLayout extends ViewGroup { 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 @@ public class CellLayout extends ViewGroup { // 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 class CellLayout extends ViewGroup { 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 class CellLayout extends ViewGroup { public void setScrollProgress(float progress) { if (Float.compare(Math.abs(progress), mScrollProgress) != 0) { mScrollProgress = Math.abs(progress); - updateBgAlpha(); + if (!SHOW_HOME_GARDENING.get()) { + updateBgAlpha(); + } } } @@ -615,7 +617,7 @@ public class CellLayout extends ViewGroup { } } - if (mVisualizeDropLocation) { + if (mVisualizeDropLocation && !SHOW_HOME_GARDENING.get()) { for (int i = 0; i < mDragOutlines.length; i++) { final float alpha = mDragOutlineAlphas[i]; if (alpha <= 0) continue; @@ -737,9 +739,19 @@ public class CellLayout extends ViewGroup { 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) { @@ -829,8 +841,8 @@ public class CellLayout extends ViewGroup { final int hStartPadding = getPaddingLeft(); final int vStartPadding = getPaddingTop(); - result[0] = (x - hStartPadding) / mCellWidth; - result[1] = (y - vStartPadding) / mCellHeight; + result[0] = (x - hStartPadding) / (mCellWidth + mBorderSpace.x); + result[1] = (y - vStartPadding) / (mCellHeight + mBorderSpace.y); final int xAxis = mCountX; final int yAxis = mCountY; @@ -842,16 +854,6 @@ public class CellLayout extends ViewGroup { } /** - * Given a point, return the cell that most closely encloses that point - * @param x X coordinate of the point - * @param y Y coordinate of the point - * @param result Array of 2 ints to hold the x and y coordinate of the cell - */ - void pointToCellRounded(int x, int y, int[] result) { - pointToCellExact(x + (mCellWidth / 2), y + (mCellHeight / 2), result); - } - - /** * Given a cell coordinate, return the point that represents the upper left corner of that cell * * @param cellX X coordinate of the cell @@ -922,21 +924,21 @@ public class CellLayout extends ViewGroup { 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; @@ -946,8 +948,9 @@ public class CellLayout extends ViewGroup { 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() { @@ -1056,7 +1059,7 @@ public class CellLayout extends ViewGroup { 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; @@ -1163,7 +1166,7 @@ public class CellLayout extends ViewGroup { mDragOutlineAnims[oldIndex].animateOut(); mDragOutlineCurrent = (oldIndex + 1) % mDragOutlines.length; - LayoutParams cell = mDragOutlines[mDragOutlineCurrent]; + CellLayoutLayoutParams cell = mDragOutlines[mDragOutlineCurrent]; cell.cellX = cellX; cell.cellY = cellY; cell.cellHSpan = spanX; @@ -1202,13 +1205,14 @@ public class CellLayout extends ViewGroup { int row = cellY + 1; int col = workspace.mIsRtl ? mCountX - cellX : cellX + 1; int panelCount = workspace.getPanelCount(); + int screenId = workspace.getIdForScreen(this); + int pageIndex = workspace.getPageIndexForScreenId(screenId); if (panelCount > 1) { // Increment the column if the target is on the right side of a two panel home - int screenId = workspace.getIdForScreen(this); - int pageIndex = workspace.getPageIndexForScreenId(screenId); col += (pageIndex % panelCount) * mCountX; } - return getContext().getString(R.string.move_to_empty_cell, row, col); + return getContext().getString(R.string.move_to_empty_cell_description, row, col, + workspace.getPageDescription(pageIndex)); } } @@ -1239,31 +1243,17 @@ public class CellLayout extends ViewGroup { */ int[] findNearestVacantArea(int pixelX, int pixelY, int minSpanX, int minSpanY, int spanX, int spanY, int[] result, int[] resultSpan) { - return findNearestArea(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY, true, + return findNearestArea(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY, false, 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. - * - * @param pixelX The X location at which you want to search for a vacant area. - * @param pixelY The Y location at which you want to search for a vacant area. + * @param relativeXPos The X location relative to the Cell layout at which you want to search + * for a vacant area. + * @param relativeYPos The Y location relative to the Cell layout at which you want to search + * for a vacant area. * @param minSpanX The minimum horizontal span required * @param minSpanY The minimum vertical span required * @param spanX Horizontal span of the object. @@ -1274,15 +1264,13 @@ public class CellLayout extends ViewGroup { * @return The X, Y cell of a vacant area that can contain this object, * nearest the requested location. */ - private int[] findNearestArea(int pixelX, int pixelY, 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 (pixelX, pixelY) 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. - pixelX -= mCellWidth * (spanX - 1) / 2f; - pixelY -= mCellHeight * (spanY - 1) / 2f; + private int[] findNearestArea(int relativeXPos, int relativeYPos, int minSpanX, int minSpanY, + int spanX, int spanY, boolean ignoreOccupied, int[] result, int[] resultSpan) { + // 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. + relativeXPos = (int) (relativeXPos - (mCellWidth + mBorderSpace.x) * (spanX - 1) / 2f); + relativeYPos = (int) (relativeYPos - (mCellHeight + mBorderSpace.y) * (spanY - 1) / 2f); // Keep track of best-scoring drop area final int[] bestXY = result != null ? result : new int[2]; @@ -1303,7 +1291,7 @@ public class CellLayout extends ViewGroup { for (int x = 0; x < countX - (minSpanX - 1); x++) { int ySize = -1; int xSize = -1; - if (ignoreOccupied) { + if (!ignoreOccupied) { // First, let's see if this thing fits anywhere for (int i = 0; i < minSpanX; i++) { for (int j = 0; j < minSpanY; j++) { @@ -1347,9 +1335,6 @@ public class CellLayout extends ViewGroup { hitMaxY |= ySize >= spanY; incX = !incX; } - incX = true; - hitMaxX = xSize >= spanX; - hitMaxY = ySize >= spanY; } final int[] cellXY = mTmpPoint; cellToCenterPoint(x, y, cellXY); @@ -1357,8 +1342,7 @@ public class CellLayout extends ViewGroup { // 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)) { @@ -1367,7 +1351,7 @@ public class CellLayout extends ViewGroup { } } validRegions.push(currentRect); - double distance = Math.hypot(cellXY[0] - pixelX, cellXY[1] - pixelY); + double distance = Math.hypot(cellXY[0] - relativeXPos, cellXY[1] - relativeYPos); if ((distance <= bestDistance && !contained) || currentRect.contains(bestRect)) { @@ -1388,10 +1372,399 @@ public class CellLayout extends ViewGroup { bestXY[0] = -1; bestXY[1] = -1; } - recycleTempRects(validRegions); return bestXY; } + private void copySolutionToTempState(ItemConfiguration solution, View dragView) { + mTmpOccupied.clear(); + + int childCount = mShortcutsAndWidgets.getChildCount(); + for (int i = 0; i < childCount; i++) { + View child = mShortcutsAndWidgets.getChildAt(i); + if (child == dragView) continue; + CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams(); + CellAndSpan c = solution.map.get(child); + if (c != null) { + lp.tmpCellX = c.cellX; + lp.tmpCellY = c.cellY; + lp.cellHSpan = c.spanX; + lp.cellVSpan = c.spanY; + mTmpOccupied.markCells(c, true); + } + } + mTmpOccupied.markCells(solution, true); + } + + private void animateItemsToSolution(ItemConfiguration solution, View dragView, boolean + commitDragView) { + + GridOccupancy occupied = DESTRUCTIVE_REORDER ? mOccupied : mTmpOccupied; + occupied.clear(); + + int childCount = mShortcutsAndWidgets.getChildCount(); + for (int i = 0; i < childCount; i++) { + View child = mShortcutsAndWidgets.getChildAt(i); + if (child == dragView) continue; + CellAndSpan c = solution.map.get(child); + if (c != null) { + animateChildToPosition(child, c.cellX, c.cellY, REORDER_ANIMATION_DURATION, 0, + DESTRUCTIVE_REORDER, false); + occupied.markCells(c, true); + } + } + if (commitDragView) { + occupied.markCells(solution, true); + } + } + + + // This method starts or changes the reorder preview animations + private void beginOrAdjustReorderPreviewAnimations(ItemConfiguration solution, + View dragView, int mode) { + int childCount = mShortcutsAndWidgets.getChildCount(); + for (int i = 0; i < childCount; i++) { + View child = mShortcutsAndWidgets.getChildAt(i); + if (child == dragView) continue; + CellAndSpan c = solution.map.get(child); + boolean skip = mode == ReorderPreviewAnimation.MODE_HINT && solution.intersectingViews + != null && !solution.intersectingViews.contains(child); + + + 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); + rha.animate(); + } + } + } + + private static final Property<ReorderPreviewAnimation, Float> ANIMATION_PROGRESS = + new Property<ReorderPreviewAnimation, Float>(float.class, "animationProgress") { + @Override + public Float get(ReorderPreviewAnimation anim) { + return anim.animationProgress; + } + + @Override + public void set(ReorderPreviewAnimation anim, Float progress) { + anim.setAnimationProgress(progress); + } + }; + + // Class which represents the reorder preview animations. These animations show that an item is + // in a temporary state, and hint at where the item will return to. + class ReorderPreviewAnimation { + final Reorderable child; + float finalDeltaX; + float finalDeltaY; + float initDeltaX; + float initDeltaY; + final float finalScale; + float initScale; + final int mode; + boolean repeating = false; + private static final int PREVIEW_DURATION = 300; + private static final int HINT_DURATION = Workspace.REORDER_TIMEOUT; + + private static final float CHILD_DIVIDEND = 4.0f; + + public static final int MODE_HINT = 0; + public static final int MODE_PREVIEW = 1; + + float animationProgress = 0; + ValueAnimator a; + + public ReorderPreviewAnimation(Reorderable child, int mode, int cellX0, int cellY0, + int cellX1, int cellY1, int spanX, int spanY) { + regionToCenterPoint(cellX0, cellY0, spanX, spanY, mTmpPoint); + final int x0 = mTmpPoint[0]; + final int y0 = mTmpPoint[1]; + regionToCenterPoint(cellX1, cellY1, spanX, spanY, mTmpPoint); + final int x1 = mTmpPoint[0]; + final int y1 = mTmpPoint[1]; + final int dX = x1 - x0; + final int dY = y1 - y0; + + this.child = child; + this.mode = mode; + finalDeltaX = 0; + finalDeltaY = 0; + + child.getReorderBounceOffset(mTmpPointF); + initDeltaX = mTmpPointF.x; + initDeltaY = mTmpPointF.y; + initScale = child.getReorderBounceScale(); + finalScale = mChildScale - (CHILD_DIVIDEND / child.getView().getWidth()) * initScale; + + int dir = mode == MODE_HINT ? -1 : 1; + if (dX == dY && dX == 0) { + } else { + if (dY == 0) { + finalDeltaX = -dir * Math.signum(dX) * mReorderPreviewAnimationMagnitude; + } else if (dX == 0) { + finalDeltaY = -dir * Math.signum(dY) * mReorderPreviewAnimationMagnitude; + } else { + double angle = Math.atan( (float) (dY) / dX); + finalDeltaX = (int) (-dir * Math.signum(dX) + * Math.abs(Math.cos(angle) * mReorderPreviewAnimationMagnitude)); + finalDeltaY = (int) (-dir * Math.signum(dY) + * Math.abs(Math.sin(angle) * mReorderPreviewAnimationMagnitude)); + } + } + } + + void setInitialAnimationValuesToBaseline() { + initScale = mChildScale; + initDeltaX = 0; + initDeltaY = 0; + } + + void animate() { + boolean noMovement = (finalDeltaX == 0) && (finalDeltaY == 0); + + if (mShakeAnimators.containsKey(child)) { + ReorderPreviewAnimation oldAnimation = mShakeAnimators.get(child); + mShakeAnimators.remove(child); + + if (noMovement) { + // A previous animation for this item exists, and no new animation will exist. + // Finish the old animation smoothly. + oldAnimation.finishAnimation(); + return; + } else { + // A previous animation for this item exists, and a new one will exist. Stop + // the old animation in its tracks, and proceed with the new one. + oldAnimation.cancel(); + } + } + if (noMovement) { + return; + } + + ValueAnimator va = ObjectAnimator.ofFloat(this, ANIMATION_PROGRESS, 0, 1); + a = va; + + // Animations are disabled in power save mode, causing the repeated animation to jump + // spastically between beginning and end states. Since this looks bad, we don't repeat + // the animation in power save mode. + if (areAnimatorsEnabled()) { + va.setRepeatMode(ValueAnimator.REVERSE); + va.setRepeatCount(ValueAnimator.INFINITE); + } + + va.setDuration(mode == MODE_HINT ? HINT_DURATION : PREVIEW_DURATION); + va.setStartDelay((int) (Math.random() * 60)); + va.addListener(new AnimatorListenerAdapter() { + public void onAnimationRepeat(Animator animation) { + // We make sure to end only after a full period + setInitialAnimationValuesToBaseline(); + repeating = true; + } + }); + mShakeAnimators.put(child, this); + va.start(); + } + + private void setAnimationProgress(float progress) { + animationProgress = progress; + float r1 = (mode == MODE_HINT && repeating) ? 1.0f : animationProgress; + float x = r1 * finalDeltaX + (1 - r1) * initDeltaX; + float y = r1 * finalDeltaY + (1 - r1) * initDeltaY; + child.setReorderBounceOffset(x, y); + float s = animationProgress * finalScale + (1 - animationProgress) * initScale; + child.setReorderBounceScale(s); + } + + private void cancel() { + if (a != null) { + a.cancel(); + } + } + + /** + * Smoothly returns the item to its baseline position / scale + */ + @Thunk void finishAnimation() { + if (a != null) { + a.cancel(); + } + + setInitialAnimationValuesToBaseline(); + ValueAnimator va = ObjectAnimator.ofFloat(this, ANIMATION_PROGRESS, + animationProgress, 0); + a = va; + a.setInterpolator(DEACCEL_1_5); + a.setDuration(REORDER_ANIMATION_DURATION); + a.start(); + } + } + + private void completeAndClearReorderPreviewAnimations() { + for (ReorderPreviewAnimation a: mShakeAnimators.values()) { + a.finishAnimation(); + } + mShakeAnimators.clear(); + } + + private void commitTempPlacement(View dragView) { + mTmpOccupied.copyTo(mOccupied); + + int screenId = getWorkspace().getIdForScreen(this); + int container = Favorites.CONTAINER_DESKTOP; + + if (mContainerType == HOTSEAT) { + screenId = -1; + container = Favorites.CONTAINER_HOTSEAT; + } + + int childCount = mShortcutsAndWidgets.getChildCount(); + for (int i = 0; i < childCount; i++) { + View child = mShortcutsAndWidgets.getChildAt(i); + 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 + || info.spanY != lp.cellVSpan); + + info.cellX = lp.cellX = lp.tmpCellX; + info.cellY = lp.cellY = lp.tmpCellY; + info.spanX = lp.cellHSpan; + info.spanY = lp.cellVSpan; + + if (requiresDbUpdate) { + Launcher.cast(mActivity).getModelWriter().modifyItemInDatabase(info, container, + screenId, info.cellX, info.cellY, info.spanX, info.spanY); + } + } + } + } + + private void setUseTempCoords(boolean useTempCoords) { + int childCount = mShortcutsAndWidgets.getChildCount(); + for (int i = 0; i < childCount; i++) { + CellLayoutLayoutParams lp = (CellLayoutLayoutParams) mShortcutsAndWidgets.getChildAt( + i).getLayoutParams(); + lp.useTmpCoords = useTempCoords; + } + } + + /** + * 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, + resultSpan); + if (result[0] >= 0 && result[1] >= 0) { + copyCurrentStateToSolution(solution, false); + solution.cellX = result[0]; + solution.cellY = result[1]; + solution.spanX = resultSpan[0]; + solution.spanY = resultSpan[1]; + solution.isSolution = true; + } else { + solution.isSolution = false; + } + return solution; + } + + // For a given cell and span, fetch the set of views intersecting the region. + private void getViewsIntersectingRegion(int cellX, int cellY, int spanX, int spanY, + View dragView, Rect boundingRect, ArrayList<View> intersectingViews) { + if (boundingRect != null) { + boundingRect.set(cellX, cellY, cellX + spanX, cellY + spanY); + } + intersectingViews.clear(); + Rect r0 = new Rect(cellX, cellY, cellX + spanX, cellY + spanY); + Rect r1 = new Rect(); + final int count = mShortcutsAndWidgets.getChildCount(); + for (int i = 0; i < count; i++) { + View child = mShortcutsAndWidgets.getChildAt(i); + if (child == dragView) continue; + CellLayoutLayoutParams + lp = (CellLayoutLayoutParams) child.getLayoutParams(); + r1.set(lp.cellX, lp.cellY, lp.cellX + lp.cellHSpan, lp.cellY + lp.cellVSpan); + if (Rect.intersects(r0, r1)) { + mIntersectingViews.add(child); + if (boundingRect != null) { + boundingRect.union(r1); + } + } + } + } + + boolean isNearestDropLocationOccupied(int pixelX, int pixelY, int spanX, int spanY, + View dragView, int[] result) { + result = findNearestAreaIgnoreOccupied(pixelX, pixelY, spanX, spanY, result); + getViewsIntersectingRegion(result[0], result[1], spanX, spanY, dragView, null, + mIntersectingViews); + return !mIntersectingViews.isEmpty(); + } + + void revertTempState() { + completeAndClearReorderPreviewAnimations(); + if (isItemPlacementDirty() && !DESTRUCTIVE_REORDER) { + final int count = mShortcutsAndWidgets.getChildCount(); + for (int i = 0; i < count; i++) { + View child = mShortcutsAndWidgets.getChildAt(i); + CellLayoutLayoutParams + lp = (CellLayoutLayoutParams) child.getLayoutParams(); + if (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.cellY) { + lp.tmpCellX = lp.cellX; + lp.tmpCellY = lp.cellY; + animateChildToPosition(child, lp.cellX, lp.cellY, REORDER_ANIMATION_DURATION, + 0, false, false); + } + } + setItemPlacementDirty(false); + } + } + + boolean createAreaForResize(int cellX, int cellY, int spanX, int spanY, + View dragView, int[] direction, boolean commit) { + int[] pixelXY = new int[2]; + regionToCenterPoint(cellX, cellY, spanX, spanY, pixelXY); + + // First we determine if things have moved enough to cause a different layout + ItemConfiguration swapSolution = findReorderSolution(pixelXY[0], pixelXY[1], spanX, spanY, + spanX, spanY, direction, dragView, true, new ItemConfiguration()); + + setUseTempCoords(true); + if (swapSolution != null && swapSolution.isSolution) { + // If we're just testing for a possible location (MODE_ACCEPT_DROP), we don't bother + // committing anything or animating anything as we just want to determine if a solution + // exists + copySolutionToTempState(swapSolution, dragView); + setItemPlacementDirty(true); + animateItemsToSolution(swapSolution, dragView, commit); + + if (commit) { + commitTempPlacement(null); + completeAndClearReorderPreviewAnimations(); + setItemPlacementDirty(false); + } else { + beginOrAdjustReorderPreviewAnimations(swapSolution, dragView, + ReorderPreviewAnimation.MODE_PREVIEW); + } + mShortcutsAndWidgets.requestLayout(); + } + return swapSolution.isSolution; + } + /** * Find a vacant area that will fit the given bounds nearest the requested * cell location, and will also weigh in a suggested direction vector of the @@ -1478,6 +1851,101 @@ public class CellLayout extends ViewGroup { 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 @@ -1663,152 +2131,6 @@ public class CellLayout extends ViewGroup { } } - 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. @@ -1906,6 +2228,123 @@ public class CellLayout extends ViewGroup { 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 @@ -1927,7 +2366,7 @@ public class CellLayout extends ViewGroup { for (View child: solution.map.keySet()) { if (child == ignoreView) continue; CellAndSpan c = solution.map.get(child); - LayoutParams lp = (LayoutParams) child.getLayoutParams(); + 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) { @@ -1962,23 +2401,6 @@ public class CellLayout extends ViewGroup { 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) { @@ -1991,7 +2413,7 @@ public class CellLayout extends ViewGroup { // 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); + result = findNearestAreaIgnoreOccupied(pixelX, pixelY, spanX, spanY, result); boolean success; // First we try the exact nearest position of the item being dragged, @@ -2024,7 +2446,7 @@ public class CellLayout extends ViewGroup { 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(); CellAndSpan c; if (temp) { c = new CellAndSpan(lp.tmpCellX, lp.tmpCellY, lp.cellHSpan, lp.cellVSpan); @@ -2035,532 +2457,163 @@ public class CellLayout extends ViewGroup { } } - private void copySolutionToTempState(ItemConfiguration solution, View dragView) { - mTmpOccupied.clear(); - - int childCount = mShortcutsAndWidgets.getChildCount(); - for (int i = 0; i < childCount; i++) { - View child = mShortcutsAndWidgets.getChildAt(i); - if (child == dragView) continue; - LayoutParams lp = (LayoutParams) child.getLayoutParams(); - CellAndSpan c = solution.map.get(child); - if (c != null) { - lp.tmpCellX = c.cellX; - lp.tmpCellY = c.cellY; - lp.cellHSpan = c.spanX; - lp.cellVSpan = c.spanY; - mTmpOccupied.markCells(c, true); - } - } - mTmpOccupied.markCells(solution, true); - } - - private void animateItemsToSolution(ItemConfiguration solution, View dragView, boolean - commitDragView) { - - GridOccupancy occupied = DESTRUCTIVE_REORDER ? mOccupied : mTmpOccupied; - occupied.clear(); - - int childCount = mShortcutsAndWidgets.getChildCount(); - for (int i = 0; i < childCount; i++) { - View child = mShortcutsAndWidgets.getChildAt(i); - if (child == dragView) continue; - CellAndSpan c = solution.map.get(child); - if (c != null) { - animateChildToPosition(child, c.cellX, c.cellY, REORDER_ANIMATION_DURATION, 0, - DESTRUCTIVE_REORDER, false); - occupied.markCells(c, true); - } - } - if (commitDragView) { - occupied.markCells(solution, true); - } - } - - - // This method starts or changes the reorder preview animations - private void beginOrAdjustReorderPreviewAnimations(ItemConfiguration solution, - View dragView, int mode) { - int childCount = mShortcutsAndWidgets.getChildCount(); - for (int i = 0; i < childCount; i++) { - View child = mShortcutsAndWidgets.getChildAt(i); - if (child == dragView) continue; - CellAndSpan c = solution.map.get(child); - boolean skip = mode == ReorderPreviewAnimation.MODE_HINT && solution.intersectingViews - != null && !solution.intersectingViews.contains(child); - - - LayoutParams lp = (LayoutParams) 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); - rha.animate(); - } + /** + * 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; } - private static final Property<ReorderPreviewAnimation, Float> ANIMATION_PROGRESS = - new Property<ReorderPreviewAnimation, Float>(float.class, "animationProgress") { - @Override - public Float get(ReorderPreviewAnimation anim) { - return anim.animationProgress; - } - - @Override - public void set(ReorderPreviewAnimation anim, Float progress) { - anim.setAnimationProgress(progress); - } - }; - - // Class which represents the reorder preview animations. These animations show that an item is - // in a temporary state, and hint at where the item will return to. - class ReorderPreviewAnimation { - final Reorderable child; - float finalDeltaX; - float finalDeltaY; - float initDeltaX; - float initDeltaY; - final float finalScale; - float initScale; - final int mode; - boolean repeating = false; - private static final int PREVIEW_DURATION = 300; - private static final int HINT_DURATION = Workspace.REORDER_TIMEOUT; - - private static final float CHILD_DIVIDEND = 4.0f; - - public static final int MODE_HINT = 0; - public static final int MODE_PREVIEW = 1; - - float animationProgress = 0; - ValueAnimator a; - - public ReorderPreviewAnimation(Reorderable child, int mode, int cellX0, int cellY0, - int cellX1, int cellY1, int spanX, int spanY) { - regionToCenterPoint(cellX0, cellY0, spanX, spanY, mTmpPoint); - final int x0 = mTmpPoint[0]; - final int y0 = mTmpPoint[1]; - regionToCenterPoint(cellX1, cellY1, spanX, spanY, mTmpPoint); - final int x1 = mTmpPoint[0]; - final int y1 = mTmpPoint[1]; - final int dX = x1 - x0; - final int dY = y1 - y0; - - this.child = child; - this.mode = mode; - finalDeltaX = 0; - finalDeltaY = 0; - - child.getReorderBounceOffset(mTmpPointF); - initDeltaX = mTmpPointF.x; - initDeltaY = mTmpPointF.y; - initScale = child.getReorderBounceScale(); - finalScale = mChildScale - (CHILD_DIVIDEND / child.getView().getWidth()) * initScale; - - int dir = mode == MODE_HINT ? -1 : 1; - if (dX == dY && dX == 0) { - } else { - if (dY == 0) { - finalDeltaX = -dir * Math.signum(dX) * mReorderPreviewAnimationMagnitude; - } else if (dX == 0) { - finalDeltaY = -dir * Math.signum(dY) * mReorderPreviewAnimationMagnitude; - } else { - double angle = Math.atan( (float) (dY) / dX); - finalDeltaX = (int) (-dir * Math.signum(dX) - * Math.abs(Math.cos(angle) * mReorderPreviewAnimationMagnitude)); - finalDeltaY = (int) (-dir * Math.signum(dY) - * Math.abs(Math.sin(angle) * mReorderPreviewAnimationMagnitude)); - } - } - } - - void setInitialAnimationValuesToBaseline() { - initScale = mChildScale; - initDeltaX = 0; - initDeltaY = 0; - } - - void animate() { - boolean noMovement = (finalDeltaX == 0) && (finalDeltaY == 0); - - if (mShakeAnimators.containsKey(child)) { - ReorderPreviewAnimation oldAnimation = mShakeAnimators.get(child); - mShakeAnimators.remove(child); - - if (noMovement) { - // A previous animation for this item exists, and no new animation will exist. - // Finish the old animation smoothly. - oldAnimation.finishAnimation(); - return; - } else { - // A previous animation for this item exists, and a new one will exist. Stop - // the old animation in its tracks, and proceed with the new one. - oldAnimation.cancel(); - } - } - if (noMovement) { - return; - } - - ValueAnimator va = ObjectAnimator.ofFloat(this, ANIMATION_PROGRESS, 0, 1); - a = va; - - // Animations are disabled in power save mode, causing the repeated animation to jump - // spastically between beginning and end states. Since this looks bad, we don't repeat - // the animation in power save mode. - if (areAnimatorsEnabled()) { - va.setRepeatMode(ValueAnimator.REVERSE); - va.setRepeatCount(ValueAnimator.INFINITE); - } - - va.setDuration(mode == MODE_HINT ? HINT_DURATION : PREVIEW_DURATION); - va.setStartDelay((int) (Math.random() * 60)); - va.addListener(new AnimatorListenerAdapter() { - public void onAnimationRepeat(Animator animation) { - // We make sure to end only after a full period - setInitialAnimationValuesToBaseline(); - repeating = true; - } - }); - mShakeAnimators.put(child, this); - va.start(); - } - - private void setAnimationProgress(float progress) { - animationProgress = progress; - float r1 = (mode == MODE_HINT && repeating) ? 1.0f : animationProgress; - float x = r1 * finalDeltaX + (1 - r1) * initDeltaX; - float y = r1 * finalDeltaY + (1 - r1) * initDeltaY; - child.setReorderBounceOffset(x, y); - float s = animationProgress * finalScale + (1 - animationProgress) * initScale; - child.setReorderBounceScale(s); - } + /** + * 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); - private void cancel() { - if (a != null) { - a.cancel(); - } - } + ItemConfiguration dropInPlaceSolution = dropInPlaceSolution(pixelX, pixelY, spanX, spanY, + dragView); - /** - * Smoothly returns the item to its baseline position / scale - */ - @Thunk void finishAnimation() { - if (a != null) { - a.cancel(); - } + // 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()); - setInitialAnimationValuesToBaseline(); - ValueAnimator va = ObjectAnimator.ofFloat(this, ANIMATION_PROGRESS, - animationProgress, 0); - a = va; - a.setInterpolator(DEACCEL_1_5); - a.setDuration(REORDER_ANIMATION_DURATION); - a.start(); - } - } + // We attempt the approach which doesn't shuffle views at all + ItemConfiguration closestSpaceSolution = closestEmptySpaceReorder(pixelX, pixelY, minSpanX, + minSpanY, spanX, spanY); - private void completeAndClearReorderPreviewAnimations() { - for (ReorderPreviewAnimation a: mShakeAnimators.values()) { - a.finishAnimation(); + // 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() >= closestSpaceSolution.area()) { + return swapSolution; + } else if (closestSpaceSolution.isSolution) { + return closestSpaceSolution; + } else if (dropInPlaceSolution.isSolution) { + return dropInPlaceSolution; } - mShakeAnimators.clear(); + return null; } - private void commitTempPlacement(View dragView) { - mTmpOccupied.copyTo(mOccupied); - - int screenId = getWorkspace().getIdForScreen(this); - int container = Favorites.CONTAINER_DESKTOP; - - if (mContainerType == HOTSEAT) { - screenId = -1; - container = Favorites.CONTAINER_HOTSEAT; - } - - int childCount = mShortcutsAndWidgets.getChildCount(); - for (int i = 0; i < childCount; i++) { - View child = mShortcutsAndWidgets.getChildAt(i); - LayoutParams lp = (LayoutParams) 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 - || info.spanY != lp.cellVSpan); - - info.cellX = lp.cellX = lp.tmpCellX; - info.cellY = lp.cellY = lp.tmpCellY; - info.spanX = lp.cellHSpan; - info.spanY = lp.cellVSpan; - - if (requiresDbUpdate) { - Launcher.cast(mActivity).getModelWriter().modifyItemInDatabase(info, container, - screenId, info.cellX, info.cellY, info.spanX, info.spanY); - } - } + 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}; } - } - - private void setUseTempCoords(boolean useTempCoords) { - int childCount = mShortcutsAndWidgets.getChildCount(); - for (int i = 0; i < childCount; i++) { - LayoutParams lp = (LayoutParams) mShortcutsAndWidgets.getChildAt(i).getLayoutParams(); - lp.useTmpCoords = useTempCoords; + if (result == null) { + result = new int[]{-1, -1}; } - } - private ItemConfiguration findConfigurationNoShuffle(int pixelX, int pixelY, int minSpanX, int minSpanY, - int spanX, int spanY, View dragView, ItemConfiguration solution) { - int[] result = new int[2]; - int[] resultSpan = new int[2]; - findNearestVacantArea(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY, result, - resultSpan); - if (result[0] >= 0 && result[1] >= 0) { - copyCurrentStateToSolution(solution, false); - solution.cellX = result[0]; - solution.cellY = result[1]; - solution.spanX = resultSpan[0]; - solution.spanY = resultSpan[1]; - solution.isSolution = true; + 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 { - solution.isSolution = false; - } - 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; + finalSolution = mPreviousSolution; + // We reset this vector after drop + if (mode == MODE_ON_DROP || mode == MODE_ON_DROP_EXTERNAL) { + mPreviousSolution = null; + } } - if (deltaX == 0 && deltaY == 0) { - // No idea what to do, give a random direction. - resultDirection[0] = 1; - resultDirection[1] = 0; + if (finalSolution == null || !finalSolution.isSolution) { + result[0] = result[1] = resultSpan[0] = resultSpan[1] = -1; } else { - computeDirectionVector(deltaX, deltaY, resultDirection); + result[0] = finalSolution.cellX; + result[1] = finalSolution.cellY; + resultSpan[0] = finalSolution.spanX; + resultSpan[1] = finalSolution.spanY; + performReorder(finalSolution, dragView, mode); } + return result; } - // For a given cell and span, fetch the set of views intersecting the region. - private void getViewsIntersectingRegion(int cellX, int cellY, int spanX, int spanY, - View dragView, Rect boundingRect, ArrayList<View> intersectingViews) { - if (boundingRect != null) { - boundingRect.set(cellX, cellY, cellX + spanX, cellY + spanY); + /** + * 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; } - intersectingViews.clear(); - Rect r0 = new Rect(cellX, cellY, cellX + spanX, cellY + spanY); - Rect r1 = new Rect(); - final int count = mShortcutsAndWidgets.getChildCount(); - for (int i = 0; i < count; i++) { - View child = mShortcutsAndWidgets.getChildAt(i); - if (child == dragView) continue; - LayoutParams lp = (LayoutParams) child.getLayoutParams(); - r1.set(lp.cellX, lp.cellY, lp.cellX + lp.cellHSpan, lp.cellY + lp.cellVSpan); - if (Rect.intersects(r0, r1)) { - mIntersectingViews.add(child); - if (boundingRect != null) { - boundingRect.union(r1); - } + // 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); } - } - } - - boolean isNearestDropLocationOccupied(int pixelX, int pixelY, int spanX, int spanY, - View dragView, int[] result) { - result = findNearestArea(pixelX, pixelY, spanX, spanY, result); - getViewsIntersectingRegion(result[0], result[1], spanX, spanY, dragView, null, - mIntersectingViews); - return !mIntersectingViews.isEmpty(); - } - void revertTempState() { - completeAndClearReorderPreviewAnimations(); - if (isItemPlacementDirty() && !DESTRUCTIVE_REORDER) { - final int count = mShortcutsAndWidgets.getChildCount(); - for (int i = 0; i < count; i++) { - View child = mShortcutsAndWidgets.getChildAt(i); - 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); - } + if (!DESTRUCTIVE_REORDER) { + copySolutionToTempState(solution, dragView); } - setItemPlacementDirty(false); - } - } - - boolean createAreaForResize(int cellX, int cellY, int spanX, int spanY, - View dragView, int[] direction, boolean commit) { - int[] pixelXY = new int[2]; - regionToCenterPoint(cellX, cellY, spanX, spanY, pixelXY); - - // First we determine if things have moved enough to cause a different layout - ItemConfiguration swapSolution = findReorderSolution(pixelXY[0], pixelXY[1], spanX, spanY, - spanX, spanY, direction, dragView, true, new ItemConfiguration()); - - setUseTempCoords(true); - if (swapSolution != null && swapSolution.isSolution) { - // If we're just testing for a possible location (MODE_ACCEPT_DROP), we don't bother - // committing anything or animating anything as we just want to determine if a solution - // exists - copySolutionToTempState(swapSolution, dragView); setItemPlacementDirty(true); - animateItemsToSolution(swapSolution, dragView, commit); + animateItemsToSolution(solution, dragView, mode == MODE_ON_DROP); - if (commit) { - commitTempPlacement(null); + 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(swapSolution, dragView, + beginOrAdjustReorderPreviewAnimations(solution, dragView, ReorderPreviewAnimation.MODE_PREVIEW); } - mShortcutsAndWidgets.requestLayout(); - } - 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); - - if (resultSpan == null) { - resultSpan = new int[2]; - } - - // 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; - } - } else { - getDirectionVectorForDrop(pixelX, pixelY, spanX, spanY, dragView, mDirectionVector); - mPreviousReorderDirection[0] = mDirectionVector[0]; - mPreviousReorderDirection[1] = mDirectionVector[1]; - } - - // 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; - - // 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 (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; - } - return result; - } - - boolean foundSolution = true; - if (!DESTRUCTIVE_REORDER) { - setUseTempCoords(true); - } - - if (finalSolution != null) { - result[0] = finalSolution.cellX; - result[1] = finalSolution.cellY; - resultSpan[0] = finalSolution.spanX; - resultSpan[1] = finalSolution.spanY; - - // 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); - } - } - } else { - foundSolution = false; - result[0] = result[1] = resultSpan[0] = resultSpan[1] = -1; } - 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) { @@ -2627,8 +2680,9 @@ public class CellLayout extends ViewGroup { * @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) { - return findNearestArea(pixelX, pixelY, spanX, spanY, spanX, spanY, false, result, null); + public int[] findNearestAreaIgnoreOccupied(int pixelX, int pixelY, int spanX, int spanY, + int[] result) { + return findNearestArea(pixelX, pixelY, spanX, spanY, spanX, spanY, true, result, null); } boolean existsEmptyCell() { @@ -2662,6 +2716,7 @@ public class CellLayout extends ViewGroup { */ void onDragEnter() { mDragging = true; + mPreviousSolution = null; } /** @@ -2676,6 +2731,7 @@ public class CellLayout extends ViewGroup { } // Invalidate the drag data + mPreviousSolution = null; mDragCell[0] = mDragCell[1] = -1; mDragCellSpan[0] = mDragCellSpan[1] = -1; mDragOutlineAnims[mDragOutlineCurrent].animateOut(); @@ -2693,7 +2749,8 @@ public class CellLayout extends ViewGroup { */ void onDropChild(View child) { if (child != null) { - LayoutParams lp = (LayoutParams) child.getLayoutParams(); + CellLayoutLayoutParams + lp = (CellLayoutLayoutParams) child.getLayoutParams(); lp.dropped = true; child.requestLayout(); markCellsAsOccupiedForView(child); @@ -2735,7 +2792,8 @@ public class CellLayout extends ViewGroup { return; } if (view == null || view.getParent() != mShortcutsAndWidgets) return; - LayoutParams lp = (LayoutParams) view.getLayoutParams(); + CellLayoutLayoutParams + lp = (CellLayoutLayoutParams) view.getLayoutParams(); mOccupied.markCells(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, true); } @@ -2747,7 +2805,8 @@ public class CellLayout extends ViewGroup { return; } if (view == null || view.getParent() != mShortcutsAndWidgets) return; - LayoutParams lp = (LayoutParams) view.getLayoutParams(); + CellLayoutLayoutParams + lp = (CellLayoutLayoutParams) view.getLayoutParams(); mOccupied.markCells(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, false); } @@ -2771,165 +2830,17 @@ public class CellLayout extends ViewGroup { @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 c707df04c0..3e4e96bec0 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 @@ public class CheckLongPressHelper { private float mLongPressTimeoutFactor = DEFAULT_LONG_PRESS_TIMEOUT_FACTOR; private boolean mHasPerformedLongPress; + private boolean mIsInMouseRightClick; private Runnable mPendingCheckForLongPress; @@ -59,6 +64,26 @@ public class CheckLongPressHelper { // 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 @@ public class CheckLongPressHelper { 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 @@ public class CheckLongPressHelper { } /** - * 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 4daca8b109..c69ae4dac8 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 android.util.Log; 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 @@ public class DefaultLayoutParser extends AutoInstallsLayout { 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 @@ public class DefaultLayoutParser extends AutoInstallsLayout { // 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 @@ public class DefaultLayoutParser extends AutoInstallsLayout { 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 @@ public class DefaultLayoutParser extends AutoInstallsLayout { 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 b2763977ca..25520e1bea 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_DEFAULT; import static com.android.launcher3.InvariantDeviceProfile.INDEX_LANDSCAPE; import static com.android.launcher3.InvariantDeviceProfile.INDEX_TWO_PANEL_LANDSCAPE; import static com.android.launcher3.InvariantDeviceProfile.INDEX_TWO_PANEL_PORTRAIT; -import static com.android.launcher3.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 class DeviceProfile { public final boolean isTablet; public final boolean isPhone; public final boolean transposeLayoutWithOrientation; + public final boolean isMultiDisplay; public final boolean isTwoPanels; public final boolean isQsbInline; @@ -107,15 +123,13 @@ public class DeviceProfile { public Rect cellLayoutPaddingPx = new Rect(); public final int edgeMarginPx; - public float workspaceSpringLoadShrunkTop; - public float workspaceSpringLoadShrunkBottom; - public final int workspaceSpringLoadedBottomSpace; + public final float workspaceContentScale; public final int workspaceSpringLoadedMinNextPageVisiblePx; private final int extraSpace; + private int maxEmptySpace; public int workspaceTopPadding; public int workspaceBottomPadding; - public int extraHotseatBottomPadding; // Workspace page indicator public final int workspacePageIndicatorHeight; @@ -138,12 +152,12 @@ public class DeviceProfile { // 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; @@ -157,31 +171,41 @@ public class DeviceProfile { public int folderChildDrawablePaddingPx; // Hotseat - public int hotseatBarSizeExtraSpacePx; - public final int numShownHotseatIcons; + public int numShownHotseatIcons; public int hotseatCellHeightPx; - private final int hotseatExtraVerticalSize; - private final boolean areNavButtonsInline; + public final boolean areNavButtonsInline; // In portrait: size = height, in landscape: size = width public int hotseatBarSizePx; - public int hotseatBarTopPaddingPx; - public final int hotseatBarBottomPaddingPx; + public int hotseatBarBottomSpacePx; + public int hotseatBarEndOffset; + public int hotseatQsbSpace; public int springLoadedHotseatBarTopMarginPx; // 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 final float qsbBottomMarginOriginalPx; - public int qsbBottomMarginPx; - 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; @@ -193,7 +217,6 @@ public class DeviceProfile { // Overview public int overviewTaskMarginPx; - public int overviewTaskMarginGridPx; public int overviewTaskIconSizePx; public int overviewTaskIconDrawableSizePx; public int overviewTaskIconDrawableSizeGridPx; @@ -205,8 +228,11 @@ public class DeviceProfile { 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; @@ -222,13 +248,12 @@ public class DeviceProfile { // Insets private final Rect mInsets = new Rect(); public final Rect workspacePadding = new Rect(); - private final Rect mHotseatPadding = 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; @@ -236,19 +261,22 @@ public class DeviceProfile { public boolean isTaskbarPresentInApps; public int taskbarSize; 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; @@ -260,7 +288,7 @@ public class DeviceProfile { mInfo = info; isTablet = info.isTablet(windowBounds); isPhone = !isTablet; - isTwoPanels = isTablet && useTwoPanels; + isTwoPanels = isTablet && isMultiDisplay; isTaskbarPresent = isTablet && ApiWrapper.TASKBAR_DRAWN_IN_PROCESS; // Some more constants. @@ -275,7 +303,7 @@ public class DeviceProfile { widthPx = windowBounds.bounds.width(); heightPx = windowBounds.bounds.height(); availableWidthPx = windowBounds.availableSize.x; - availableHeightPx = windowBounds.availableSize.y; + availableHeightPx = windowBounds.availableSize.y; aspectRatio = ((float) Math.max(widthPx, heightPx)) / Math.min(widthPx, heightPx); boolean isTallDevice = Float.compare(aspectRatio, TALL_DEVICE_ASPECT_RATIO_THRESHOLD) >= 0; @@ -296,11 +324,20 @@ public class DeviceProfile { } if (isTaskbarPresent) { - taskbarSize = res.getDimensionPixelSize(R.dimen.taskbar_size); - stashedTaskbarSize = res.getDimensionPixelSize(R.dimen.taskbar_stashed_size); + if (DisplayController.isTransientTaskbar(context)) { + taskbarSize = res.getDimensionPixelSize(R.dimen.transient_taskbar_size); + stashedTaskbarSize = + res.getDimensionPixelSize(R.dimen.transient_taskbar_stashed_size); + transientTaskbarMargin = + res.getDimensionPixelSize(R.dimen.transient_taskbar_margin); + } else { + taskbarSize = res.getDimensionPixelSize(R.dimen.taskbar_size); + stashedTaskbarSize = res.getDimensionPixelSize(R.dimen.taskbar_stashed_size); + } } edgeMarginPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin); + workspaceContentScale = res.getFloat(R.dimen.workspace_content_scale); desiredWorkspaceHorizontalMarginPx = getHorizontalMarginPx(inv, res); desiredWorkspaceHorizontalMarginOriginalPx = desiredWorkspaceHorizontalMarginPx; @@ -312,24 +349,57 @@ public class DeviceProfile { 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); @@ -352,44 +422,50 @@ public class DeviceProfile { dropTargetButtonWorkspaceEdgeGapPx = res.getDimensionPixelSize( R.dimen.drop_target_button_workspace_edge_gap); - workspaceSpringLoadedBottomSpace = - res.getDimensionPixelSize(R.dimen.dynamic_grid_min_spring_loaded_space); workspaceSpringLoadedMinNextPageVisiblePx = res.getDimensionPixelSize( R.dimen.dynamic_grid_spring_loaded_min_next_space_visible); workspaceCellPaddingXPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_padding_x); hotseatQsbHeight = res.getDimensionPixelSize(R.dimen.qsb_widget_height); + hotseatQsbShadowHeight = res.getDimensionPixelSize(R.dimen.qsb_shadow_height); + hotseatQsbVisualHeight = hotseatQsbHeight - 2 * hotseatQsbShadowHeight; + // Whether QSB might be inline in appropriate orientation (e.g. landscape). boolean canQsbInline = (isTwoPanels ? inv.inlineQsb[INDEX_TWO_PANEL_PORTRAIT] || 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; - hotseatBarSizeExtraSpacePx = 0; - hotseatBarTopPaddingPx = - res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_top_padding); - if (isQsbInline) { - hotseatBarBottomPaddingPx = res.getDimensionPixelSize(R.dimen.inline_qsb_bottom_margin); + + int hotseatBarBottomSpace = pxFromDp(inv.hotseatBarBottomSpace[mTypeIndex], mMetrics); + int minQsbMargin = res.getDimensionPixelSize(R.dimen.min_qsb_margin); + hotseatQsbSpace = pxFromDp(inv.hotseatQsbSpace[mTypeIndex], mMetrics); + // Have a little space between the inset and the QSB + if (mInsets.bottom + minQsbMargin > hotseatBarBottomSpace) { + int availableSpace = hotseatQsbSpace - (mInsets.bottom - hotseatBarBottomSpace); + + // Only change the spaces if there is space + if (availableSpace > 0) { + // Make sure there is enough space between hotseat/QSB and QSB/navBar + if (availableSpace < minQsbMargin * 2) { + minQsbMargin = availableSpace / 2; + hotseatQsbSpace = minQsbMargin; + } else { + hotseatQsbSpace -= minQsbMargin; + } + } + hotseatBarBottomSpacePx = mInsets.bottom + minQsbMargin; + } else { - hotseatBarBottomPaddingPx = (isTallDevice ? res.getDimensionPixelSize( - R.dimen.dynamic_grid_hotseat_bottom_tall_padding) - : res.getDimensionPixelSize( - R.dimen.dynamic_grid_hotseat_bottom_non_tall_padding)) - + res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_bottom_padding); + hotseatBarBottomSpacePx = hotseatBarBottomSpace; } springLoadedHotseatBarTopMarginPx = res.getDimensionPixelSize( @@ -398,47 +474,49 @@ public class DeviceProfile { res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_side_padding); // Add a bit of space between nav bar and hotseat in vertical bar layout. hotseatBarSidePaddingStartPx = isVerticalBarLayout() ? workspacePageIndicatorHeight : 0; - hotseatExtraVerticalSize = - res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_extra_vertical_size); - updateHotseatIconSize(pxFromDp(inv.iconSize[INDEX_DEFAULT], mMetrics)); - - qsbBottomMarginOriginalPx = isScalableGrid - ? res.getDimensionPixelSize(R.dimen.scalable_grid_qsb_bottom_margin) - : 0; + updateHotseatSizes(pxFromDp(inv.iconSize[INDEX_DEFAULT], mMetrics)); + if (areNavButtonsInline && !isPhone) { + inlineNavButtonsEndSpacing = res.getDimensionPixelSize(inv.inlineNavButtonsEndSpacing); + /* + * 3 nav buttons + + * Spacing between 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) + + 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; - // In vertical bar, use the smaller task margin for the top regardless of mode. - overviewActionsTopMarginPx = isVerticalBarLayout() - ? overviewTaskMarginPx - : res.getDimensionPixelSize(R.dimen.overview_actions_top_margin); + 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); @@ -446,42 +524,6 @@ public class DeviceProfile { workspaceTopPadding = Math.round(paddingWorkspaceTop * cellScaleToFit); workspaceBottomPadding = Math.round(paddingWorkspaceBottom * cellScaleToFit); - extraHotseatBottomPadding = Math.round(paddingHotseatBottom * cellScaleToFit); - - hotseatBarSizePx += extraHotseatBottomPadding; - - qsbBottomMarginPx = Math.round(qsbBottomMarginOriginalPx * cellScaleToFit); - } else if (!isVerticalBarLayout() && isPhone && isTallDevice) { - // We increase the hotseat size when there is extra space. - - if (Float.compare(aspectRatio, TALLER_DEVICE_ASPECT_RATIO_THRESHOLD) >= 0 - && extraSpace >= Utilities.dpToPx(TALL_DEVICE_EXTRA_SPACE_THRESHOLD_DP)) { - // For taller devices, we will take a piece of the extra space from each row, - // and add it to the space above and below the hotseat. - - // For devices with more extra space, we take a larger piece from each cell. - int piece = extraSpace < Utilities.dpToPx(TALL_DEVICE_MORE_EXTRA_SPACE_THRESHOLD_DP) - ? 7 : 5; - - int extraSpace = ((getCellSize().y - iconSizePx - iconDrawablePaddingPx * 2) - * inv.numRows) / piece; - - workspaceTopPadding = extraSpace / 8; - int halfLeftOver = (extraSpace - workspaceTopPadding) / 2; - hotseatBarTopPaddingPx += halfLeftOver; - hotseatBarSizeExtraSpacePx = halfLeftOver; - } else { - // ie. For a display with a large aspect ratio, we can keep the icons on the - // workspace in portrait mode closer together by adding more height to the hotseat. - // Note: This calculation was created after noticing a pattern in the design spec. - hotseatBarSizeExtraSpacePx = getCellSize().y - iconSizePx - - iconDrawablePaddingPx * 2 - workspacePageIndicatorHeight; - } - - updateHotseatIconSize(iconSizePx); - - // Recalculate the available dimensions using the new hotseat size. - updateAvailableDimensions(res); } int cellLayoutPadding = @@ -491,25 +533,54 @@ public class DeviceProfile { 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) @@ -524,7 +595,7 @@ public class DeviceProfile { 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) { @@ -537,22 +608,80 @@ public class DeviceProfile { : res.getDimensionPixelSize(R.dimen.dynamic_grid_left_right_margin); } - private void updateHotseatIconSize(int hotseatIconSizePx) { + /** Updates hotseatCellHeightPx and hotseatBarSizePx */ + private void updateHotseatSizes(int hotseatIconSizePx) { // Ensure there is enough space for folder icons, which have a slightly larger radius. hotseatCellHeightPx = (int) Math.ceil(hotseatIconSizePx * ICON_OVERLAP_FACTOR); + if (isVerticalBarLayout()) { hotseatBarSizePx = hotseatIconSizePx + hotseatBarSidePaddingStartPx + hotseatBarSidePaddingEndPx; + } else if (isQsbInline) { + hotseatBarSizePx = Math.max(hotseatIconSizePx, hotseatQsbVisualHeight) + + hotseatBarBottomSpacePx; } else { - hotseatBarSizePx = hotseatIconSizePx + hotseatBarTopPaddingPx - + hotseatBarBottomPaddingPx + (isScalableGrid ? 0 : hotseatExtraVerticalSize) - + hotseatBarSizeExtraSpacePx; + hotseatBarSizePx = hotseatIconSizePx + + hotseatQsbSpace + + hotseatQsbVisualHeight + + hotseatBarBottomSpacePx; } } + /** + * 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); - } private Point getCellLayoutBorderSpace(InvariantDeviceProfile idp, float scale) { @@ -591,10 +720,16 @@ public class DeviceProfile { 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); } @@ -611,13 +746,18 @@ public class DeviceProfile { .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; } @@ -672,6 +812,11 @@ public class DeviceProfile { * 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(); @@ -728,20 +873,52 @@ public class DeviceProfile { // 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 and distribute the space for + // cellHeight, and reduce iconSize. + cellHeightPx = (cellHeightPx * inv.numRows + + cellLayoutBorderSpacePx.y * numBorders) / inv.numRows; + iconSizePx = Math.min(iconSizePx, cellHeightPx - cellTextAndPaddingHeight); + cellLayoutBorderSpacePx.y = 0; + } + cellContentHeight = iconSizePx + cellTextAndPaddingHeight; + } cellYPaddingPx = Math.max(0, cellHeightPx - cellContentHeight) / 2; desiredWorkspaceHorizontalMarginPx = (int) (desiredWorkspaceHorizontalMarginOriginalPx * scale); @@ -764,7 +941,7 @@ public class DeviceProfile { // All apps updateAllAppsIconSize(scale, res); - updateHotseatIconSize(iconSizePx); + updateHotseatSizes(iconSizePx); // Folder icon folderIconSizePx = IconNormalizer.getNormalizedCircleSize(iconSizePx); @@ -772,20 +949,14 @@ public class DeviceProfile { } /** - * 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); } @@ -798,17 +969,41 @@ public class DeviceProfile { 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 - allAppsCellWidthPx = pxFromDp(inv.allAppsCellSize[mTypeIndex].x, mMetrics, scale); 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]; @@ -816,6 +1011,7 @@ public class DeviceProfile { allAppsIconTextSizePx = (int) (pxFromSp(invIconTextSizeSp, mMetrics) * scale); allAppsIconDrawablePaddingPx = res.getDimensionPixelSize(R.dimen.all_apps_icon_drawable_padding); + allAppsCellWidthPx = allAppsIconSizePx + (2 * allAppsIconDrawablePaddingPx); } updateAllAppsContainerWidth(res); @@ -827,22 +1023,20 @@ public class DeviceProfile { 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; @@ -854,27 +1048,21 @@ public class DeviceProfile { } 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; - - folderCellWidthPx = (int) Math.max(minWidth, cellWidthPx * scale); - folderCellHeightPx = (int) Math.max(minHeight, cellHeightPx * scale); + if (inv.folderStyle == INVALID_RESOURCE_HANDLE) { + folderCellWidthPx = pxFromDp(getCellSize().x, mMetrics, scale); + folderCellHeightPx = pxFromDp(getCellSize().y, mMetrics, 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); @@ -883,6 +1071,10 @@ public class DeviceProfile { 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, @@ -922,6 +1114,13 @@ public class DeviceProfile { } /** + * 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() { @@ -932,46 +1131,42 @@ public class DeviceProfile { * Gets the space in px from the bottom of last item in the vertical-bar hotseat to the * bottom of the screen. */ - public int getVerticalHotseatLastItemBottomOffset() { + private int getVerticalHotseatLastItemBottomOffset(Context context) { + Rect hotseatBarPadding = getHotseatLayoutPadding(context); int cellHeight = calculateCellHeight( - heightPx - mHotseatPadding.top - mHotseatPadding.bottom, hotseatBorderSpace, + heightPx - hotseatBarPadding.top - hotseatBarPadding.bottom, hotseatBorderSpace, numShownHotseatIcons); - int hotseatSize = (cellHeight * numShownHotseatIcons) - + (hotseatBorderSpace * (numShownHotseatIcons - 1)); - int extraHotseatEndSpacing = (heightPx - hotseatSize) / 2; int extraIconEndSpacing = (cellHeight - iconSizePx) / 2; - return extraHotseatEndSpacing + extraIconEndSpacing + mHotseatPadding.bottom; + return extraIconEndSpacing + hotseatBarPadding.bottom; } /** * Gets the scaled top of the workspace in px for the spring-loaded edit state. */ public float getCellLayoutSpringLoadShrunkTop() { - workspaceSpringLoadShrunkTop = mInsets.top + dropTargetBarTopMarginPx + dropTargetBarSizePx + return mInsets.top + dropTargetBarTopMarginPx + dropTargetBarSizePx + dropTargetBarBottomMarginPx; - return workspaceSpringLoadShrunkTop; } /** * Gets the scaled bottom of the workspace in px for the spring-loaded edit state. */ - private float getCellLayoutSpringLoadShrunkBottom() { + public float getCellLayoutSpringLoadShrunkBottom(Context context) { int topOfHotseat = hotseatBarSizePx + springLoadedHotseatBarTopMarginPx; - workspaceSpringLoadShrunkBottom = - heightPx - (isVerticalBarLayout() ? getVerticalHotseatLastItemBottomOffset() - : topOfHotseat); - return workspaceSpringLoadShrunkBottom; + 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); @@ -1022,10 +1217,11 @@ public class DeviceProfile { padding.right = hotseatBarSizePx; } } else { - // Pad the bottom of the workspace with search/hotseat bar sizes - int hotseatTop = hotseatBarSizePx; - int paddingBottom = hotseatTop + workspacePageIndicatorHeight - + workspaceBottomPadding - mWorkspacePageIndicatorOverlapWorkspace; + // Pad the bottom of the workspace with hotseat bar + // and leave a bit of space in case a widget go all the way down + int paddingBottom = hotseatBarSizePx + workspaceBottomPadding + + workspacePageIndicatorHeight - mWorkspacePageIndicatorOverlapWorkspace + - mInsets.bottom; int paddingTop = workspaceTopPadding + (isScalableGrid ? 0 : edgeMarginPx); int paddingSide = desiredWorkspaceHorizontalMarginPx; @@ -1052,6 +1248,7 @@ public class DeviceProfile { * 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 @@ -1065,50 +1262,42 @@ public class DeviceProfile { + diffOverlapFactor), 0); if (isSeascape()) { - mHotseatPadding.set(mInsets.left + hotseatBarSidePaddingStartPx, paddingTop, + hotseatBarPadding.set(mInsets.left + hotseatBarSidePaddingStartPx, paddingTop, hotseatBarSidePaddingEndPx, paddingBottom); } else { - mHotseatPadding.set(hotseatBarSidePaddingEndPx, paddingTop, + hotseatBarPadding.set(hotseatBarSidePaddingEndPx, paddingTop, mInsets.right + hotseatBarSidePaddingStartPx, paddingBottom); } } else if (isTaskbarPresent) { // Center the QSB vertically with hotseat - int hotseatBottomPadding = getHotseatBottomPadding(); - int hotseatTopPadding = - workspacePadding.bottom - hotseatBottomPadding - hotseatCellHeightPx; - - // Push icons to the side - int additionalQsbSpace = isQsbInline ? qsbWidth + hotseatBorderSpace : 0; - int requiredWidth = iconSizePx * numShownHotseatIcons - + hotseatBorderSpace * (numShownHotseatIcons - 1) - + additionalQsbSpace; - int endOffset = ApiWrapper.getHotseatEndOffset(context); - int hotseatWidth = Math.min(requiredWidth, availableWidthPx - endOffset); - int sideSpacing = (availableWidthPx - hotseatWidth) / 2; + int hotseatBarBottomPadding = getHotseatBarBottomPadding(); + int hotseatBarTopPadding = + hotseatBarSizePx - hotseatBarBottomPadding - hotseatCellHeightPx; + + 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; + } - mHotseatPadding.set(sideSpacing, hotseatTopPadding, sideSpacing, hotseatBottomPadding); + hotseatBarPadding.set(leftSpacing, hotseatBarTopPadding, rightSpacing, + hotseatBarBottomPadding); boolean isRtl = Utilities.isRtl(context.getResources()); if (isRtl) { - mHotseatPadding.right += additionalQsbSpace; + hotseatBarPadding.right += getAdditionalQsbSpace(); } else { - mHotseatPadding.left += additionalQsbSpace; - } - - if (endOffset > sideSpacing) { - int diff = isRtl - ? sideSpacing - endOffset - : endOffset - sideSpacing; - mHotseatPadding.left -= diff; - mHotseatPadding.right += diff; + hotseatBarPadding.left += getAdditionalQsbSpace(); } } else if (isScalableGrid) { - int sideSpacing = (availableWidthPx - qsbWidth) / 2; - mHotseatPadding.set(sideSpacing, - hotseatBarTopPaddingPx, + int sideSpacing = (availableWidthPx - hotseatQsbWidth) / 2; + hotseatBarPadding.set(sideSpacing, + 0, sideSpacing, - hotseatBarSizePx - hotseatCellHeightPx - hotseatBarTopPaddingPx - + mInsets.bottom); + getHotseatBarBottomPadding()); } else { // We want the edges of the hotseat to line up with the edges of the workspace, but the // icons in the hotseat are a different size, and so don't line up perfectly. To account @@ -1117,14 +1306,29 @@ public class DeviceProfile { float workspaceCellWidth = (float) widthPx / inv.numColumns; float hotseatCellWidth = (float) widthPx / numShownHotseatIcons; int hotseatAdjustment = Math.round((workspaceCellWidth - hotseatCellWidth) / 2); - mHotseatPadding.set(hotseatAdjustment + workspacePadding.left + cellLayoutPaddingPx.left - + mInsets.left, hotseatBarTopPaddingPx, + hotseatBarPadding.set( + hotseatAdjustment + workspacePadding.left + cellLayoutPaddingPx.left + + mInsets.left, + 0, hotseatAdjustment + workspacePadding.right + cellLayoutPaddingPx.right + mInsets.right, - hotseatBarSizePx - hotseatCellHeightPx - hotseatBarTopPaddingPx - + mInsets.bottom); + getHotseatBarBottomPadding()); } - return mHotseatPadding; + 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; } /** @@ -1132,27 +1336,22 @@ public class DeviceProfile { */ public int getQsbOffsetY() { if (isQsbInline) { - return hotseatBarBottomPaddingPx; - } - - int freeSpace = isTaskbarPresent - ? workspacePadding.bottom - : hotseatBarSizePx - hotseatCellHeightPx - hotseatQsbHeight; - - if (isScalableGrid && qsbBottomMarginPx > mInsets.bottom) { - // Note that taskbarSize = 0 unless isTaskbarPresent. - return Math.min(qsbBottomMarginPx + taskbarSize, freeSpace); + return getHotseatBarBottomPadding() - ((hotseatQsbHeight - hotseatCellHeightPx) / 2); + } else if (isTaskbarPresent) { // QSB on top + return hotseatBarSizePx - hotseatQsbHeight + hotseatQsbShadowHeight; } else { - return (int) (freeSpace * mQsbCenterFactor) - + (isTaskbarPresent ? taskbarSize : mInsets.bottom); + return hotseatBarBottomSpacePx - hotseatQsbShadowHeight; } } - private int getHotseatBottomPadding() { - if (isQsbInline) { - return getQsbOffsetY() - (Math.abs(hotseatQsbHeight - hotseatCellHeightPx) / 2); + /** + * Returns the number of pixels the hotseat is translated from the bottom of the screen. + */ + private int getHotseatBarBottomPadding() { + if (isTaskbarPresent) { // QSB on top or inline + return hotseatBarBottomSpacePx - (Math.abs(hotseatCellHeightPx - iconSizePx) / 2); } else { - return (getQsbOffsetY() - taskbarSize) / 2; + return hotseatBarSizePx - hotseatCellHeightPx; } } @@ -1163,19 +1362,24 @@ public class DeviceProfile { int taskbarIconBottomSpace = (taskbarSize - iconSizePx) / 2; int launcherIconBottomSpace = Math.min((hotseatCellHeightPx - iconSizePx) / 2, gridVisualizationPaddingY); - return getHotseatBottomPadding() + launcherIconBottomSpace - taskbarIconBottomSpace; + return getHotseatBarBottomPadding() + launcherIconBottomSpace - taskbarIconBottomSpace; } /** * Returns the number of pixels required below OverviewActions excluding insets. */ public int getOverviewActionsClaimedSpaceBelow() { - if (isTaskbarPresent && !isGestureMode) { - // Align vertically to where nav buttons are. - return ((taskbarSize - overviewActionsHeight) / 2) + getTaskbarOffsetY(); - } + if (isTaskbarPresent) { + if (FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get()) { + return taskbarSize + transientTaskbarMargin * 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. */ @@ -1185,6 +1389,19 @@ public class DeviceProfile { } /** + * 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() { @@ -1267,7 +1484,12 @@ public class DeviceProfile { return "\t" + name + ": " + value + "px (" + dpiFromPx(value, mMetrics.densityDpi) + "dp)"; } - 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"); @@ -1301,7 +1523,7 @@ public class DeviceProfile { 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)); @@ -1313,9 +1535,12 @@ public class DeviceProfile { 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)); @@ -1329,24 +1554,31 @@ public class DeviceProfile { 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", allAppsIconDrawablePaddingPx)); writer.println(prefix + pxToDpStr("allAppsCellHeightPx", allAppsCellHeightPx)); writer.println(prefix + pxToDpStr("allAppsCellWidthPx", allAppsCellWidthPx)); - writer.println(prefix + pxToDpStr("allAppsBorderSpacePx", allAppsBorderSpacePx.x)); + writer.println(prefix + pxToDpStr("allAppsBorderSpacePxX", allAppsBorderSpacePx.x)); + writer.println(prefix + pxToDpStr("allAppsBorderSpacePxY", allAppsBorderSpacePx.y)); writer.println(prefix + "\tnumShownAllAppsColumns: " + numShownAllAppsColumns); writer.println(prefix + pxToDpStr("allAppsLeftRightPadding", allAppsLeftRightPadding)); writer.println(prefix + pxToDpStr("allAppsLeftRightMargin", allAppsLeftRightMargin)); @@ -1354,22 +1586,29 @@ public class DeviceProfile { writer.println(prefix + pxToDpStr("hotseatBarSizePx", hotseatBarSizePx)); writer.println(prefix + "\tinv.hotseatColumnSpan: " + inv.hotseatColumnSpan[mTypeIndex]); writer.println(prefix + pxToDpStr("hotseatCellHeightPx", hotseatCellHeightPx)); - writer.println(prefix + pxToDpStr("hotseatBarTopPaddingPx", hotseatBarTopPaddingPx)); - writer.println(prefix + pxToDpStr("hotseatBarBottomPaddingPx", hotseatBarBottomPaddingPx)); + writer.println(prefix + pxToDpStr("hotseatBarBottomSpacePx", hotseatBarBottomSpacePx)); writer.println(prefix + pxToDpStr("hotseatBarSidePaddingStartPx", hotseatBarSidePaddingStartPx)); writer.println(prefix + pxToDpStr("hotseatBarSidePaddingEndPx", hotseatBarSidePaddingEndPx)); + writer.println(prefix + pxToDpStr("hotseatBarEndOffset", hotseatBarEndOffset)); + writer.println(prefix + pxToDpStr("hotseatQsbSpace", hotseatQsbSpace)); + writer.println(prefix + pxToDpStr("hotseatQsbHeight", hotseatQsbHeight)); writer.println(prefix + pxToDpStr("springLoadedHotseatBarTopMarginPx", springLoadedHotseatBarTopMarginPx)); - writer.println(prefix + pxToDpStr("mHotseatPadding.top", mHotseatPadding.top)); - writer.println(prefix + pxToDpStr("mHotseatPadding.bottom", mHotseatPadding.bottom)); - writer.println(prefix + pxToDpStr("mHotseatPadding.left", mHotseatPadding.left)); - writer.println(prefix + pxToDpStr("mHotseatPadding.right", mHotseatPadding.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); @@ -1387,17 +1626,11 @@ public class DeviceProfile { 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("extraHotseatBottomPadding", extraHotseatBottomPadding)); writer.println(prefix + pxToDpStr("overviewTaskMarginPx", overviewTaskMarginPx)); - writer.println(prefix + pxToDpStr("overviewTaskMarginGridPx", overviewTaskMarginGridPx)); writer.println(prefix + pxToDpStr("overviewTaskIconSizePx", overviewTaskIconSizePx)); writer.println(prefix + pxToDpStr("overviewTaskIconDrawableSizePx", overviewTaskIconDrawableSizePx)); @@ -1409,6 +1642,8 @@ public class DeviceProfile { 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)); @@ -1420,16 +1655,16 @@ public class DeviceProfile { writer.println( prefix + pxToDpStr("dropTargetBarBottomMarginPx", dropTargetBarBottomMarginPx)); - writer.println( - prefix + pxToDpStr("workspaceSpringLoadShrunkTop", workspaceSpringLoadShrunkTop)); - writer.println(prefix + pxToDpStr("workspaceSpringLoadShrunkBottom", - workspaceSpringLoadShrunkBottom)); - writer.println(prefix + pxToDpStr("workspaceSpringLoadedBottomSpace", - workspaceSpringLoadedBottomSpace)); + 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())); } private static Context getContext(Context c, Info info, int orientation, WindowBounds bounds) { @@ -1454,33 +1689,20 @@ public class DeviceProfile { 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 { @@ -1489,11 +1711,14 @@ public class DeviceProfile { 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; @@ -1506,11 +1731,15 @@ public class DeviceProfile { 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; @@ -1527,6 +1756,19 @@ public class DeviceProfile { 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"); @@ -1535,10 +1777,17 @@ public class DeviceProfile { mTransposeLayoutWithOrientation = !mInfo.isTablet(mWindowBounds); } if (mIsGestureMode == null) { - mIsGestureMode = DisplayController.getNavigationMode(mContext).hasGestures; + mIsGestureMode = mInfo.navigationMode.hasGestures; + } + if (mDotRendererCache == null) { + mDotRendererCache = new SparseArray<>(); + } + if (mViewScaleProvider == null) { + mViewScaleProvider = DEFAULT_PROVIDER; } - return new DeviceProfile(mContext, mInv, mInfo, mWindowBounds, mIsMultiWindowMode, - mTransposeLayoutWithOrientation, mUseTwoPanels, mIsGestureMode); + 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 70d84760fc..2d995103e7 100644 --- a/src/com/android/launcher3/DropTarget.java +++ b/src/com/android/launcher3/DropTarget.java @@ -20,7 +20,6 @@ import android.content.Context; 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 interface DropTarget { 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 d908440bc8..52257319f3 100644 --- a/src/com/android/launcher3/DropTargetBar.java +++ b/src/com/android/launcher3/DropTargetBar.java @@ -18,6 +18,7 @@ package com.android.launcher3; 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; @@ -37,7 +38,7 @@ import com.android.launcher3.anim.Interpolators; import com.android.launcher3.dragndrop.DragController; import com.android.launcher3.dragndrop.DragController.DragListener; import com.android.launcher3.dragndrop.DragOptions; -import com.android.launcher3.testing.TestProtocol; +import com.android.launcher3.testing.shared.TestProtocol; /* * The top bar containing various drop targets: Delete/App Info/Uninstall. @@ -118,7 +119,13 @@ public class DropTargetBar extends FrameLayout 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 @@ public class DropTargetBar extends FrameLayout 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 @@ public class DropTargetBar extends FrameLayout 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,28 +186,23 @@ public class DropTargetBar extends FrameLayout 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); firstButton.measure(widthSpec, heightSpec); if (!mIsVertical) { - // Remove icons and put the button's text on two lines if text is truncated. + // Remove both icons and put the button's text on two lines if text is truncated. if (firstButton.isTextTruncated(availableWidth)) { firstButton.setIconVisible(false); + secondButton.setIconVisible(false); firstButton.setTextMultiLine(true); firstButton.setPadding(horizontalPadding, verticalPadding / 2, horizontalPadding, verticalPadding / 2); @@ -209,13 +215,24 @@ public class DropTargetBar extends FrameLayout } secondButton.measure(widthSpec, heightSpec); if (!mIsVertical) { + // Remove both icons and put the button's text on two lines if text is truncated. if (secondButton.isTextTruncated(availableWidth)) { secondButton.setIconVisible(false); + firstButton.setIconVisible(false); secondButton.setTextMultiLine(true); secondButton.setPadding(horizontalPadding, verticalPadding / 2, 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); } @@ -229,7 +246,7 @@ public class DropTargetBar extends FrameLayout 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 4629ca7a2b..f94a3c5521 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 android.widget.EditText; 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 @@ public class ExtendedEditText extends EditText { 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 @@ public class ExtendedEditText extends EditText { 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 f117069144..2f927d3142 100644 --- a/src/com/android/launcher3/FastScrollRecyclerView.java +++ b/src/com/android/launcher3/FastScrollRecyclerView.java @@ -56,7 +56,9 @@ public abstract class FastScrollRecyclerView extends RecyclerView { @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); - bindFastScrollbar(); + if (mScrollbar == null || !mScrollbar.hasRecyclerView()) { + bindFastScrollbar(); + } } public void bindFastScrollbar() { @@ -86,15 +88,19 @@ public abstract class FastScrollRecyclerView extends RecyclerView { * Returns the available scroll height: * AvailableScrollHeight = Total height of the all items - last page height */ - protected abstract int getAvailableScrollHeight(); + protected int getAvailableScrollHeight() { + // AvailableScrollHeight = Total height of the all items - first page height + int firstPageHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom(); + int availableScrollHeight = computeVerticalScrollRange() - firstPageHeight; + return Math.max(0, availableScrollHeight); + } /** * Returns the available scroll bar height: * AvailableScrollBarHeight = Total height of the visible view - thumb height */ protected int getAvailableScrollBarHeight() { - int availableScrollBarHeight = getScrollbarTrackHeight() - mScrollbar.getThumbHeight(); - return availableScrollBarHeight; + return getScrollbarTrackHeight() - mScrollbar.getThumbHeight(); } /** @@ -138,10 +144,7 @@ public abstract class FastScrollRecyclerView extends RecyclerView { // 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; } /** @@ -154,14 +157,6 @@ public abstract class FastScrollRecyclerView extends RecyclerView { /** * Maps the touch (from 0..1) to the adapter position that should be visible. * <p>Override in each subclass of this base class. - * - * @return the scroll top of this recycler view. - */ - public abstract int getCurrentScrollY(); - - /** - * Maps the touch (from 0..1) to the adapter position that should be visible. - * <p>Override in each subclass of this base class. */ public abstract String scrollToPositionAtProgress(float touchFraction); diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java index 76106fc58d..bf492a9313 100644 --- a/src/com/android/launcher3/Hotseat.java +++ b/src/com/android/launcher3/Hotseat.java @@ -47,7 +47,6 @@ public class Hotseat extends CellLayout implements Insettable { private Consumer<Boolean> mOnVisibilityAggregatedCallback; private final View mQsb; - private final int mQsbHeight; public Hotseat(Context context) { this(context, null); @@ -62,8 +61,6 @@ public class Hotseat extends CellLayout implements Insettable { mQsb = LayoutInflater.from(context).inflate(R.layout.search_container_hotseat, this, false); addView(mQsb); - - mQsbHeight = getResources().getDimensionPixelSize(R.dimen.qsb_widget_height); } /** @@ -111,9 +108,7 @@ public class Hotseat extends CellLayout implements Insettable { mQsb.setVisibility(View.VISIBLE); lp.gravity = Gravity.BOTTOM; lp.width = ViewGroup.LayoutParams.MATCH_PARENT; - lp.height = grid.isTaskbarPresent - ? grid.workspacePadding.bottom - : grid.hotseatBarSizePx + insets.bottom; + lp.height = grid.hotseatBarSizePx; } Rect padding = grid.getHotseatLayoutPadding(getContext()); @@ -173,29 +168,29 @@ public class Hotseat extends CellLayout implements Insettable { 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); } @@ -206,6 +201,13 @@ public class Hotseat extends CellLayout implements Insettable { getShortcutsAndWidgets().setAlpha(alpha); } + /** + * Sets the alpha value of just our QSB. + */ + public void setQsbAlpha(float alpha) { + mQsb.setAlpha(alpha); + } + public float getIconsAlpha() { return getShortcutsAndWidgets().getAlpha(); } diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java index 69566705d9..f9d1b98a89 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,16 +45,22 @@ import android.util.TypedValue; 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; import com.android.launcher3.util.DisplayController; 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; @@ -79,7 +87,8 @@ public class InvariantDeviceProfile { @Retention(RetentionPolicy.SOURCE) @IntDef({TYPE_PHONE, TYPE_MULTI_DISPLAY, TYPE_TABLET}) - public @interface DeviceType{} + public @interface DeviceType {} + public static final int TYPE_PHONE = 0; public static final int TYPE_MULTI_DISPLAY = 1; public static final int TYPE_TABLET = 2; @@ -104,6 +113,11 @@ public class InvariantDeviceProfile { 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. */ @@ -125,8 +139,9 @@ public class InvariantDeviceProfile { public PointF[] minCellSize; public PointF[] borderSpaces; - public float folderBorderSpace; - public float[] hotseatBorderSpaces; + public @DimenRes int inlineNavButtonsEndSpacing; + + public @StyleRes int folderStyle; public float[] horizontalMargin; @@ -143,11 +158,6 @@ public class InvariantDeviceProfile { 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. @@ -155,6 +165,8 @@ public class InvariantDeviceProfile { public int numDatabaseHotseatIcons; public int[] hotseatColumnSpan; + public float[] hotseatBarBottomSpace; + public float[] hotseatQsbSpace; /** * Number of columns in the all apps list. @@ -166,21 +178,19 @@ public class InvariantDeviceProfile { * 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; @@ -194,7 +204,10 @@ public class InvariantDeviceProfile { 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(); + Log.d("b/258560494", "InvariantDeviceProfile - setting newGridName: " + newGridName + + ", gridName: " + gridName); } new DeviceGridState(this).writeToPrefs(context); @@ -234,7 +247,8 @@ public class InvariantDeviceProfile { /*allowDisabledGrid=*/false), defaultDeviceType); - Info myInfo = new Info(context, display); + Context displayContext = context.createDisplayContext(display); + Info myInfo = new Info(displayContext); @DeviceType int deviceType = getDeviceType(myInfo); DisplayOption myDisplayOption = invDistWeightedInterpolate( myInfo, @@ -255,8 +269,6 @@ public class InvariantDeviceProfile { COUNT_SIZES); System.arraycopy(defaultDisplayOption.borderSpaces, 0, result.borderSpaces, 0, COUNT_SIZES); - System.arraycopy(defaultDisplayOption.inlineQsb, 0, result.inlineQsb, 0, - COUNT_SIZES); initGrid(context, myInfo, result, deviceType); } @@ -303,8 +315,7 @@ public class InvariantDeviceProfile { } 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) { @@ -320,6 +331,11 @@ public class InvariantDeviceProfile { return displayOption.grid.name; } + @VisibleForTesting + public static String getDefaultGridName(Context context) { + return new InvariantDeviceProfile().initGrid(context, null); + } + private void initGrid(Context context, Info displayInfo, DisplayOption displayOption, @DeviceType int deviceType) { DisplayMetrics metrics = context.getResources().getDisplayMetrics(); @@ -330,12 +346,17 @@ public class InvariantDeviceProfile { 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; + inlineNavButtonsEndSpacing = closestProfile.inlineNavButtonsEndSpacing; + mExtraAttrs = closestProfile.extraAttrs; iconSize = displayOption.iconSizes; @@ -351,16 +372,15 @@ public class InvariantDeviceProfile { 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; numAllAppsColumns = closestProfile.numAllAppsColumns; numDatabaseAllAppsColumns = deviceType == TYPE_MULTI_DISPLAY @@ -370,16 +390,8 @@ public class InvariantDeviceProfile { 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 @@ -387,10 +399,12 @@ public class InvariantDeviceProfile { 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 @@ -410,6 +424,21 @@ public class InvariantDeviceProfile { } 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); } @@ -425,7 +454,8 @@ public class InvariantDeviceProfile { 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(); + Log.d("b/258560494", "setCurrentGrid: " + gridName); MAIN_EXECUTOR.execute(() -> onConfigChanged(appContext)); } @@ -490,6 +520,10 @@ public class InvariantDeviceProfile { } } if (filteredProfiles.isEmpty()) { + if (gridName != null) { + Log.d("b/258560494", "No matching grid from for gridName: " + gridName + + ", deviceType: " + deviceType); + } // No grid found, use the default options for (DisplayOption option : profiles) { if (option.canBeDefault) { @@ -561,8 +595,24 @@ public class InvariantDeviceProfile { */ 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); } } @@ -633,16 +683,16 @@ public class InvariantDeviceProfile { float screenHeight = config.screenHeightDp * res.getDisplayMetrics().density; int rotation = WindowManagerProxy.INSTANCE.get(context).getRotation(context); - if (Utilities.IS_DEBUG_DEVICE) { + 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/231312158", "getDeviceProfile -" + Log.d("b/253338238", "getDeviceProfile -" + "\nconfig: " + config + "\ndisplayMetrics: " + res.getDisplayMetrics() + "\nrotation: " + rotation - + "\n" + stringWriter.toString(), + + "\n" + stringWriter, new Exception()); } return getBestMatch(screenWidth, screenHeight, rotation); @@ -725,6 +775,12 @@ public class InvariantDeviceProfile { 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; @@ -733,14 +789,18 @@ public class InvariantDeviceProfile { 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 final boolean[] inlineQsb = new boolean[COUNT_SIZES]; + + private @DimenRes int inlineNavButtonsEndSpacing; private final String dbFile; private final int defaultLayoutId; @@ -761,10 +821,8 @@ public class InvariantDeviceProfile { 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); @@ -775,10 +833,9 @@ public class InvariantDeviceProfile { 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); + hotseatColumnSpan[INDEX_DEFAULT] = a.getInt( R.styleable.GridDisplayOption_hotseatColumnSpan, numColumns); hotseatColumnSpan[INDEX_LANDSCAPE] = a.getInt( @@ -790,15 +847,22 @@ public class InvariantDeviceProfile { R.styleable.GridDisplayOption_hotseatColumnSpanTwoPanelPortrait, numColumns); + 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); @@ -810,6 +874,19 @@ public class InvariantDeviceProfile { && ((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)); @@ -818,26 +895,18 @@ public class InvariantDeviceProfile { @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]; private final float[] iconSizes = new float[COUNT_SIZES]; private final float[] textSizes = new float[COUNT_SIZES]; @@ -857,19 +926,6 @@ public class InvariantDeviceProfile { 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; @@ -929,8 +985,6 @@ public class InvariantDeviceProfile { 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, @@ -1009,7 +1063,9 @@ public class InvariantDeviceProfile { 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]); @@ -1051,17 +1107,33 @@ public class InvariantDeviceProfile { 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(), + R.dimen.hotseat_bar_bottom_space_default)); + hotseatBarBottomSpace[INDEX_LANDSCAPE] = a.getFloat( + R.styleable.ProfileDisplayOption_hotseatBarBottomSpaceLandscape, + hotseatBarBottomSpace[INDEX_DEFAULT]); + hotseatBarBottomSpace[INDEX_TWO_PANEL_LANDSCAPE] = a.getFloat( + R.styleable.ProfileDisplayOption_hotseatBarBottomSpaceTwoPanelLandscape, + hotseatBarBottomSpace[INDEX_DEFAULT]); + hotseatBarBottomSpace[INDEX_TWO_PANEL_PORTRAIT] = a.getFloat( + R.styleable.ProfileDisplayOption_hotseatBarBottomSpaceTwoPanelPortrait, + hotseatBarBottomSpace[INDEX_DEFAULT]); + + hotseatQsbSpace[INDEX_DEFAULT] = a.getFloat( + R.styleable.ProfileDisplayOption_hotseatQsbSpace, + ResourcesCompat.getFloat(context.getResources(), + R.dimen.hotseat_qsb_space_default)); + hotseatQsbSpace[INDEX_LANDSCAPE] = a.getFloat( + R.styleable.ProfileDisplayOption_hotseatQsbSpaceLandscape, + hotseatQsbSpace[INDEX_DEFAULT]); + hotseatQsbSpace[INDEX_TWO_PANEL_LANDSCAPE] = a.getFloat( + R.styleable.ProfileDisplayOption_hotseatQsbSpaceTwoPanelLandscape, + hotseatQsbSpace[INDEX_DEFAULT]); + hotseatQsbSpace[INDEX_TWO_PANEL_PORTRAIT] = a.getFloat( + R.styleable.ProfileDisplayOption_hotseatQsbSpaceTwoPanelPortrait, + hotseatQsbSpace[INDEX_DEFAULT]); a.recycle(); } @@ -1084,7 +1156,6 @@ public class InvariantDeviceProfile { allAppsIconSizes[i] = 0; allAppsIconTextSizes[i] = 0; allAppsBorderSpaces[i] = new PointF(); - inlineQsb[i] = false; } } @@ -1097,7 +1168,8 @@ public class InvariantDeviceProfile { minCellSize[i].x *= w; minCellSize[i].y *= w; horizontalMargin[i] *= w; - hotseatBorderSpaces[i] *= w; + hotseatBarBottomSpace[i] *= w; + hotseatQsbSpace[i] *= w; allAppsCellSize[i].x *= w; allAppsCellSize[i].y *= w; allAppsIconSizes[i] *= w; @@ -1106,8 +1178,6 @@ public class InvariantDeviceProfile { allAppsBorderSpaces[i].y *= w; } - folderBorderSpace *= w; - return this; } @@ -1120,18 +1190,16 @@ public class InvariantDeviceProfile { 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; allAppsCellSize[i].y += p.allAppsCellSize[i].y; allAppsIconSizes[i] += p.allAppsIconSizes[i]; 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 ebed31bd54..43772e4279 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -18,8 +18,6 @@ package com.android.launcher3; 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; @@ -30,10 +28,12 @@ import static com.android.launcher3.AbstractFloatingView.TYPE_ICON_SURFACE; import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE; import static com.android.launcher3.AbstractFloatingView.TYPE_SNACKBAR; import static com.android.launcher3.AbstractFloatingView.getTopOpenViewWithType; +import static com.android.launcher3.LauncherAnimUtils.HOTSEAT_SCALE_PROPERTY_FACTORY; +import static com.android.launcher3.LauncherAnimUtils.SCALE_INDEX_WIDGET_TRANSITION; import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY; +import static com.android.launcher3.LauncherAnimUtils.WORKSPACE_SCALE_PROPERTY_FACTORY; import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; import static com.android.launcher3.LauncherState.ALL_APPS; -import static com.android.launcher3.LauncherState.FLAG_CLOSE_POPUPS; import static com.android.launcher3.LauncherState.FLAG_MULTI_PAGE; import static com.android.launcher3.LauncherState.FLAG_NON_INTERACTIVE; import static com.android.launcher3.LauncherState.NORMAL; @@ -42,6 +42,8 @@ import static com.android.launcher3.LauncherState.NO_SCALE; 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; @@ -50,6 +52,8 @@ import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCH 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; @@ -58,7 +62,6 @@ import static com.android.launcher3.popup.SystemShortcut.INSTALL; 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.TestProtocol.BAD_STATE; import static com.android.launcher3.util.ItemInfoMatcher.forFolderMatch; import android.animation.Animator; @@ -97,6 +100,8 @@ import android.os.Trace; 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; import android.view.KeyEvent; @@ -128,6 +133,7 @@ import com.android.launcher3.allapps.AllAppsRecyclerView; import com.android.launcher3.allapps.AllAppsStore; import com.android.launcher3.allapps.AllAppsTransitionController; import com.android.launcher3.allapps.BaseAllAppsContainerView; +import com.android.launcher3.allapps.BaseSearchConfig; import com.android.launcher3.allapps.DiscoveryBounce; import com.android.launcher3.anim.PropertyListBuilder; import com.android.launcher3.compat.AccessibilityManagerCompat; @@ -138,6 +144,7 @@ import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.dragndrop.DragOptions; import com.android.launcher3.dragndrop.DragView; import com.android.launcher3.dragndrop.LauncherDragController; +import com.android.launcher3.folder.Folder; import com.android.launcher3.folder.FolderGridOrganizer; import com.android.launcher3.folder.FolderIcon; import com.android.launcher3.icons.BitmapRenderer; @@ -162,6 +169,7 @@ import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.LauncherAppWidgetInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.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; @@ -174,9 +182,8 @@ import com.android.launcher3.statemanager.StateManager.StateHandler; import com.android.launcher3.statemanager.StatefulActivity; import com.android.launcher3.states.RotationHelper; import com.android.launcher3.testing.TestLogging; -import com.android.launcher3.testing.TestProtocol; +import com.android.launcher3.testing.shared.TestProtocol; import com.android.launcher3.touch.AllAppsSwipeController; -import com.android.launcher3.touch.ItemClickHandler; import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper; import com.android.launcher3.util.ActivityResultInfo; import com.android.launcher3.util.ActivityTracker; @@ -194,16 +201,15 @@ 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.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; @@ -217,7 +223,6 @@ import com.android.systemui.plugins.PluginListener; 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; @@ -237,7 +242,7 @@ import java.util.stream.Stream; */ 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<>(); @@ -266,7 +271,7 @@ public class Launcher extends StatefulActivity<LauncherState> protected static final int REQUEST_LAST = 100; // Type: int - private static final String RUNTIME_STATE = "launcher.state"; + protected static final String RUNTIME_STATE = "launcher.state"; // Type: PendingRequestArgs private static final String RUNTIME_STATE_PENDING_REQUEST_ARGS = "launcher.request_args"; // Type: int @@ -278,6 +283,9 @@ public class Launcher extends StatefulActivity<LauncherState> // Type int[] private static final String RUNTIME_STATE_CURRENT_SCREEN_IDS = "launcher.current_screen_ids"; + // Type PendingSplitSelectInfo<Parcelable> + protected static final String PENDING_SPLIT_SELECT_INFO = "launcher.pending_split_select_info"; + public static final String ON_CREATE_EVT = "Launcher.onCreate"; public static final String ON_START_EVT = "Launcher.onStart"; public static final String ON_RESUME_EVT = "Launcher.onResume"; @@ -299,16 +307,24 @@ public class Launcher extends StatefulActivity<LauncherState> public static final int DISPLAY_WORKSPACE_TRACE_COOKIE = 0; public static final int DISPLAY_ALL_APPS_TRACE_COOKIE = 1; - private Configuration mOldConfig; + private static final FloatProperty<Workspace<?>> WORKSPACE_WIDGET_SCALE = + WORKSPACE_SCALE_PROPERTY_FACTORY.get(SCALE_INDEX_WIDGET_TRANSITION); + private static final FloatProperty<Hotseat> HOTSEAT_WIDGET_SCALE = + HOTSEAT_SCALE_PROPERTY_FACTORY.get(SCALE_INDEX_WIDGET_TRANSITION); + + 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; @Thunk DragLayer mDragLayer; - private DragController mDragController; private WidgetManagerHelper mAppWidgetManager; - private LauncherAppWidgetHost mAppWidgetHost; + private LauncherWidgetHolder mAppWidgetHolder; private final int[] mTmpAddItemCellCoordinates = new int[2]; @@ -369,6 +385,7 @@ public class Launcher extends StatefulActivity<LauncherState> private RotationHelper mRotationHelper; protected LauncherOverlayManager mOverlayManager; + protected DragController mDragController; // If true, overlay callbacks are deferred private boolean mDeferOverlayCallbacks; private final Runnable mDeferredOverlayCallbacks = this::checkIfOverlayStillDeferred; @@ -386,6 +403,7 @@ public class Launcher extends StatefulActivity<LauncherState> private LauncherState mPrevLauncherState; private StringCache mStringCache; + private BaseSearchConfig mBaseSearchConfig; @Override @TargetApi(Build.VERSION_CODES.S) @@ -454,28 +472,29 @@ public class Launcher extends StatefulActivity<LauncherState> 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(); - mDragController = new LauncherDragController(this); + initDragController(); mAllAppsController = new AllAppsTransitionController(this); mStateManager = new StateManager<>(this, NORMAL); mOnboardingPrefs = createOnboardingPrefs(mSharedPrefs); + // TODO: move the SearchConfig to SearchState when new LauncherState is created. + mBaseSearchConfig = new BaseSearchConfig(); + mAppWidgetManager = new WidgetManagerHelper(this); - mAppWidgetHost = createAppWidgetHost(); - mAppWidgetHost.startListening(); + mAppWidgetHolder = createAppWidgetHolder(); + mAppWidgetHolder.startListening(); - inflateRootView(R.layout.launcher); setupViews(); crossFadeWithPreviousAppearance(); mPopupDataProvider = new PopupDataProvider(this::updateNotificationDots); @@ -500,7 +519,6 @@ public class Launcher extends StatefulActivity<LauncherState> 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; @@ -603,20 +621,24 @@ public class Launcher extends StatefulActivity<LauncherState> 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. + */ + protected void initDragController() { + mDragController = new LauncherDragController(this); } @Override public void onIdpChanged(boolean modelPropertiesChanged) { - initDeviceProfile(mDeviceProfile.inv); + onHandleConfigurationChanged(); + } + + @Override + protected void onHandleConfigurationChanged() { + if (!initDeviceProfile(mDeviceProfile.inv)) { + return; + } + dispatchDeviceProfileChanged(); reapplyUi(); mDragLayer.recreateControllers(); @@ -632,16 +654,16 @@ public class Launcher extends StatefulActivity<LauncherState> } /** - * 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()); @@ -649,6 +671,7 @@ public class Launcher extends StatefulActivity<LauncherState> onDeviceProfileInitiated(); mModelWriter = mModel.getWriter(getDeviceProfile().isVerticalBarLayout(), true, this); + return true; } public RotationHelper getRotationHelper() { @@ -671,17 +694,9 @@ public class Launcher extends StatefulActivity<LauncherState> */ @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; @@ -740,7 +755,7 @@ public class Launcher extends StatefulActivity<LauncherState> completeAddAppWidget(appWidgetId, info, null, null); break; case REQUEST_RECONFIGURE_APPWIDGET: - mStatsLogManager.logger().withItemInfo(info).log(LAUNCHER_WIDGET_RECONFIGURED); + getStatsLogManager().logger().withItemInfo(info).log(LAUNCHER_WIDGET_RECONFIGURED); completeRestoreAppWidget(appWidgetId, LauncherAppWidgetInfo.RESTORE_COMPLETED); break; case REQUEST_BIND_PENDING_APPWIDGET: { @@ -939,7 +954,7 @@ public class Launcher extends StatefulActivity<LauncherState> 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() { @@ -950,7 +965,7 @@ public class Launcher extends StatefulActivity<LauncherState> } }; } else if (resultCode == RESULT_CANCELED) { - mAppWidgetHost.deleteAppWidgetId(appWidgetId); + mAppWidgetHolder.deleteAppWidgetId(appWidgetId); animationType = Workspace.CANCEL_TWO_STAGE_WIDGET_DROP_ANIMATION; } if (mDragLayer.getAnimatedView() != null) { @@ -973,7 +988,7 @@ public class Launcher extends StatefulActivity<LauncherState> } hideKeyboard(); logStopAndResume(false /* isResume */); - mAppWidgetHost.setActivityStarted(false); + mAppWidgetHolder.setActivityStarted(false); NotificationListener.removeNotificationsChangedListener(getPopupDataProvider()); } @@ -986,7 +1001,7 @@ public class Launcher extends StatefulActivity<LauncherState> mOverlayManager.onActivityStarted(this); } - mAppWidgetHost.setActivityStarted(true); + mAppWidgetHolder.setActivityStarted(true); TraceHelper.INSTANCE.endSection(traceToken); } @@ -1006,7 +1021,7 @@ public class Launcher extends StatefulActivity<LauncherState> NotificationListener.addNotificationsChangedListener(mPopupDataProvider); DiscoveryBounce.showForHomeIfNeeded(this); - mAppWidgetHost.setActivityResumed(true); + mAppWidgetHolder.setActivityResumed(true); } private void logStopAndResume(boolean isResume) { @@ -1080,10 +1095,6 @@ public class Launcher extends StatefulActivity<LauncherState> } addActivityFlags(ACTIVITY_STATE_TRANSITION_ACTIVE); - if (state.hasFlag(FLAG_CLOSE_POPUPS)) { - AbstractFloatingView.closeAllOpenViews(this, !state.hasFlag(FLAG_NON_INTERACTIVE)); - } - if (state == SPRING_LOADED) { // Prevent any Un/InstallShortcutReceivers from updating the db while we are // not on homescreen @@ -1110,6 +1121,7 @@ public class Launcher extends StatefulActivity<LauncherState> .log(getAllAppsEntryEvent().get()); } } + updateDisallowBack(); } /** @@ -1124,7 +1136,7 @@ public class Launcher extends StatefulActivity<LauncherState> @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(); @@ -1171,7 +1183,6 @@ public class Launcher extends StatefulActivity<LauncherState> mOverlayManager.onActivityResumed(this); } - AbstractFloatingView.closeAllOpenViewsExcept(this, false, TYPE_REBIND_SAFE); DragView.removeAllViews(this); TraceHelper.INSTANCE.endSection(traceToken); } @@ -1189,19 +1200,7 @@ public class Launcher extends StatefulActivity<LauncherState> 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); } /** @@ -1245,6 +1244,7 @@ public class Launcher extends StatefulActivity<LauncherState> * 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); @@ -1277,6 +1277,15 @@ public class Launcher extends StatefulActivity<LauncherState> mAllAppsController.setupViews(mScrimView, mAppsView); } + @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); + } + /** * Creates a view representing a shortcut. * @@ -1300,7 +1309,7 @@ public class Launcher extends StatefulActivity<LauncherState> 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; } @@ -1403,7 +1412,7 @@ public class Launcher extends StatefulActivity<LauncherState> if (hostView == null) { // Perform actual inflation because we're live - hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo); + hostView = mAppWidgetHolder.createView(this, appWidgetId, appWidgetInfo); } LauncherAppWidgetInfo launcherInfo; @@ -1534,13 +1543,13 @@ public class Launcher extends StatefulActivity<LauncherState> 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() { @@ -1558,13 +1567,17 @@ public class Launcher extends StatefulActivity<LauncherState> @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) { @@ -1582,7 +1595,6 @@ public class Launcher extends StatefulActivity<LauncherState> && AbstractFloatingView.getTopOpenView(this) == null; boolean isActionMain = Intent.ACTION_MAIN.equals(intent.getAction()); boolean internalStateHandled = ACTIVITY_TRACKER.handleNewIntent(this); - hideKeyboard(); if (isActionMain) { if (!internalStateHandled) { @@ -1640,16 +1652,6 @@ public class Launcher extends StatefulActivity<LauncherState> } } - /** - * 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); @@ -1679,6 +1681,10 @@ public class Launcher extends StatefulActivity<LauncherState> 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) { @@ -1707,10 +1713,11 @@ public class Launcher extends StatefulActivity<LauncherState> 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(); @@ -1882,7 +1889,7 @@ public class Launcher extends StatefulActivity<LauncherState> appWidgetId = CustomWidgetManager.INSTANCE.get(this).getWidgetIdForCustomProvider( info.componentName); } else { - appWidgetId = getAppWidgetHost().allocateAppWidgetId(); + appWidgetId = getAppWidgetHolder().allocateAppWidgetId(); } Bundle options = info.bindOptions; @@ -1996,7 +2003,7 @@ public class Launcher extends StatefulActivity<LauncherState> final LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo) itemInfo; mWorkspace.removeWorkspaceItem(v); if (deleteFromDb) { - getModelWriter().deleteWidgetInfo(widgetInfo, getAppWidgetHost(), reason); + getModelWriter().deleteWidgetInfo(widgetInfo, getAppWidgetHolder(), reason); } } else { return false; @@ -2254,7 +2261,7 @@ public class Launcher extends StatefulActivity<LauncherState> mWorkspace.clearDropTargets(); mWorkspace.removeAllWorkspaceScreens(); - mAppWidgetHost.clearViews(); + mAppWidgetHolder.clearViews(); if (mHotseat != null) { mHotseat.resetLayout(getDeviceProfile().isVerticalBarLayout()); @@ -2561,7 +2568,7 @@ public class Launcher extends StatefulActivity<LauncherState> 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 @@ -2623,18 +2630,18 @@ public class Launcher extends StatefulActivity<LauncherState> // 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); } @@ -2738,6 +2745,8 @@ public class Launcher extends StatefulActivity<LauncherState> getViewCache().setCacheSize(R.layout.folder_page, 2); TraceHelper.INSTANCE.endSection(traceToken); + + mWorkspace.removeExtraEmptyScreen(true); } private boolean canAnimatePageChange() { @@ -2776,7 +2785,7 @@ public class Launcher extends StatefulActivity<LauncherState> 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()); @@ -2787,16 +2796,30 @@ public class Launcher extends StatefulActivity<LauncherState> } 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)); } /** @@ -2849,7 +2872,16 @@ public class Launcher extends StatefulActivity<LauncherState> /** * 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 @@ -2990,13 +3022,14 @@ public class Launcher extends StatefulActivity<LauncherState> writer.println(prefix + "\tmPendingRequestArgs=" + mPendingRequestArgs + " mPendingActivityResult=" + mPendingActivityResult); writer.println(prefix + "\tmRotationHelper: " + mRotationHelper); - writer.println(prefix + "\tmAppWidgetHost.isListening: " + mAppWidgetHost.isListening()); + 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); @@ -3120,7 +3153,22 @@ public class Launcher extends StatefulActivity<LauncherState> 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() { @@ -3220,7 +3268,10 @@ public class Launcher extends StatefulActivity<LauncherState> * @param progress Transition progress from 0 to 1; where 0 => home and 1 => widgets. */ public void onWidgetsTransition(float progress) { - // No-Op + float scale = Utilities.mapToRange(progress, 0f, 1f, 1f, + mDeviceProfile.bottomSheetWorkspaceScale, EMPHASIZED); + WORKSPACE_WIDGET_SCALE.set(getWorkspace(), scale); + HOTSEAT_WIDGET_SCALE.set(getHotseat(), scale); } private static class NonConfigInstance { diff --git a/src/com/android/launcher3/LauncherAnimUtils.java b/src/com/android/launcher3/LauncherAnimUtils.java index 808bf96f9f..4e80d41402 100644 --- a/src/com/android/launcher3/LauncherAnimUtils.java +++ b/src/com/android/launcher3/LauncherAnimUtils.java @@ -26,6 +26,7 @@ import android.util.FloatProperty; import android.util.IntProperty; import android.view.View; import android.view.ViewGroup.LayoutParams; +import android.widget.TextView; import com.android.launcher3.util.MultiScalePropertyFactory; @@ -80,9 +81,9 @@ public class LauncherAnimUtils { new MultiScalePropertyFactory<Hotseat>("hotseat_scale_property"); public static final int SCALE_INDEX_UNFOLD_ANIMATION = 1; - public static final int SCALE_INDEX_UNLOCK_ANIMATION = 2; - public static final int SCALE_INDEX_WORKSPACE_STATE = 3; - public static final int SCALE_INDEX_REVEAL_ANIM = 4; + public static final int SCALE_INDEX_WORKSPACE_STATE = 2; + public static final int SCALE_INDEX_REVEAL_ANIM = 3; + public static final int SCALE_INDEX_WIDGET_TRANSITION = 4; /** Increase the duration if we prevented the fling, as we are going against a high velocity. */ public static int blockedFlingDurationFactor(float velocity) { @@ -115,6 +116,32 @@ public class LauncherAnimUtils { } }; + public static final IntProperty<TextView> TEXT_COLOR = + new IntProperty<TextView>("textColor") { + @Override + public Integer get(TextView view) { + return view.getTextColors().getDefaultColor(); + } + + @Override + public void setValue(TextView view, int color) { + view.setTextColor(color); + } + }; + + public static final IntProperty<TextView> HINT_TEXT_COLOR = + new IntProperty<TextView>("hintTextColor") { + @Override + public Integer get(TextView view) { + return view.getHintTextColors().getDefaultColor(); + } + + @Override + public void setValue(TextView view, int color) { + view.setHintTextColor(color); + } + }; + public static final FloatProperty<View> VIEW_TRANSLATE_X = View.TRANSLATION_X instanceof FloatProperty ? (FloatProperty) View.TRANSLATION_X : new FloatProperty<View>("translateX") { @@ -191,4 +218,32 @@ public class LauncherAnimUtils { } }; } + + /** + * 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 597bc8da3a..49659364b9 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -18,8 +18,7 @@ package com.android.launcher3; 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.getDevicePrefs; import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; import static com.android.launcher3.util.SettingsCache.NOTIFICATION_BADGING_URI; @@ -118,12 +117,10 @@ public class LauncherAppState implements SafeCloseable { observer, MODEL_EXECUTOR.getHandler()); mOnTerminateCallback.add(iconChangeTracker::close); MODEL_EXECUTOR.execute(observer::verifyIconChanged); - if (ENABLE_THEMED_ICONS.get()) { - SharedPreferences prefs = Utilities.getPrefs(mContext); - prefs.registerOnSharedPreferenceChangeListener(observer); - mOnTerminateCallback.add( - () -> prefs.unregisterOnSharedPreferenceChangeListener(observer)); - } + SharedPreferences prefs = LauncherPrefs.getPrefs(mContext); + prefs.registerOnSharedPreferenceChangeListener(observer); + mOnTerminateCallback.add( + () -> prefs.unregisterOnSharedPreferenceChangeListener(observer)); InstallSessionTracker installSessionTracker = InstallSessionHelper.INSTANCE.get(context).registerInstallTracker(mModel); diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index de0d3002e7..20df89763b 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -33,6 +33,7 @@ import android.text.TextUtils; import android.util.Log; import android.util.Pair; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.WorkerThread; @@ -63,7 +64,7 @@ import com.android.launcher3.pm.InstallSessionTracker; import com.android.launcher3.pm.PackageInstallInfo; import com.android.launcher3.pm.UserCache; import com.android.launcher3.shortcuts.ShortcutRequest; -import com.android.launcher3.testing.TestProtocol; +import com.android.launcher3.testing.shared.TestProtocol; import com.android.launcher3.util.IntSet; import com.android.launcher3.util.ItemInfoMatcher; import com.android.launcher3.util.PackageUserKey; @@ -89,9 +90,11 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi 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 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi } } + @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 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi } }; - 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 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi /** * 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 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi } @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 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi } @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 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi * 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 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi 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 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi /** * 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 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi * 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 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi /** * 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 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi return startLoader(new Callbacks[0]); } - private boolean startLoader(Callbacks[] newCallbacks) { + private boolean startLoader(@NonNull final Callbacks[] newCallbacks) { // Enable queue before starting loader. It will get disabled in Launcher#finishBindingItems ItemInstallQueue.INSTANCE.get(mApp.getContext()) .pauseModelPush(ItemInstallQueue.FLAG_LOADER_RUNNING); @@ -433,7 +451,7 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi * 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 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi } @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 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi } @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 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi } @Override - public void onPackageStateChanged(PackageInstallInfo installInfo) { + public void onPackageStateChanged(@NonNull final PackageInstallInfo installInfo) { enqueueModelUpdateTask(new PackageInstallStateChangedTask(installInfo)); } @@ -494,7 +512,8 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi * 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 LauncherModel extends LauncherApps.Callback implements InstallSessi 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 class LauncherModel extends LauncherApps.Callback implements InstallSessi } } - public LoaderTransaction beginLoader(LoaderTask task) throws CancellationException { + public LoaderTransaction beginLoader(@NonNull final LoaderTask task) + throws CancellationException { return new LoaderTransaction(task); } @@ -554,7 +575,8 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi /** * 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 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi /** * 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 class LauncherModel extends LauncherApps.Callback implements InstallSessi */ public interface CallbackTask { - void execute(Callbacks callbacks); + void execute(@NonNull Callbacks callbacks); } /** @@ -599,12 +623,14 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi /** * 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 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi /** * 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 class LauncherModel extends LauncherApps.Callback implements InstallSessi 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 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi /** * 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 0000000000..23ff10ad41 --- /dev/null +++ b/src/com/android/launcher3/LauncherPrefs.kt @@ -0,0 +1,20 @@ +package com.android.launcher3 + +import android.content.Context +import android.content.SharedPreferences + +object LauncherPrefs { + + @JvmStatic + fun getPrefs(context: Context): SharedPreferences { + // Use application context for shared preferences, so that we use a single cached instance + return context.applicationContext.getSharedPreferences( + LauncherFiles.SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE) + } + + @JvmStatic + fun getDevicePrefs(context: Context): SharedPreferences { + // Use application context for shared preferences, so that we use a single cached instance + return context.applicationContext.getSharedPreferences( + LauncherFiles.DEVICE_PREFERENCES_KEY, Context.MODE_PRIVATE) + }}
\ No newline at end of file diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index 1abb61b1fc..b1dd8a66fd 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.Intent; 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.text.TextUtils; 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.IntArray; 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.Arrays; 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 class LauncherProvider extends ContentProvider { 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 @@ public class LauncherProvider extends ContentProvider { private long mLastRestoreTimestamp = 0L; - private boolean mUseTestWorkspaceLayout; + private int mDefaultWorkspaceLayoutOverride = 0; /** * $ adb shell dumpsys activity provider com.android.launcher3 @@ -162,7 +166,7 @@ public class LauncherProvider extends ContentProvider { private synchronized boolean prepForMigration(String dbFile, String targetTableName, Supplier<DatabaseHelper> src, Supplier<DatabaseHelper> dst) { if (TextUtils.equals(dbFile, mOpenHelper.getDatabaseName())) { - Log.e("b/198965093", "prepForMigration - target db is same as current: " + dbFile); + Log.e(TAG, "prepForMigration - target db is same as current: " + dbFile); return false; } @@ -254,17 +258,20 @@ public class LauncherProvider extends ContentProvider { 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 @@ public class LauncherProvider extends ContentProvider { 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 @@ public class LauncherProvider extends ContentProvider { 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 @@ public class LauncherProvider extends ContentProvider { } private void clearFlagEmptyDbCreated() { - Utilities.getPrefs(getContext()).edit() + LauncherPrefs.getPrefs(getContext()).edit() .remove(mOpenHelper.getKey(EMPTY_DATABASE_CREATED)).commit(); } @@ -527,32 +544,30 @@ public class LauncherProvider extends ContentProvider { * 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 @@ public class LauncherProvider extends ContentProvider { // 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 @@ public class LauncherProvider extends ContentProvider { * * @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 @@ public class LauncherProvider extends ContentProvider { 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 @@ public class LauncherProvider extends ContentProvider { .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 @@ public class LauncherProvider extends ContentProvider { */ 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(); } @@ -918,28 +935,46 @@ public class LauncherProvider extends ContentProvider { */ 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(); } } @@ -1040,8 +1075,12 @@ public class LauncherProvider extends ContentProvider { 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 a5c5c02735..1592154c3a 100644 --- a/src/com/android/launcher3/LauncherRootView.java +++ b/src/com/android/launcher3/LauncherRootView.java @@ -55,6 +55,8 @@ public class LauncherRootView extends InsettableFrameLayout { @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 66195f3a1d..6ea331d485 100644 --- a/src/com/android/launcher3/LauncherSettings.java +++ b/src/com/android/launcher3/LauncherSettings.java @@ -94,15 +94,11 @@ public class LauncherSettings { */ 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 class LauncherSettings { 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 class LauncherSettings { 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 @@ public class LauncherSettings { 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 class LauncherSettings { 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 ea6a9199d7..5dddc6f458 100644 --- a/src/com/android/launcher3/LauncherState.java +++ b/src/com/android/launcher3/LauncherState.java @@ -19,16 +19,16 @@ import static com.android.launcher3.anim.Interpolators.ACCEL_2; import static com.android.launcher3.anim.Interpolators.DEACCEL_2; import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME; import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW; -import static com.android.launcher3.testing.TestProtocol.ALL_APPS_STATE_ORDINAL; -import static com.android.launcher3.testing.TestProtocol.BACKGROUND_APP_STATE_ORDINAL; -import static com.android.launcher3.testing.TestProtocol.HINT_STATE_ORDINAL; -import static com.android.launcher3.testing.TestProtocol.HINT_STATE_TWO_BUTTON_ORDINAL; -import static com.android.launcher3.testing.TestProtocol.NORMAL_STATE_ORDINAL; -import static com.android.launcher3.testing.TestProtocol.OVERVIEW_MODAL_TASK_STATE_ORDINAL; -import static com.android.launcher3.testing.TestProtocol.OVERVIEW_SPLIT_SELECT_ORDINAL; -import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL; -import static com.android.launcher3.testing.TestProtocol.QUICK_SWITCH_STATE_ORDINAL; -import static com.android.launcher3.testing.TestProtocol.SPRING_LOADED_STATE_ORDINAL; +import static com.android.launcher3.testing.shared.TestProtocol.ALL_APPS_STATE_ORDINAL; +import static com.android.launcher3.testing.shared.TestProtocol.BACKGROUND_APP_STATE_ORDINAL; +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.NORMAL_STATE_ORDINAL; +import static com.android.launcher3.testing.shared.TestProtocol.OVERVIEW_MODAL_TASK_STATE_ORDINAL; +import static com.android.launcher3.testing.shared.TestProtocol.OVERVIEW_SPLIT_SELECT_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.testing.shared.TestProtocol.SPRING_LOADED_STATE_ORDINAL; import android.content.Context; import android.graphics.Color; @@ -38,9 +38,10 @@ import com.android.launcher3.statemanager.BaseState; import com.android.launcher3.statemanager.StateManager; import com.android.launcher3.states.HintState; import com.android.launcher3.states.SpringLoadedState; -import com.android.launcher3.testing.TestProtocol; +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 +72,14 @@ public abstract class LauncherState implements BaseState<LauncherState> { 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 +108,7 @@ public abstract class LauncherState implements BaseState<LauncherState> { */ 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. @@ -208,14 +205,18 @@ public abstract class LauncherState implements BaseState<LauncherState> { return (getVisibleElements(launcher) & elements) == elements; } - /** Returns whether taskbar is stashed and thus should replace hotseat with a handle */ + /** + * Returns whether taskbar is stashed and thus should either: + * 1) replace hotseat or taskbar icons with a handle in gesture navigation mode or + * 2) fade out the hotseat or taskbar icons in 3-button navigation mode. + */ public boolean isTaskbarStashed(Launcher launcher) { return false; } /** Returns whether taskbar is aligned with the hotseat vs position inside apps */ public boolean isTaskbarAlignedWithHotseat(Launcher launcher) { - return !isTaskbarStashed(launcher); + return true; } /** @@ -261,7 +262,8 @@ public abstract class LauncherState implements BaseState<LauncherState> { * * 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); } @@ -271,14 +273,16 @@ public abstract class LauncherState implements BaseState<LauncherState> { * * @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; } diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index cba0b7d709..4f5cc4a980 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -259,8 +259,13 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou 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 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou 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 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou int finalPos = mOrientationHandler.getPrimaryValue(mScroller.getFinalX(), mScroller.getFinalY()); if (newPos == finalPos && mEdgeGlowLeft.isFinished() && mEdgeGlowRight.isFinished()) { - mScroller.abortAnimation(); + abortScrollerAnimation(false); } invalidate(); @@ -694,7 +699,7 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou } /** 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 abstract class PagedView<T extends View & PageIndicator> extends ViewGrou */ 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 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou final int childCount = getChildCount(); int[] pageScrolls = mPageScrolls; boolean pageScrollChanged = false; - if (!pageScrollsInitialized()) { + if (!isPageScrollsInitialized()) { pageScrolls = new int[childCount]; pageScrollChanged = true; } @@ -767,6 +772,13 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou } 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,9 +1199,7 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou } public int getScrollForPage(int index) { - // TODO(b/233112195): Use !pageScrollsInitialized() instead of mPageScrolls == null, once we - // root cause where we should be using runOnPageScrollsInitialized(). - if (mPageScrolls == null || index >= mPageScrolls.length || index < 0) { + if (!isPageScrollsInitialized() || index >= mPageScrolls.length || index < 0) { return 0; } else { return mPageScrolls[index]; @@ -1199,7 +1209,7 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou // 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); diff --git a/src/com/android/launcher3/Partner.java b/src/com/android/launcher3/Partner.java deleted file mode 100644 index 2e27f32226..0000000000 --- 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 be994ee138..b7a22fc9b9 100644 --- a/src/com/android/launcher3/PendingAddItemInfo.java +++ b/src/com/android/launcher3/PendingAddItemInfo.java @@ -18,6 +18,7 @@ package com.android.launcher3; import android.content.ComponentName; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.launcher3.model.data.ItemInfo; @@ -43,6 +44,7 @@ public class PendingAddItemInfo extends ItemInfo { /** * 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 f8bc1f4e6e..791cfff9b0 100644 --- a/src/com/android/launcher3/SecondaryDropTarget.java +++ b/src/com/android/launcher3/SecondaryDropTarget.java @@ -159,7 +159,7 @@ public class SecondaryDropTarget extends ButtonDropTarget implements OnAlarmList return RECONFIGURE; } return INVALID; - } else if (FeatureFlags.ENABLE_PREDICTION_DISMISS.get() && info.isPredictedItem()) { + } else if (info.isPredictedItem()) { return DISMISS_PREDICTION; } @@ -288,7 +288,7 @@ public class SecondaryDropTarget extends ButtonDropTarget implements OnAlarmList 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 b81637f670..50ad2be505 100644 --- a/src/com/android/launcher3/SessionCommitReceiver.java +++ b/src/com/android/launcher3/SessionCommitReceiver.java @@ -31,7 +31,7 @@ import androidx.annotation.WorkerThread; import com.android.launcher3.logging.FileLog; import com.android.launcher3.model.ItemInstallQueue; import com.android.launcher3.pm.InstallSessionHelper; -import com.android.launcher3.testing.TestProtocol; +import com.android.launcher3.testing.shared.TestProtocol; import com.android.launcher3.util.Executors; /** @@ -98,6 +98,6 @@ public class SessionCommitReceiver extends BroadcastReceiver { } 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 5583eaeba9..7a74d7ed1b 100644 --- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java +++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java @@ -25,13 +25,16 @@ import static com.android.launcher3.CellLayout.WORKSPACE; 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,7 +81,7 @@ public class ShortcutAndWidgetContainer extends ViewGroup implements FolderIcon. 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)) { @@ -105,12 +108,13 @@ public class ShortcutAndWidgetContainer extends ViewGroup implements FolderIcon. } 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 class ShortcutAndWidgetContainer extends ViewGroup implements FolderIcon. } 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 @@ public class ShortcutAndWidgetContainer extends ViewGroup implements FolderIcon. // 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 @@ public class ShortcutAndWidgetContainer extends ViewGroup implements FolderIcon. 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 @@ public class ShortcutAndWidgetContainer extends ViewGroup implements FolderIcon. * 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,7 +255,7 @@ public class ShortcutAndWidgetContainer extends ViewGroup implements FolderIcon. @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) { @@ -261,7 +266,7 @@ public class ShortcutAndWidgetContainer extends ViewGroup implements FolderIcon. @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 b50f03aa70..d76e5383cf 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -26,20 +26,12 @@ import android.annotation.TargetApi; 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 +43,6 @@ import android.graphics.RectF; import android.graphics.drawable.AdaptiveIconDrawable; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; -import android.net.Uri; import android.os.Build; import android.os.Build.VERSION_CODES; import android.os.DeadObjectException; @@ -71,25 +62,22 @@ import android.view.MotionEvent; 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 +86,8 @@ import com.android.launcher3.widget.PendingAddShortcutInfo; 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 +101,6 @@ public final class Utilities { 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(); @@ -164,14 +148,6 @@ public final class Utilities { 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(); @@ -183,12 +159,6 @@ public final class Utilities { 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. @@ -302,9 +272,9 @@ public final class Utilities { * 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) { @@ -325,40 +295,23 @@ public final class Utilities { 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]}; + public static void scaleRectFAboutCenter(RectF r, float scale) { + scaleRectFAboutCenter(r, scale, scale); } /** - * Helper method to set rectOut with rectFSrc. + * Similar to {@link #scaleRectAboutCenter(Rect, float)} except this allows different scales + * for X and Y */ - 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 scaleRectFAboutCenter(RectF r, float scaleX, float scaleY) { + float px = r.centerX(); + float py = r.centerY(); + r.offset(-px, -py); + r.left = r.left * scaleX; + r.top = r.top * scaleY; + r.right = r.right * scaleX; + r.bottom = r.bottom * scaleY; + r.offset(px, py); } public static void scaleRectAboutCenter(Rect r, float scale) { @@ -366,27 +319,14 @@ public final class Utilities { 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); + r.offset(cx, cy); } } - 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) { @@ -402,21 +342,6 @@ public final class Utilities { } /** - * Similar to {@link #scaleRectAboutCenter(Rect, float)} except this allows different scales - * for X and Y - */ - public static void scaleRectFAboutCenter(RectF r, float scaleX, float scaleY) { - float px = r.centerX(); - float py = r.centerY(); - r.offset(-px, -py); - r.left = r.left * scaleX; - r.top = r.top * scaleY; - r.right = r.right * scaleX; - r.bottom = r.bottom * scaleY; - r.offset(px, py); - } - - /** * Maps t from one range to another range. * @param t The value to map. * @param fromMin The lower bound of the range that t is being mapped from. @@ -451,30 +376,6 @@ public final class Utilities { } /** - * 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. */ @@ -518,14 +419,19 @@ public final class Utilities { 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) { @@ -551,18 +457,6 @@ public final class Utilities { } /** - * 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. @@ -586,15 +480,6 @@ public final class Utilities { } /** - * Returns an intent for starting the default home activity - */ - public static Intent createHomeIntent() { - return new Intent(Intent.ACTION_MAIN) - .addCategory(Intent.CATEGORY_HOME) - .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - } - - /** * Wraps a message with a TTS span, so that a different message is spoken than * what is getting displayed. * @param msg original message @@ -621,18 +506,6 @@ public final class Utilities { 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(); } @@ -646,42 +519,6 @@ public final class Utilities { || 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. */ @@ -691,12 +528,6 @@ public final class Utilities { 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. * @@ -758,8 +589,8 @@ public final class Utilities { 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; } @@ -794,14 +625,6 @@ public final class Utilities { } } - /** - * @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; } @@ -812,28 +635,6 @@ public final class Utilities { } /** - * 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. @@ -881,16 +682,6 @@ public final class Utilities { 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); @@ -931,4 +722,14 @@ public final class Utilities { } return options; } + + /** 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 b574b6e200..4640b19a24 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.HINT_STATE; 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.TestProtocol.BAD_STATE; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -45,6 +43,7 @@ import android.content.Context; 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,12 +52,13 @@ import android.os.Parcelable; import android.util.AttributeSet; import android.util.Log; import android.util.SparseArray; +import android.view.Gravity; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; -import android.view.ViewTreeObserver; import android.view.accessibility.AccessibilityNodeInfo; +import android.widget.FrameLayout; import android.widget.Toast; import androidx.annotation.Nullable; @@ -67,6 +67,7 @@ import com.android.launcher3.accessibility.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 +107,9 @@ import com.android.launcher3.util.PackageUserKey; import com.android.launcher3.util.RunnableList; import com.android.launcher3.util.Thunk; import com.android.launcher3.util.WallpaperOffsetInterpolator; -import com.android.launcher3.widget.LauncherAppWidgetHost; -import com.android.launcher3.widget.LauncherAppWidgetHost.ProviderChangedListener; import com.android.launcher3.widget.LauncherAppWidgetHostView; +import com.android.launcher3.widget.LauncherWidgetHolder; +import com.android.launcher3.widget.LauncherWidgetHolder.ProviderChangedListener; import com.android.launcher3.widget.NavigableAppWidgetHostView; import com.android.launcher3.widget.PendingAddShortcutInfo; import com.android.launcher3.widget.PendingAddWidgetInfo; @@ -117,6 +118,7 @@ import com.android.launcher3.widget.WidgetManagerHelper; import com.android.launcher3.widget.dragndrop.AppWidgetHostViewDragListener; import com.android.launcher3.widget.util.WidgetSizes; import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlay; +import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlayCallbacks; import java.util.ArrayList; import java.util.Iterator; @@ -134,7 +136,7 @@ import java.util.stream.Collectors; 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,14 +154,12 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> 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; - private ShortcutAndWidgetContainer mDragSourceInternal; + protected ShortcutAndWidgetContainer mDragSourceInternal; @Thunk final IntSparseArrayMap<CellLayout> mWorkspaceScreens = new IntSparseArrayMap<>(); @Thunk final IntArray mScreenOrder = new IntArray(); @@ -169,7 +169,7 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> /** * CellInfo for the cell that is currently being dragged */ - private CellLayout.CellInfo mDragInfo; + protected CellLayout.CellInfo mDragInfo; /** * Target drop area calculated during last acceptDrop call. @@ -195,7 +195,7 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> @Thunk final Launcher mLauncher; @Thunk DragController mDragController; - private final int[] mTempXY = new int[2]; + protected final int[] mTempXY = new int[2]; private final float[] mTempFXY = new float[2]; private final Rect mTempRect = new Rect(); @Thunk float[] mDragViewVisualCenter = new float[2]; @@ -214,7 +214,7 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> private boolean mUnlockWallpaperFromDefaultPageOnLayout; public static final int REORDER_TIMEOUT = 650; - private final Alarm mReorderAlarm = new Alarm(); + protected final Alarm mReorderAlarm = new Alarm(); private PreviewBackground mFolderCreateBg; private FolderIcon mDragOverFolderIcon = null; private boolean mCreateUserFolderOnDrop = false; @@ -223,8 +223,8 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> // 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; @@ -242,7 +242,7 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> private static final int DRAG_MODE_CREATE_FOLDER = 1; private static final int DRAG_MODE_ADD_TO_FOLDER = 2; private static final int DRAG_MODE_REORDER = 3; - private int mDragMode = DRAG_MODE_NONE; + protected int mDragMode = DRAG_MODE_NONE; @Thunk int mLastReorderX = -1; @Thunk int mLastReorderY = -1; @@ -254,14 +254,12 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> // State related to Launcher Overlay private OverlayEdgeEffect mOverlayEdgeEffect; - boolean mOverlayShown = false; - private Runnable mOnOverlayHiddenCallback; + private boolean mOverlayShown = false; + private float mOverlayProgress; // 1 -> overlay completely visible, 0 -> home visible + private final List<LauncherOverlayCallbacks> mOverlayCallbacks = new ArrayList<>(); private boolean mForceDrawAdjacentPages = false; - // Total over scrollX in the overlay direction. - private float mOverlayTranslation; - // Handles workspace state transitions private final WorkspaceStateTransitionAnimation mStateTransitionAnimation; @@ -290,7 +288,7 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> mLauncher = Launcher.getLauncher(context); mStateTransitionAnimation = new WorkspaceStateTransitionAnimation(mLauncher, this); mWallpaperManager = WallpaperManager.getInstance(context); - + mAllAppsIconSize = mLauncher.getDeviceProfile().allAppsIconSizePx; mWallpaperOffset = new WallpaperOffsetInterpolator(this); setHapticFeedbackEnabled(false); @@ -326,6 +324,26 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> 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 +388,8 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> 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 +572,22 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> // 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 +596,9 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> // 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 +918,10 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> 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 +1073,23 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> 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 +1150,15 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> } 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 +1208,46 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> @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; - - // 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) { + mOverlayShown = true; + mLauncher.onOverlayVisibilityChanged(true); + } + } 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 +1622,14 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> mDragSourceInternal = (ShortcutAndWidgetContainer) child.getParent(); } - if (child instanceof BubbleTextView && !dragOptions.isAccessibleDrag) { - dragOptions.preDragCondition = ((BubbleTextView) child).startLongPressAction(); + if (child instanceof BubbleTextView) { + BubbleTextView btv = (BubbleTextView) child; + if (!dragOptions.isAccessibleDrag) { + dragOptions.preDragCondition = btv.startLongPressAction(); + } + if (btv.isDisplaySearchResult()) { + dragOptions.preDragEndScale = (float) mAllAppsIconSize / btv.getIconSize(); + } } final DragView dv; @@ -1802,7 +1753,7 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> boolean willCreateUserFolder(ItemInfo info, View dropOverView, boolean considerTimeout) { if (dropOverView != null) { - CellLayout.LayoutParams lp = (CellLayout.LayoutParams) dropOverView.getLayoutParams(); + CellLayoutLayoutParams lp = (CellLayoutLayoutParams) dropOverView.getLayoutParams(); if (lp.useTmpCoords && (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.cellY)) { return false; } @@ -1837,7 +1788,7 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> } boolean willAddToExistingUserFolder(ItemInfo dragInfo, View dropOverView) { if (dropOverView != null) { - CellLayout.LayoutParams lp = (CellLayout.LayoutParams) dropOverView.getLayoutParams(); + CellLayoutLayoutParams lp = (CellLayoutLayoutParams) dropOverView.getLayoutParams(); if (lp.useTmpCoords && (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.cellY)) { return false; } @@ -1980,10 +1931,10 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> // If the item being dropped is a shortcut and the nearest drop // cell also contains a shortcut, then create a folder with the two shortcuts. - if (createUserFolderIfNecessary(cell, container, - dropTargetLayout, mTargetCell, distance, false, d) + if (createUserFolderIfNecessary(cell, container, dropTargetLayout, mTargetCell, + distance, false, d) || addToExistingFolderIfNecessary(cell, dropTargetLayout, mTargetCell, - distance, d, false)) { + distance, d, false)) { mLauncher.getStateManager().goToState(NORMAL, SPRING_LOADED_EXIT_DELAY); return; } @@ -2012,8 +1963,8 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> mTargetCell[0] = mTargetCell[1] = -1; } else { mTargetCell = dropTargetLayout.performReorder((int) mDragViewVisualCenter[0], - (int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY, cell, - mTargetCell, resultSpan, CellLayout.MODE_ON_DROP); + (int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY, + cell, mTargetCell, resultSpan, CellLayout.MODE_ON_DROP); } boolean foundCell = mTargetCell[0] >= 0 && mTargetCell[1] >= 0; @@ -2054,7 +2005,7 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> } // update the item's position after drop - CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams(); + CellLayoutLayoutParams lp = (CellLayoutLayoutParams) cell.getLayoutParams(); lp.cellX = lp.tmpCellX = mTargetCell[0]; lp.cellY = lp.tmpCellY = mTargetCell[1]; lp.cellHSpan = item.spanX; @@ -2063,19 +2014,11 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> 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 && !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); @@ -2088,7 +2031,7 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> } // If we can't find a drop location, we return the item to its original position - CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams(); + CellLayoutLayoutParams lp = (CellLayoutLayoutParams) cell.getLayoutParams(); mTargetCell[0] = lp.cellX; mTargetCell[1] = lp.cellY; CellLayout layout = (CellLayout) cell.getParent().getParent(); @@ -2096,8 +2039,16 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> } } 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); + } } } @@ -2123,7 +2074,8 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> 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); @@ -2149,6 +2101,20 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> } } + @Nullable + private Runnable getWidgetResizeFrameRunnable(DragOptions options, + LauncherAppWidgetHostView hostView, CellLayout cellLayout) { + AppWidgetProviderInfo pInfo = hostView.getAppWidgetInfo(); + if (pInfo != null && !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) @@ -2296,7 +2262,7 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> } } - private void cleanupFolderCreation() { + protected void cleanupFolderCreation() { if (mFolderCreateBg != null) { mFolderCreateBg.animateToRest(); } @@ -2309,7 +2275,7 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> } } - private void cleanupReorder(boolean cancelAlarm) { + protected void cleanupReorder(boolean cancelAlarm) { // Any pending reorders are canceled if (cancelAlarm) { mReorderAlarm.cancelAlarm(); @@ -2385,7 +2351,7 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> } 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]; @@ -2401,26 +2367,8 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1], item.spanX, item.spanY, child, mTargetCell); - if (!nearestDropOccupied) { - 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]; - mDragTargetLayout.performReorder((int) mDragViewVisualCenter[0], - (int) mDragViewVisualCenter[1], minSpanX, minSpanY, item.spanX, item.spanY, - child, mTargetCell, resultSpan, 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, - minSpanX, minSpanY, item.spanX, item.spanY, d, child); - mReorderAlarm.setOnAlarmListener(listener); - mReorderAlarm.setAlarm(REORDER_TIMEOUT); - } + manageReorderOnDragOver(d, targetCellDistance, nearestDropOccupied, minSpanX, minSpanY, + reorderX, reorderY); if (mDragMode == DRAG_MODE_CREATE_FOLDER || mDragMode == DRAG_MODE_ADD_TO_FOLDER || !nearestDropOccupied) { @@ -2431,6 +2379,35 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> } } + protected void manageReorderOnDragOver(DragObject d, float targetCellDistance, + boolean nearestDropOccupied, int minSpanX, int minSpanY, int reorderX, int reorderY) { + + ItemInfo item = d.dragInfo; + final View child = (mDragInfo == null) ? null : mDragInfo.cell; + 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) + && (mLastReorderX != reorderX || mLastReorderY != reorderY) + && 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, 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, + minSpanX, minSpanY, item.spanX, item.spanY, d, child); + mReorderAlarm.setOnAlarmListener(listener); + mReorderAlarm.setAlarm(REORDER_TIMEOUT); + } + } /** * Updates {@link #mDragTargetLayout} and {@link #mDragOverlappingLayout} * based on the DragObject's position. @@ -2483,10 +2460,10 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> } 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); } @@ -2627,8 +2604,6 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> 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, @@ -2640,7 +2615,6 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> setDragMode(DRAG_MODE_REORDER); } - boolean resize = resultSpan[0] != spanX || resultSpan[1] != spanY; mDragTargetLayout.visualizeDropLocation(mTargetCell[0], mTargetCell[1], resultSpan[0], resultSpan[1], dragObject); } @@ -2874,7 +2848,8 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> 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; @@ -2996,7 +2971,7 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> */ @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); } @@ -3331,7 +3306,7 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> 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; @@ -3408,7 +3383,7 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> 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 @@ -3417,7 +3392,11 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> return getPageDescription(page); } - private String getPageDescription(int page) { + /** + * @param page page index. + * @return Description of the page at the given page index. + */ + public String getPageDescription(int page) { int nScreens = getChildCount(); int extraScreenId = mScreenOrder.indexOf(EXTRA_EMPTY_SCREEN_ID); if (extraScreenId >= 0 && nScreens > 1) { @@ -3453,19 +3432,19 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> */ 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); @@ -3475,7 +3454,7 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> @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 7e6e1b60de..91e12faa1d 100644 --- a/src/com/android/launcher3/WorkspaceLayoutManager.java +++ b/src/com/android/launcher3/WorkspaceLayoutManager.java @@ -19,6 +19,7 @@ import android.util.Log; import android.view.View; import android.view.ViewGroup; +import com.android.launcher3.celllayout.CellLayoutLayoutParams; import com.android.launcher3.folder.Folder; import com.android.launcher3.folder.FolderIcon; import com.android.launcher3.model.data.ItemInfo; @@ -111,11 +112,11 @@ public interface WorkspaceLayoutManager { } 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 = (CellLayoutLayoutParams) genericLp; lp.cellX = x; lp.cellY = y; lp.cellHSpan = spanX; diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java index a991c2f959..62e7ef308c 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.FLAG_HOTSEAT_INACCESSIBLE; 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 @@ import com.android.systemui.plugins.ResourceProvider; */ 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 @@ public class WorkspaceStateTransitionAnimation { 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 79214e896a..063b82e0d9 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.LauncherSettings; 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 @@ public class LauncherAccessibilityDelegate extends BaseAccessibilityDelegate<Lau 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 @@ public class LauncherAccessibilityDelegate extends BaseAccessibilityDelegate<Lau } private boolean performResizeAction(int action, View host, LauncherAppWidgetInfo info) { - CellLayout.LayoutParams lp = (CellLayout.LayoutParams) host.getLayoutParams(); + CellLayoutLayoutParams lp = (CellLayoutLayoutParams) host.getLayoutParams(); CellLayout layout = (CellLayout) host.getParent().getParent(); layout.markCellsAsUnoccupiedForView(host); diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java index 0f0442d5f0..fd6bdefb60 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 android.widget.RelativeLayout; 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> { + + private static final long DEFAULT_SEARCH_TRANSITION_DURATION_MS = 300; + + // Used to animate Search results out and A-Z apps in, or vice-versa. + private final SearchTransitionController mSearchTransitionController; - protected SearchUiManager mSearchUiManager; - /** - * View that defines the search box. Result is rendered inside the recycler view defined in the - * base class. - */ - private View mSearchContainer; /** {@code true} when rendered view is in search state instead of the scroll state. */ private boolean mIsSearching; + private boolean mRebindAdaptersAfterSearchAnimation; public ActivityAllAppsContainerView(Context context) { this(context, null); @@ -65,6 +62,14 @@ public class ActivityAllAppsContainerView<T extends Context & AppLauncher 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 @@ public class ActivityAllAppsContainerView<T extends Context & AppLauncher 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 @@ public class ActivityAllAppsContainerView<T extends Context & AppLauncher 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 @@ public class ActivityAllAppsContainerView<T extends Context & AppLauncher } @Override - protected boolean shouldShowTabs() { - return super.shouldShowTabs() && !isSearching(); + public boolean isSearching() { + return mIsSearching; } @Override - public boolean isSearching() { - return mIsSearching; + 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 @@ public class ActivityAllAppsContainerView<T extends Context & AppLauncher 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); @@ -239,14 +265,6 @@ public class ActivityAllAppsContainerView<T extends Context & AppLauncher (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; @@ -283,7 +301,7 @@ public class ActivityAllAppsContainerView<T extends Context & AppLauncher layoutParams.topMargin = includeTabsMargin ? getContext().getResources().getDimensionPixelSize( - R.dimen.all_apps_header_pill_height) + R.dimen.all_apps_header_pill_height) : 0; } @@ -304,4 +322,27 @@ public class ActivityAllAppsContainerView<T extends Context & AppLauncher 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 3830a9333d..0000000000 --- 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 33d2f2b1df..63e6d13a5c 100644 --- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java +++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java @@ -26,10 +26,13 @@ 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 +43,62 @@ public class AllAppsGridAdapter<T extends Context & ActivityContext> extends 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 @@ -125,6 +158,23 @@ public class AllAppsGridAdapter<T extends Context & ActivityContext> extends } 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 +203,12 @@ public class AllAppsGridAdapter<T extends Context & ActivityContext> extends @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 af17cf72e9..d308fcb3b8 100644 --- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java +++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java @@ -15,32 +15,28 @@ */ package com.android.launcher3.allapps; -import static android.view.View.MeasureSpec.UNSPECIFIED; - -import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_SCROLLED; +import static com.android.launcher3.logger.LauncherAtom.ContainerInfo; +import static com.android.launcher3.logger.LauncherAtom.SearchResultContainer; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_SCROLLED_DOWN; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_SCROLLED_UNKNOWN_DIRECTION; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_SCROLLED_UP; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_VERTICAL_SWIPE_BEGIN; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_VERTICAL_SWIPE_END; import static com.android.launcher3.util.LogConfig.SEARCH_LOGGING; -import static com.android.launcher3.util.UiThreadHelper.hideKeyboardAsync; import android.content.Context; -import android.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 android.view.View; 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; @@ -55,44 +51,11 @@ public class AllAppsRecyclerView extends FastScrollRecyclerView { 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); @@ -109,9 +72,6 @@ public class AllAppsRecyclerView extends FastScrollRecyclerView { 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); } @@ -133,21 +93,12 @@ public class AllAppsRecyclerView extends FastScrollRecyclerView { 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()); } @@ -159,33 +110,13 @@ public class AllAppsRecyclerView extends FastScrollRecyclerView { } @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 @@ -195,28 +126,24 @@ public class AllAppsRecyclerView extends FastScrollRecyclerView { 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; } /** @@ -249,17 +176,6 @@ public class AllAppsRecyclerView extends FastScrollRecyclerView { } @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; } @@ -280,13 +196,13 @@ public class AllAppsRecyclerView extends FastScrollRecyclerView { 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; @@ -341,104 +257,35 @@ public class AllAppsRecyclerView extends FastScrollRecyclerView { } } - @Override - public int getCurrentScrollY() { - // Return early if there are no items or we haven't been measured - List<AllAppsGridAdapter.AdapterItem> items = mApps.getAdapterItems(); - if (items.isEmpty() || mNumAppsPerRow == 0 || getChildCount() == 0) { - return -1; - } - - // Calculate the y and offset for the item - View child = getChildAt(0); - int position = getChildAdapterPosition(child); - if (position == NO_POSITION) { - return -1; - } - return getPaddingTop() + - getCurrentScrollY(position, getLayoutManager().getDecoratedTop(child)); - } - - public int getCurrentScrollY(int position, int offset) { - 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 - offset; - } - - /** - * Returns the available scroll height: - * AvailableScrollHeight = Total height of the all items - last page height - */ - @Override - protected int getAvailableScrollHeight() { - return getPaddingTop() + getCurrentScrollY(getAdapter().getItemCount(), 0) - - getHeight() + getPaddingBottom(); - } - 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(); + ExtendedEditText editText = context.getAppsView().getSearchUiManager().getEditText(); + ContainerInfo containerInfo = ContainerInfo.newBuilder().setSearchResultContainer( + SearchResultContainer + .newBuilder() + .setQueryLength((editText == null) ? -1 : editText.length())).build(); + + // mCumulativeVerticalScroll == 0 when user comes back to original position, we don't + // know the direction of scrolling. + mgr.logger().withContainerInfo(containerInfo).log( + mCumulativeVerticalScroll == 0 ? LAUNCHER_ALLAPPS_SCROLLED_UNKNOWN_DIRECTION + : (mCumulativeVerticalScroll > 0) ? LAUNCHER_ALLAPPS_SCROLLED_DOWN + : LAUNCHER_ALLAPPS_SCROLLED_UP); + } } diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java index a4a208533a..9930abeb12 100644 --- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -15,6 +15,7 @@ */ package com.android.launcher3.allapps; +import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y; import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.ALL_APPS_CONTENT; import static com.android.launcher3.LauncherState.NORMAL; @@ -23,7 +24,6 @@ import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER; import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE; import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS; -import static com.android.launcher3.util.SystemUiController.UI_STATE_ALLAPPS; import android.animation.Animator; import android.animation.Animator.AnimatorListener; @@ -37,16 +37,14 @@ 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.Utilities; import com.android.launcher3.anim.AnimatorListeners; import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.anim.PropertySetter; -import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.statemanager.StateManager.StateHandler; import com.android.launcher3.states.StateAnimationConfig; -import com.android.launcher3.util.MultiAdditivePropertyFactory; +import com.android.launcher3.util.MultiPropertyFactory; +import com.android.launcher3.util.MultiPropertyFactory.MultiProperty; import com.android.launcher3.util.MultiValueAlpha; -import com.android.launcher3.util.UiThreadHelper; import com.android.launcher3.views.ScrimView; /** @@ -78,6 +76,8 @@ public class AllAppsTransitionController } }; + 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") { @@ -86,8 +86,7 @@ public class AllAppsTransitionController if (controller.mIsTablet) { return controller.mAppsView.getActiveRecyclerView().getTranslationY(); } else { - return controller.getAppsViewPullbackTranslationY().get( - controller.mAppsView); + return controller.getAppsViewPullbackTranslationY().getValue(); } } @@ -95,13 +94,18 @@ public class AllAppsTransitionController 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") { @@ -118,8 +122,12 @@ public class AllAppsTransitionController 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); } } }; @@ -133,6 +141,9 @@ public class AllAppsTransitionController private final Launcher mLauncher; private boolean mIsVerticalLayout; + // Whether this class should take care of closing the keyboard. + private boolean mShouldControlKeyboard; + // Animation in this class is controlled by a single variable {@link mProgress}. // Visually, it represents top y coordinate of the all apps container if multiplied with // {@link mShiftRange}. @@ -144,10 +155,8 @@ public class AllAppsTransitionController private ScrimView mScrimView; - private final MultiAdditivePropertyFactory<View> - mAppsViewTranslationYPropertyFactory = new MultiAdditivePropertyFactory<>( - "appsViewTranslationY", View.TRANSLATION_Y); private MultiValueAlpha mAppsViewAlpha; + private MultiPropertyFactory<View> mAppsViewTranslationY; private boolean mIsTablet; @@ -188,7 +197,7 @@ public class AllAppsTransitionController */ public void setProgress(float progress) { mProgress = progress; - getAppsViewProgressTranslationY().set(mAppsView, mProgress * mShiftRange); + getAppsViewProgressTranslationY().setValue(mProgress * mShiftRange); mLauncher.onAllAppsTransition(1 - progress); } @@ -196,20 +205,20 @@ public class AllAppsTransitionController 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); } /** @@ -230,12 +239,25 @@ public class AllAppsTransitionController @Override public void setStateWithAnimation(LauncherState toState, StateAnimationConfig config, PendingAnimation builder) { - if (NORMAL.equals(toState) && mLauncher.isInState(ALL_APPS)) { - UiThreadHelper.hideKeyboardAsync(mLauncher, mLauncher.getAppsView().getWindowToken()); + if (mLauncher.isInState(ALL_APPS) && !ALL_APPS.equals(toState)) { + // For atomic animations, we close the keyboard immediately. + if (!config.userControlled && mShouldControlKeyboard) { + mLauncher.getAppsView().getSearchUiManager().getEditText().hideKeyboard(); + } + builder.addEndListener(success -> { // Reset pull back progress and alpha after switching states. - ALL_APPS_PULL_BACK_TRANSLATION.set(this, 0f); - ALL_APPS_PULL_BACK_ALPHA.set(this, 1f); + ALL_APPS_PULL_BACK_TRANSLATION.set(this, ALL_APPS_PULL_BACK_TRANSLATION_DEFAULT); + ALL_APPS_PULL_BACK_ALPHA.set(this, ALL_APPS_PULL_BACK_ALPHA_DEFAULT); + + // We only want to close the keyboard if the animation has completed successfully. + // The reason is that with keyboard sync, if the user swipes down from All Apps with + // the keyboard open and then changes their mind and swipes back up, we want the + // keyboard to remain open. However an onCancel signal is sent to the listeners + // (success = false), so we need to check for that. + if (config.userControlled && success && mShouldControlKeyboard) { + mLauncher.getAppsView().getSearchUiManager().getEditText().hideKeyboard(); + } }); } @@ -243,7 +265,6 @@ public class AllAppsTransitionController if (Float.compare(mProgress, targetProgress) == 0) { setAlphas(toState, config, builder); // Fail fast - onProgressAnimationEnd(); return; } @@ -275,7 +296,7 @@ public class AllAppsTransitionController 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 = @@ -293,14 +314,14 @@ public class AllAppsTransitionController public void setupViews(ScrimView scrimView, ActivityAllAppsContainerView<Launcher> appsView) { mScrimView = scrimView; mAppsView = appsView; - if (FeatureFlags.ENABLE_DEVICE_SEARCH.get() && Utilities.ATLEAST_R) { - mLauncher.getSystemUiController().updateUiState(UI_STATE_ALLAPPS, - View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN - | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); - } 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(); } /** @@ -315,9 +336,11 @@ public class AllAppsTransitionController * 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 */); + 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 45a567dd19..29767bf465 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; @@ -50,7 +45,7 @@ public class AlphabeticalAppsList<T extends Context & ActivityContext> implement public static final String TAG = "AlphabeticalAppsList"; - private final WorkAdapterProvider mWorkAdapterProvider; + private final WorkProfileManager mWorkProviderManager; /** * Info about a fast scroller section, depending if sections are merged, the fast scroller @@ -92,11 +87,11 @@ public class AlphabeticalAppsList<T extends Context & ActivityContext> implement private Predicate<ItemInfo> mItemFilter; public AlphabeticalAppsList(Context context, @Nullable AllAppsStore appsStore, - WorkAdapterProvider adapterProvider) { + WorkProfileManager workProfileManager) { mAllAppsStore = appsStore; mActivityContext = ActivityContext.lookupContext(context); mAppNameComparator = new AppInfoComparator(context); - mWorkAdapterProvider = adapterProvider; + mWorkProviderManager = workProfileManager; mNumAppsPerRowAllApps = mActivityContext.getDeviceProfile().inv.numAllAppsColumns; if (mAllAppsStore != null) { mAllAppsStore.addUpdateListener(this); @@ -173,13 +168,6 @@ public class AlphabeticalAppsList<T extends Context & ActivityContext> implement } /** - * 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 @@ public class AlphabeticalAppsList<T extends Context & ActivityContext> implement // 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; - if (mWorkAdapterProvider != null) { - position += mWorkAdapterProvider.addWorkItems(mAdapterItems); - if (!mWorkAdapterProvider.shouldShowWorkApps()) { - return; - } + boolean addApps = true; + if (mWorkProviderManager != null) { + position += mWorkProviderManager.addWorkItems(mAdapterItems); + addApps = mWorkProviderManager.shouldShowWorkApps(); } - 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)); + 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)); + } + 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 fcba246c95..42f8b0ccaa 100644 --- a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java +++ b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java @@ -19,7 +19,6 @@ import static com.android.launcher3.touch.ItemLongClickListener.INSTANCE_ALL_APP 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,14 +53,13 @@ public abstract class BaseAllAppsAdapter<T extends Context & ActivityContext> ex 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; + // A divider that separates the apps list and the search market button + public static final int VIEW_TYPE_ALL_APPS_DIVIDER = 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 + public static final int VIEW_TYPE_WORK_EDU_CARD = 1 << 4; + public static final int VIEW_TYPE_WORK_DISABLED_CARD = 1 << 5; - // 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 NEXT_ID = 6; // Common view type masks public static final int VIEW_TYPE_MASK_DIVIDER = VIEW_TYPE_ALL_APPS_DIVIDER; @@ -112,7 +110,7 @@ public abstract class BaseAllAppsAdapter<T extends Context & ActivityContext> ex } protected boolean isCountedForAccessibility() { - return viewType == VIEW_TYPE_ICON || viewType == VIEW_TYPE_SEARCH_MARKET; + return viewType == VIEW_TYPE_ICON; } /** @@ -129,20 +127,22 @@ public abstract class BaseAllAppsAdapter<T extends Context & ActivityContext> ex 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, @@ -150,7 +150,6 @@ public abstract class BaseAllAppsAdapter<T extends Context & ActivityContext> ex Resources res = activityContext.getResources(); mActivityContext = activityContext; mApps = apps; - mEmptySearchMessage = res.getString(R.string.all_apps_loading_message); mLayoutInflater = inflater; mOnIconClickListener = mActivityContext.getItemOnClickListener(); @@ -181,16 +180,6 @@ public abstract class BaseAllAppsAdapter<T extends Context & ActivityContext> ex } /** - * 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(); @@ -217,14 +206,15 @@ public abstract class BaseAllAppsAdapter<T extends Context & ActivityContext> ex 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)); + case VIEW_TYPE_WORK_EDU_CARD: + return new ViewHolder(mLayoutInflater.inflate( + R.layout.work_apps_edu, parent, false)); + case VIEW_TYPE_WORK_DISABLED_CARD: + return new ViewHolder(mLayoutInflater.inflate( + R.layout.work_apps_paused, parent, false)); default: BaseAdapterProvider adapterProvider = getAdapterProvider(viewType); if (adapterProvider != null) { @@ -237,29 +227,28 @@ public abstract class BaseAllAppsAdapter<T extends Context & ActivityContext> ex @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 break; + case VIEW_TYPE_WORK_EDU_CARD: + ((WorkEduCard) holder.itemView).setPosition(position); + break; default: BaseAdapterProvider adapterProvider = getAdapterProvider(holder.getItemViewType()); if (adapterProvider != null) { diff --git a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java index ecadec673a..00e89bacc5 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.content.Context; 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.os.UserManager; 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,15 +52,17 @@ import androidx.core.graphics.ColorUtils; 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; import com.android.launcher3.Insettable; import com.android.launcher3.InsettableFrameLayout; +import com.android.launcher3.LauncherPrefs; 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 +85,8 @@ import java.util.stream.Stream; * * @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 +95,9 @@ public abstract class BaseAllAppsContainerView<T extends Context & ActivityConte 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,72 +106,144 @@ public abstract class BaseAllAppsContainerView<T extends Context & ActivityConte 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), mActivityContext.getDeviceProfile()); + this, LauncherPrefs.getPrefs(mActivityContext), + 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() { @@ -191,7 +272,6 @@ public abstract class BaseAllAppsContainerView<T extends Context & ActivityConte reset(true); } } - } @Override @@ -235,6 +315,9 @@ public abstract class BaseAllAppsContainerView<T extends Context & ActivityConte 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() { @@ -245,6 +328,10 @@ public abstract class BaseAllAppsContainerView<T extends Context & ActivityConte mWorkManager.reset(); } } + + mActivityContext.getStatsLogManager().logger() + .withCardinality(mAllAppsStore.getApps().length) + .log(LAUNCHER_ALLAPPS_COUNT); } /** @@ -253,7 +340,7 @@ public abstract class BaseAllAppsContainerView<T extends Context & ActivityConte public boolean shouldContainerScroll(MotionEvent ev) { BaseDragLayer dragLayer = mActivityContext.getDragLayer(); // Scroll if not within the container view (e.g. over large-screen scrim). - if (!dragLayer.isEventOverView(this, ev)) { + if (!dragLayer.isEventOverView(getVisibleContainerView(), ev)) { return true; } if (dragLayer.isEventOverView(mBottomSheetHandleArea, ev)) { @@ -273,6 +360,13 @@ public abstract class BaseAllAppsContainerView<T extends Context & ActivityConte @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 @@ -290,6 +384,10 @@ public abstract class BaseAllAppsContainerView<T extends Context & ActivityConte @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 @@ -304,7 +402,8 @@ public abstract class BaseAllAppsContainerView<T extends Context & ActivityConte mTouchHandler.handleTouchEvent(ev, mFastScrollerOffset); return true; } - if (isSearching()) { + if (isSearching() + && mActivityContext.getDragLayer().isEventOverView(getVisibleContainerView(), ev)) { // if in search state, consume touch event. return true; } @@ -349,7 +448,7 @@ public abstract class BaseAllAppsContainerView<T extends Context & ActivityConte * 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 +492,6 @@ public abstract class BaseAllAppsContainerView<T extends Context & ActivityConte } @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 +509,12 @@ public abstract class BaseAllAppsContainerView<T extends Context & ActivityConte 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); @@ -491,11 +570,16 @@ public abstract class BaseAllAppsContainerView<T extends Context & ActivityConte 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 +587,7 @@ public abstract class BaseAllAppsContainerView<T extends Context & ActivityConte 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 +595,7 @@ public abstract class BaseAllAppsContainerView<T extends Context & ActivityConte mActivityContext.getStatsLogManager().logger() .log(LAUNCHER_ALLAPPS_TAP_ON_WORK_TAB); } - hideKeyboardAsync(ActivityContext.lookupContext(getContext()), - getApplicationWindowToken()); + mActivityContext.hideKeyboard(); }); setDeviceManagementResources(); onActivePageChanged(mViewPager.getNextPage()); @@ -525,15 +607,18 @@ public abstract class BaseAllAppsContainerView<T extends Context & ActivityConte 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 +671,10 @@ public abstract class BaseAllAppsContainerView<T extends Context & ActivityConte 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; @@ -602,6 +687,8 @@ public abstract class BaseAllAppsContainerView<T extends Context & ActivityConte if (mAH.get(currentActivePage).mRecyclerView != null) { mAH.get(currentActivePage).mRecyclerView.bindFastScrollbar(); } + // Header keeps track of active recycler view to properly render header protection. + mHeader.setActiveRV(currentActivePage); reset(true /* animate */); mWorkManager.onActivePageChanged(currentActivePage); @@ -722,22 +809,61 @@ public abstract class BaseAllAppsContainerView<T extends Context & ActivityConte @Override public void drawOnScrim(Canvas canvas) { - if (!mHeader.isHeaderProtectionSupported()) { + 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; + int bottom = getHeaderBottom() + getVisibleContainerView().getPaddingTop(); + 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); } } @@ -745,7 +871,7 @@ public abstract class BaseAllAppsContainerView<T extends Context & ActivityConte * redraws header protection */ public void invalidateHeader() { - if (mScrimView != null && mHeader.isHeaderProtectionSupported()) { + if (mScrimView != null) { mScrimView.invalidate(); } } @@ -772,7 +898,14 @@ public abstract class BaseAllAppsContainerView<T extends Context & ActivityConte 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(); } /** @@ -782,6 +915,16 @@ public abstract class BaseAllAppsContainerView<T extends Context & ActivityConte 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; @@ -799,12 +942,9 @@ public abstract class BaseAllAppsContainerView<T extends Context & ActivityConte mType = type; mAppsList = new AlphabeticalAppsList<>(mActivityContext, isSearch() ? null : mAllAppsStore, - isWork() ? mWorkManager.getAdapterProvider() : null); - + isWork() ? mWorkManager : null); BaseAdapterProvider[] adapterProviders = - isWork() ? new BaseAdapterProvider[]{mMainAdapterProvider, - mWorkManager.getAdapterProvider()} - : new BaseAdapterProvider[]{mMainAdapterProvider}; + new BaseAdapterProvider[]{mMainAdapterProvider}; mAdapter = createAdapter(mAppsList, adapterProviders); mAppsList.setAdapter(mAdapter); @@ -821,7 +961,7 @@ public abstract class BaseAllAppsContainerView<T extends Context & ActivityConte 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 0000000000..9f47e8d82f --- /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 d0331c9266..c18f9e1884 100644 --- a/src/com/android/launcher3/allapps/FloatingHeaderView.java +++ b/src/com/android/launcher3/allapps/FloatingHeaderView.java @@ -41,6 +41,7 @@ import com.android.systemui.plugins.AllAppsRow.OnHeightUpdatedListener; import com.android.systemui.plugins.PluginListener; import java.util.ArrayList; +import java.util.Arrays; import java.util.Map; public class FloatingHeaderView extends LinearLayout implements @@ -66,7 +67,7 @@ public class FloatingHeaderView extends LinearLayout implements mAnimator.cancel(); } - int current = -mCurrentRV.getCurrentScrollY(); + int current = -mCurrentRV.computeVerticalScrollOffset(); boolean headerCollapsed = mHeaderCollapsed; moved(current); applyVerticalMove(); @@ -81,16 +82,14 @@ public class FloatingHeaderView extends LinearLayout implements protected final Map<AllAppsRow, PluginHeaderRow> mPluginRows = new ArrayMap<>(); // These two values are necessary to ensure that the header protection is drawn correctly. - private final int mHeaderTopAdjustment; - private final int mHeaderBottomAdjustment; - private final 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; @@ -99,7 +98,12 @@ public class FloatingHeaderView extends LinearLayout implements 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. @@ -109,21 +113,16 @@ public class FloatingHeaderView extends LinearLayout implements // 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) - // TODO(b/208599118) Support header protection for bottom sheet. - && !ActivityContext.lookupContext(context).getDeviceProfile().isTablet; } @Override @@ -142,6 +141,7 @@ public class FloatingHeaderView extends LinearLayout implements } mFixedRows = rows.toArray(new FloatingHeaderRow[rows.size()]); mAllRows = mFixedRows; + updateFloatingRowsHeight(); } @Override @@ -173,6 +173,7 @@ public class FloatingHeaderView extends LinearLayout implements count++; } } + updateFloatingRowsHeight(); } @Override @@ -189,7 +190,7 @@ public class FloatingHeaderView extends LinearLayout implements int oldMaxHeight = mMaxTranslation; updateExpectedHeight(); - if (mMaxTranslation != oldMaxHeight || mCollapsed) { + if (mMaxTranslation != oldMaxHeight || mFloatingRowsCollapsed) { BaseAllAppsContainerView<?> parent = (BaseAllAppsContainerView<?>) getParent(); if (parent != null) { parent.setupHeader(); @@ -252,20 +253,20 @@ public class FloatingHeaderView extends LinearLayout implements } 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(); @@ -306,7 +307,7 @@ public class FloatingHeaderView extends LinearLayout implements 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 */); @@ -319,11 +320,12 @@ public class FloatingHeaderView extends LinearLayout implements 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); @@ -341,13 +343,19 @@ public class FloatingHeaderView extends LinearLayout implements /** * 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(); @@ -370,6 +378,30 @@ public class FloatingHeaderView extends LinearLayout implements 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(); @@ -406,10 +438,6 @@ public class FloatingHeaderView extends LinearLayout implements p.y = getTop() - mCurrentRV.getTop() - ((ViewGroup) mCurrentRV.getParent()).getTop(); } - public boolean isHeaderProtectionSupported() { - return mHeaderProtectionSupported; - } - @Override public boolean hasOverlappingRendering() { return false; @@ -434,16 +462,13 @@ public class FloatingHeaderView extends LinearLayout implements /** * 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 20f5e7440d..5a5ba2bb3c 100644 --- a/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java @@ -17,7 +17,6 @@ package com.android.launcher3.allapps; import android.content.Context; import android.util.AttributeSet; -import android.view.MotionEvent; import android.view.WindowInsets; import com.android.launcher3.Launcher; @@ -42,26 +41,6 @@ public class LauncherAllAppsContainerView extends ActivityAllAppsContainerView<L } @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) { if (Utilities.ATLEAST_Q) { return insets.getTappableElementInsets().bottom; @@ -69,4 +48,9 @@ public class LauncherAllAppsContainerView extends ActivityAllAppsContainerView<L 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 482bd296f0..9d1dfc0de3 100644 --- a/src/com/android/launcher3/allapps/SearchRecyclerView.java +++ b/src/com/android/launcher3/allapps/SearchRecyclerView.java @@ -17,12 +17,17 @@ package com.android.launcher3.allapps; 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 @@ public class SearchRecyclerView extends AllAppsRecyclerView { 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 class SearchRecyclerView extends AllAppsRecyclerView { 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 0000000000..50567822a6 --- /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 6138bc4a9d..228b02bbc1 100644 --- a/src/com/android/launcher3/allapps/SearchUiManager.java +++ b/src/com/android/launcher3/allapps/SearchUiManager.java @@ -64,7 +64,8 @@ public interface SearchUiManager { /** * 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 0719c4342f..684e98ed96 100644 --- a/src/com/android/launcher3/allapps/SecondaryLauncherAllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/SecondaryLauncherAllAppsContainerView.java @@ -42,4 +42,9 @@ public class SecondaryLauncherAllAppsContainerView extends @Override protected void updateBackground(DeviceProfile deviceProfile) {} + + @Override + public boolean isInAllApps() { + return mActivityContext.isAppDrawerShown(); + } } diff --git a/src/com/android/launcher3/allapps/WorkAdapterProvider.java b/src/com/android/launcher3/allapps/WorkAdapterProvider.java deleted file mode 100644 index 76d08c8084..0000000000 --- a/src/com/android/launcher3/allapps/WorkAdapterProvider.java +++ /dev/null @@ -1,138 +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.allapps; - -import android.content.SharedPreferences; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import com.android.launcher3.R; -import com.android.launcher3.allapps.BaseAllAppsAdapter.AdapterItem; -import com.android.launcher3.model.StringCache; -import com.android.launcher3.views.ActivityContext; - -import java.util.ArrayList; - -/** - * A UI expansion wrapper providing for providing work profile specific views - */ -public class WorkAdapterProvider extends BaseAdapterProvider { - - public static final String KEY_WORK_EDU_STEP = "showed_work_profile_edu"; - - private static final int VIEW_TYPE_WORK_EDU_CARD = 1 << 20; - private static final int VIEW_TYPE_WORK_DISABLED_CARD = 1 << 21; - - @WorkProfileManager.WorkProfileState - private int mState; - private ActivityContext mActivityContext; - private SharedPreferences mPreferences; - - WorkAdapterProvider(ActivityContext activityContext, SharedPreferences prefs) { - mActivityContext = activityContext; - mPreferences = prefs; - } - - @Override - public void onBindView(AllAppsGridAdapter.ViewHolder holder, int position) { - if (holder.itemView instanceof WorkEduCard) { - ((WorkEduCard) holder.itemView).setPosition(position); - } - } - - @Override - public AllAppsGridAdapter.ViewHolder onCreateViewHolder(LayoutInflater layoutInflater, - ViewGroup parent, int viewType) { - int viewId = viewType == VIEW_TYPE_WORK_DISABLED_CARD ? R.layout.work_apps_paused - : R.layout.work_apps_edu; - View view = layoutInflater.inflate(viewId, parent, false); - setDeviceManagementResources(view, viewType); - return new AllAppsGridAdapter.ViewHolder(view); - } - - private void setDeviceManagementResources(View view, int viewType) { - StringCache cache = mActivityContext.getStringCache(); - if (cache == null) { - return; - } - if (viewType == VIEW_TYPE_WORK_DISABLED_CARD) { - setWorkProfilePausedResources(view, cache); - } else { - setWorkProfileEduResources(view, cache); - } - } - - private void setWorkProfilePausedResources(View view, StringCache cache) { - TextView title = view.findViewById(R.id.work_apps_paused_title); - title.setText(cache.workProfilePausedTitle); - - TextView body = view.findViewById(R.id.work_apps_paused_content); - body.setText(cache.workProfilePausedDescription); - - TextView button = view.findViewById(R.id.enable_work_apps); - button.setText(cache.workProfileEnableButton); - } - - private void setWorkProfileEduResources(View view, StringCache cache) { - TextView title = view.findViewById(R.id.work_apps_paused_title); - title.setText(cache.workProfileEdu); - - } - - /** - * returns whether or not work apps should be visible in work tab. - */ - public boolean shouldShowWorkApps() { - return mState != WorkProfileManager.STATE_DISABLED; - } - - /** - * Adds work profile specific adapter items to adapterItems and returns number of items added - */ - public int addWorkItems(ArrayList<AllAppsGridAdapter.AdapterItem> adapterItems) { - if (mState == WorkProfileManager.STATE_DISABLED) { - //add disabled card here. - adapterItems.add(new AdapterItem(VIEW_TYPE_WORK_DISABLED_CARD)); - } else if (mState == WorkProfileManager.STATE_ENABLED && !isEduSeen()) { - adapterItems.add(new AdapterItem(VIEW_TYPE_WORK_EDU_CARD)); - } - - return adapterItems.size(); - } - - /** - * Sets the current state of work profile - */ - public void updateCurrentState(@WorkProfileManager.WorkProfileState int state) { - mState = state; - } - - @Override - public boolean isViewSupported(int viewType) { - return viewType == VIEW_TYPE_WORK_DISABLED_CARD || viewType == VIEW_TYPE_WORK_EDU_CARD; - } - - @Override - public int getItemsPerRow(int viewType, int appsPerRow) { - return 1; - } - - private boolean isEduSeen() { - return mPreferences.getInt(KEY_WORK_EDU_STEP, 0) != 0; - } -} diff --git a/src/com/android/launcher3/allapps/WorkEduCard.java b/src/com/android/launcher3/allapps/WorkEduCard.java index 539cff1a37..b3245eec4f 100644 --- a/src/com/android/launcher3/allapps/WorkEduCard.java +++ b/src/com/android/launcher3/allapps/WorkEduCard.java @@ -24,9 +24,11 @@ import android.view.ViewGroup; import android.view.animation.Animation; import android.view.animation.AnimationUtils; 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; /** @@ -72,12 +74,18 @@ public class WorkEduCard extends FrameLayout implements protected void onFinishInflate() { super.onFinishInflate(); findViewById(R.id.action_btn).setOnClickListener(this); + + StringCache cache = mActivityContext.getStringCache(); + if (cache != null) { + TextView title = findViewById(R.id.work_apps_paused_title); + title.setText(cache.workProfileEdu); + } } @Override public void onClick(View view) { startAnimation(mDismissAnim); - Utilities.getPrefs(getContext()).edit().putInt(WorkAdapterProvider.KEY_WORK_EDU_STEP, + LauncherPrefs.getPrefs(getContext()).edit().putInt(WorkProfileManager.KEY_WORK_EDU_STEP, 1).apply(); } diff --git a/src/com/android/launcher3/allapps/WorkModeSwitch.java b/src/com/android/launcher3/allapps/WorkModeSwitch.java index edb012854a..11ce738208 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,77 +39,84 @@ import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.anim.KeyboardInsetAnimationCallback; import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.model.StringCache; import com.android.launcher3.views.ActivityContext; -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; + + // 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(Context context) { + 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(); - lp.rightMargin = lp.leftMargin = dp.allAppsLeftRightPadding; - if (!dp.isGestureMode) { - if (dp.isTaskbarPresent) { - bottomMargin += dp.taskbarSize; - } else { - bottomMargin += insets.bottom; - } + if (!dp.isGestureMode && dp.isTaskbarPresent) { + bottomMargin += dp.taskbarSize; } lp.bottomMargin = bottomMargin; @@ -113,72 +126,61 @@ public class WorkModeSwitch extends Button implements Insettable, View.OnClickLi @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 @@ -198,4 +200,18 @@ public class WorkModeSwitch extends Button implements Insettable, View.OnClickLi private void removeFlag(int flag) { mFlags &= ~flag; } + + public void extend() { + mTextView.setVisibility(VISIBLE); + mStatsLogManager.logger().log(LAUNCHER_WORK_FAB_BUTTON_EXTEND); + } + + public void shrink(){ + mTextView.setVisibility(GONE); + mStatsLogManager.logger().log(LAUNCHER_WORK_FAB_BUTTON_COLLAPSE); + } + + public int getScrollThreshold() { + return mScrollThreshold; + } } diff --git a/src/com/android/launcher3/allapps/WorkPausedCard.java b/src/com/android/launcher3/allapps/WorkPausedCard.java index 729622f519..26a78039cb 100644 --- a/src/com/android/launcher3/allapps/WorkPausedCard.java +++ b/src/com/android/launcher3/allapps/WorkPausedCard.java @@ -23,9 +23,11 @@ import android.util.AttributeSet; import android.view.View; import android.widget.Button; import android.widget.LinearLayout; +import android.widget.TextView; import com.android.launcher3.R; import com.android.launcher3.Utilities; +import com.android.launcher3.model.StringCache; import com.android.launcher3.views.ActivityContext; /** @@ -49,12 +51,27 @@ public class WorkPausedCard extends LinearLayout implements View.OnClickListener mActivityContext = ActivityContext.lookupContext(getContext()); } - @Override protected void onFinishInflate() { super.onFinishInflate(); mBtn = findViewById(R.id.enable_work_apps); mBtn.setOnClickListener(this); + + StringCache cache = mActivityContext.getStringCache(); + if (cache != null) { + setWorkProfilePausedResources(cache); + } + } + + private void setWorkProfilePausedResources(StringCache cache) { + TextView title = findViewById(R.id.work_apps_paused_title); + title.setText(cache.workProfilePausedTitle); + + TextView body = findViewById(R.id.work_apps_paused_content); + body.setText(cache.workProfilePausedDescription); + + TextView button = findViewById(R.id.enable_work_apps); + button.setText(cache.workProfileEnableButton); } @Override diff --git a/src/com/android/launcher3/allapps/WorkProfileManager.java b/src/com/android/launcher3/allapps/WorkProfileManager.java index 2f5b7a2dd3..279f0d399c 100644 --- a/src/com/android/launcher3/allapps/WorkProfileManager.java +++ b/src/com/android/launcher3/allapps/WorkProfileManager.java @@ -15,6 +15,12 @@ */ package com.android.launcher3.allapps; +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; @@ -26,18 +32,24 @@ 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.DeviceProfile; 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; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; import java.util.function.Predicate; /** @@ -48,13 +60,12 @@ import java.util.function.Predicate; public class WorkProfileManager implements PersonalWorkSlidingTabStrip.OnActivePageChangedListener { private static final String TAG = "WorkProfileManager"; + public static final String KEY_WORK_EDU_STEP = "showed_work_profile_edu"; public static final int STATE_ENABLED = 1; public static final int STATE_DISABLED = 2; public static final int STATE_TRANSITION = 3; - private final UserManager mUserManager; - /** * Work profile manager states */ @@ -64,27 +75,27 @@ public class WorkProfileManager implements PersonalWorkSlidingTabStrip.OnActiveP STATE_TRANSITION }) @Retention(RetentionPolicy.SOURCE) - public @interface WorkProfileState { - } + public @interface WorkProfileState { } + private final UserManager mUserManager; private final BaseAllAppsContainerView<?> mAllApps; - private final WorkAdapterProvider mAdapterProvider; private final Predicate<ItemInfo> mMatcher; + private final StatsLogManager mStatsLogManager; private WorkModeSwitch mWorkModeSwitch; - private final DeviceProfile mDeviceProfile; @WorkProfileState private int mCurrentState; + private SharedPreferences mPreferences; - - public WorkProfileManager(UserManager userManager, BaseAllAppsContainerView<?> allApps, - SharedPreferences preferences, DeviceProfile deviceProfile) { + public WorkProfileManager( + UserManager userManager, BaseAllAppsContainerView<?> allApps, SharedPreferences prefs, + StatsLogManager statsLogManager) { mUserManager = userManager; mAllApps = allApps; - mDeviceProfile = deviceProfile; - mAdapterProvider = new WorkAdapterProvider(allApps.mActivityContext, preferences); + mPreferences = prefs; mMatcher = mAllApps.mPersonalMatcher.negate(); + mStatsLogManager = statsLogManager; } /** @@ -105,8 +116,16 @@ public class WorkProfileManager implements PersonalWorkSlidingTabStrip.OnActiveP @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); + } } } @@ -120,12 +139,16 @@ public class WorkProfileManager implements PersonalWorkSlidingTabStrip.OnActiveP private void updateCurrentState(@WorkProfileState int currentState) { mCurrentState = currentState; - mAdapterProvider.updateCurrentState(currentState); if (getAH() != null) { 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(); } } @@ -142,13 +165,16 @@ public class WorkProfileManager implements PersonalWorkSlidingTabStrip.OnActiveP 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; } /** @@ -161,10 +187,6 @@ public class WorkProfileManager implements PersonalWorkSlidingTabStrip.OnActiveP mWorkModeSwitch = null; } - public WorkAdapterProvider getAdapterProvider() { - return mAdapterProvider; - } - public Predicate<ItemInfo> getMatcher() { return mMatcher; } @@ -175,10 +197,68 @@ public class WorkProfileManager implements PersonalWorkSlidingTabStrip.OnActiveP } private BaseAllAppsContainerView<?>.AdapterHolder getAH() { - return mAllApps.mAH.get(BaseAllAppsContainerView.AdapterHolder.WORK); + return mAllApps.mAH.get(WORK); } public int getCurrentState() { return mCurrentState; } + + /** + * returns whether or not work apps should be visible in work tab. + */ + public boolean shouldShowWorkApps() { + return mCurrentState != WorkProfileManager.STATE_DISABLED; + } + + /** + * Adds work profile specific adapter items to adapterItems and returns number of items added + */ + public int addWorkItems(ArrayList<AdapterItem> adapterItems) { + if (mCurrentState == WorkProfileManager.STATE_DISABLED) { + //add disabled card here. + adapterItems.add(new AdapterItem(VIEW_TYPE_WORK_DISABLED_CARD)); + } else if (mCurrentState == WorkProfileManager.STATE_ENABLED && !isEduSeen()) { + adapterItems.add(new AdapterItem(VIEW_TYPE_WORK_EDU_CARD)); + } + return adapterItems.size(); + } + + private boolean isEduSeen() { + return mPreferences.getInt(KEY_WORK_EDU_STEP, 0) != 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 cf4091f95e..f94658c404 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 @@ public class AllAppsSearchBarController mInput.addTextChangedListener(this); mInput.setOnEditorActionListener(this); mInput.setOnBackKeyListener(this); - mInput.setOnFocusChangeListener(this); + mInput.addOnFocusChangeListener(this); mSearchAlgorithm = searchAlgorithm; } @@ -84,7 +81,10 @@ public class AllAppsSearchBarController 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 class AllAppsSearchBarController 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 @@ public class AllAppsSearchBarController 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 e0f9d1168e..d83efd6c16 100644 --- a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java +++ b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java @@ -130,7 +130,7 @@ public class AppsSearchContainerLayout extends ExtendedEditText public void initializeSearch(ActivityAllAppsContainerView<?> appsView) { mAppsView = appsView; mSearchBarController.initialize( - new DefaultAppSearchAlgorithm(getContext()), + new DefaultAppSearchAlgorithm(getContext(), true), this, mLauncher, this); } @@ -167,14 +167,11 @@ public class AppsSearchContainerLayout extends ExtendedEditText 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 4eceb7184e..ab47097697 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 @@ public class DefaultAppSearchAlgorithm implements SearchAlgorithm<AdapterItem> { 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 class DefaultAppSearchAlgorithm implements SearchAlgorithm<AdapterItem> { 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 a95bd514da..20edf8a93c 100644 --- a/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java +++ b/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java @@ -26,17 +26,17 @@ import androidx.recyclerview.widget.RecyclerView; 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 class DefaultSearchAdapterProvider extends SearchAdapterProvider<AppLaunc 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 bc52784caf..3890741d8e 100644 --- a/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java +++ b/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java @@ -58,4 +58,9 @@ public abstract class SearchAdapterProvider<T extends ActivityContext> extends B * 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 index 6c7a885a1f..b73621d414 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 @@ public class AnimatedFloat { } /** - * 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; } @@ -133,4 +126,11 @@ public class AnimatedFloat { public boolean isAnimatingToValue(float endValue) { return isAnimating() && mEndValue != null && mEndValue == endValue; } + + /** + * Returns whether we are currently not animating, and the animation's value matches the given. + */ + public boolean isSettledOnValue(float endValue) { + return !isAnimating() && value == endValue; + } } diff --git a/src/com/android/launcher3/anim/AnimatedPropertySetter.java b/src/com/android/launcher3/anim/AnimatedPropertySetter.java index e5f5e7c44b..82e645a7a0 100644 --- a/src/com/android/launcher3/anim/AnimatedPropertySetter.java +++ b/src/com/android/launcher3/anim/AnimatedPropertySetter.java @@ -43,9 +43,17 @@ public class AnimatedPropertySetter extends PropertySetter { @Override public Animator setViewAlpha(View view, float alpha, TimeInterpolator interpolator) { - if (view == null || view.getAlpha() == alpha) { + if (view == null) { return NO_OP; } + + // Short-circuit if the view already has this alpha value, but make sure the visibility is + // set correctly for the requested alpha. + if (Float.compare(view.getAlpha(), alpha) == 0) { + AlphaUpdateListener.updateVisibility(view); + return NO_OP; + } + ObjectAnimator anim = ObjectAnimator.ofFloat(view, View.ALPHA, alpha); anim.addListener(new AlphaUpdateListener(view)); anim.setInterpolator(interpolator); @@ -89,6 +97,18 @@ public class AnimatedPropertySetter extends PropertySetter { return anim; } + @NonNull + @Override + public <T> Animator setColor(T target, IntProperty<T> property, int value, + TimeInterpolator interpolator) { + if (property.get(target) == value) { + return NO_OP; + } + Animator anim = ObjectAnimator.ofArgb(target, property, value); + anim.setInterpolator(interpolator); + add(anim); + return anim; + } /** * Adds a callback to be run on every frame of the animation diff --git a/src/com/android/launcher3/anim/AnimationSuccessListener.java b/src/com/android/launcher3/anim/AnimationSuccessListener.java index a312070e58..6196df23ed 100644 --- a/src/com/android/launcher3/anim/AnimationSuccessListener.java +++ b/src/com/android/launcher3/anim/AnimationSuccessListener.java @@ -19,6 +19,8 @@ package com.android.launcher3.anim; 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 @@ public abstract class AnimationSuccessListener extends AnimatorListenerAdapter { 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 0a77aa7bcf..b55a1e4ada 100644 --- a/src/com/android/launcher3/anim/Interpolators.java +++ b/src/com/android/launcher3/anim/Interpolators.java @@ -57,6 +57,10 @@ public class Interpolators { public static final Interpolator DECELERATED_EASE = new PathInterpolator(0, 0, .2f, 1f); public static final Interpolator ACCELERATED_EASE = new PathInterpolator(0.4f, 0, 1f, 1f); + /** + * The default emphasized interpolator. Used for hero / emphasized movement of content. + */ + public static final Interpolator EMPHASIZED = createEmphasizedInterpolator(); public static final Interpolator EMPHASIZED_ACCELERATE = new PathInterpolator( 0.3f, 0f, 0.8f, 0.15f); public static final Interpolator EMPHASIZED_DECELERATE = new PathInterpolator( @@ -79,6 +83,7 @@ public class Interpolators { 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 +92,6 @@ public class Interpolators { 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 +222,14 @@ public class Interpolators { 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 9d96365acc..3863dc1962 100644 --- a/src/com/android/launcher3/anim/KeyboardInsetAnimationCallback.java +++ b/src/com/android/launcher3/anim/KeyboardInsetAnimationCallback.java @@ -53,10 +53,21 @@ public class KeyboardInsetAnimationCallback extends WindowInsetsAnimation.Callba mView.setTranslationY(mInitialTranslation); return windowInsets; } - float progress = list.get(0).getInterpolatedFraction(); - - mView.setTranslationY( - Utilities.mapRange(progress, mInitialTranslation, mTerminalTranslation)); + WindowInsetsAnimation animation = list.get(0); + + 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 @@ public class KeyboardInsetAnimationCallback extends WindowInsetsAnimation.Callba @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 d2207f6351..6ba58b6a98 100644 --- a/src/com/android/launcher3/anim/PropertySetter.java +++ b/src/com/android/launcher3/anim/PropertySetter.java @@ -38,6 +38,7 @@ public abstract class PropertySetter { public void add(Animator animatorSet) { animatorSet.setDuration(0); animatorSet.start(); + animatorSet.end(); } }; @@ -89,6 +90,16 @@ public abstract class PropertySetter { } /** + * Updates a color property of the target using the provided interpolator + */ + @NonNull + public <T> Animator setColor(T target, IntProperty<T> property, int value, + TimeInterpolator interpolator) { + property.setValue(target, value); + return NO_OP; + } + + /** * Runs the animation as part of setting the property */ public abstract void add(Animator animatorSet); diff --git a/src/com/android/launcher3/celllayout/CellLayoutLayoutParams.java b/src/com/android/launcher3/celllayout/CellLayoutLayoutParams.java new file mode 100644 index 0000000000..abd4682701 --- /dev/null +++ b/src/com/android/launcher3/celllayout/CellLayoutLayoutParams.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.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 { + + public int screenId = -1; + + /** + * Horizontal location of the item in the grid. + */ + @ViewDebug.ExportedProperty + public int cellX; + + /** + * Vertical location of the item in the grid. + */ + @ViewDebug.ExportedProperty + public int cellY; + + /** + * Temporary horizontal location of the item in the grid during reorder + */ + public int tmpCellX; + + /** + * Temporary vertical location of the item in the grid during reorder + */ + public int tmpCellY; + + /** + * Indicates that the temporary coordinates should be used to layout the items + */ + public boolean useTmpCoords; + + /** + * Number of cells spanned horizontally by the item. + */ + @ViewDebug.ExportedProperty + public int cellHSpan; + + /** + * Number of cells spanned vertically by the item. + */ + @ViewDebug.ExportedProperty + public int cellVSpan; + + /** + * Indicates whether the item will set its x, y, width and height parameters freely, + * or whether these will be computed based on cellX, cellY, cellHSpan and cellVSpan. + */ + public boolean isLockedToGrid = true; + + /** + * Indicates whether this item can be reordered. Always true except in the case of the + * the AllApps button and QSB place holder. + */ + public boolean canReorder = true; + + // X coordinate of the view in the layout. + @ViewDebug.ExportedProperty + public int x; + // Y coordinate of the view in the layout. + @ViewDebug.ExportedProperty + public int y; + + public boolean dropped; + + public CellLayoutLayoutParams(Context c, AttributeSet attrs) { + super(c, attrs); + cellHSpan = 1; + cellVSpan = 1; + } + + public CellLayoutLayoutParams(ViewGroup.LayoutParams source) { + super(source); + cellHSpan = 1; + cellVSpan = 1; + } + + public CellLayoutLayoutParams(CellLayoutLayoutParams source) { + super(source); + this.cellX = source.cellX; + this.cellY = source.cellY; + this.cellHSpan = source.cellHSpan; + this.cellVSpan = source.cellVSpan; + this.screenId = source.screenId; + this.tmpCellX = source.tmpCellX; + this.tmpCellY = source.tmpCellY; + this.useTmpCoords = source.useTmpCoords; + } + + public CellLayoutLayoutParams(int cellX, int cellY, int cellHSpan, int cellVSpan, + int screenId) { + super(CellLayoutLayoutParams.MATCH_PARENT, CellLayoutLayoutParams.MATCH_PARENT); + this.cellX = cellX; + this.cellY = cellY; + this.cellHSpan = cellHSpan; + this.cellVSpan = cellVSpan; + this.screenId = 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 ? tmpCellX : cellX; + int myCellY = useTmpCoords ? tmpCellY : cellY; + + if (invertHorizontally) { + myCellX = colCount - myCellX - cellHSpan; + } + + int hBorderSpacing = (myCellHSpan - 1) * borderSpace.x; + int vBorderSpacing = (myCellVSpan - 1) * borderSpace.y; + + float myCellWidth = ((myCellHSpan * cellWidth) + hBorderSpacing) / cellScaleX; + float myCellHeight = ((myCellVSpan * cellHeight) + vBorderSpacing) / cellScaleY; + + width = Math.round(myCellWidth) - leftMargin - rightMargin; + height = Math.round(myCellHeight) - topMargin - bottomMargin; + x = leftMargin + (myCellX * cellWidth) + (myCellX * borderSpace.x); + y = topMargin + (myCellY * cellHeight) + (myCellY * borderSpace.y); + + if (inset != null) { + x -= inset.left; + y -= inset.top; + width += inset.left + inset.right; + height += inset.top + inset.bottom; + } + } + } + + /** + * Sets the position to the provided point + */ + public void setCellXY(Point point) { + cellX = point.x; + cellY = point.y; + } + + /** + * @return the string representation of the position of the {@link CellLayoutLayoutParams} + */ + public String toString() { + return "(" + this.cellX + ", " + this.cellY + ")"; + } +} diff --git a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java index dd5812302d..770e93178e 100644 --- a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java +++ b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java @@ -27,7 +27,7 @@ import android.view.accessibility.AccessibilityManager; import androidx.annotation.Nullable; import com.android.launcher3.Utilities; -import com.android.launcher3.testing.TestProtocol; +import com.android.launcher3.testing.shared.TestProtocol; public class AccessibilityManagerCompat { diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index 090daeeb17..e3a7de8684 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 final class FeatureFlags { 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 @@ public final class FeatureFlags { * 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,8 +89,15 @@ public final class FeatureFlags { 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"); @@ -113,30 +116,18 @@ public final class FeatureFlags { "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, @@ -166,14 +157,22 @@ public final class FeatureFlags { "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, @@ -183,10 +182,6 @@ public final class FeatureFlags { "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 " @@ -209,16 +204,9 @@ public final class FeatureFlags { "ENABLE_SCRIM_FOR_APP_LAUNCH", true, "Enables scrim during app launch animation."); - public static final BooleanFlag ENABLE_SPLIT_SELECT = getDebugFlag( - "ENABLE_SPLIT_SELECT", true, "Uses new split screen selection overview UI"); - 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,41 @@ public final class FeatureFlags { "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", false, + "Enable splitting from fullscreen app with keyboard shortcuts"); + + public static final BooleanFlag ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE = getDebugFlag( + "ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE", false, + "Enable initiating split screen from workspace to workspace."); + public static final BooleanFlag ENABLE_NEW_MIGRATION_LOGIC = getDebugFlag( "ENABLE_NEW_MIGRATION_LOGIC", true, "Enable the new grid migration logic, keeping pages when src < dest"); + 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 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"); @@ -277,12 +289,112 @@ public final class FeatureFlags { "ENABLE_DISMISS_PREDICTION_UNDO", false, "Show an 'Undo' snackbar when users dismiss a predicted hotseat item"); + public static final BooleanFlag ENABLE_CACHED_WIDGET = getDebugFlag( + "ENABLE_CACHED_WIDGET", true, + "Show previously cached widgets as opposed to deferred widget where available"); + + public static final BooleanFlag USE_SEARCH_REQUEST_TIMEOUT_OVERRIDES = getDebugFlag( + "USE_SEARCH_REQUEST_TIMEOUT_OVERRIDES", false, + "Use local overrides for search request timeout"); + + public static final BooleanFlag CONTINUOUS_VIEW_TREE_CAPTURE = getDebugFlag( + "CONTINUOUS_VIEW_TREE_CAPTURE", false, "Capture View tree every frame"); + + public static final BooleanFlag FOLDABLE_WORKSPACE_REORDER = getDebugFlag( + "FOLDABLE_WORKSPACE_REORDER", true, + "In foldables, when reordering the icons and widgets, is now going to use both sides"); + + public static final BooleanFlag ENABLE_WIDGET_PICKER_DEPTH = new DeviceFlag( + "ENABLE_WIDGET_PICKER_DEPTH", true, "Enable changing depth in widget picker."); + + public static final BooleanFlag 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 = getDebugFlag( + "ENABLE_DOWNLOAD_APP_UX_V2", false, "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", false, "Enables transient taskbar."); + + public static final BooleanFlag SECONDARY_DRAG_N_DROP_TO_PIN = getDebugFlag( + "SECONDARY_DRAG_N_DROP_TO_PIN", false, + "Enable dragging and dropping to pin apps within secondary display"); + + public static final BooleanFlag SHOW_DOT_PAGINATION = getDebugFlag( + "SHOW_DOT_PAGINATION", false, "Enable showing dot pagination in workspace"); + + public static final BooleanFlag LARGE_SCREEN_WIDGET_PICKER = getDebugFlag( + "LARGE_SCREEN_WIDGET_PICKER", false, "Enable new widget picker that takes " + + "advantage of large screen format"); + + public static final BooleanFlag ENABLE_NEW_GESTURE_NAV_TUTORIAL = getDebugFlag( + "ENABLE_NEW_GESTURE_NAV_TUTORIAL", false, + "Enable the redesigned gesture navigation tutorial"); + + public static final BooleanFlag ENABLE_DEVICE_PROFILE_LOGGING = new DeviceFlag( + "ENABLE_DEVICE_PROFILE_LOGGING", false, "Allows DeviceProfile logging"); + + public static final BooleanFlag ENABLE_LAUNCH_FROM_STAGED_APP = getDebugFlag( + "ENABLE_LAUNCH_FROM_STAGED_APP", false, + "Enable the ability to tap a staged app during split select to launch it in full screen" + ); + + public static 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 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); + }); } } @@ -338,6 +450,7 @@ public final class FeatureFlags { public static class DebugFlag extends BooleanFlag { public final String description; + protected boolean mHasBeenChangedAtLeastOnce; protected boolean mCurrentValue; public DebugFlag(String key, boolean defaultValue, String description) { @@ -355,8 +468,10 @@ public final class FeatureFlags { } 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 6729f7434f..2eb6e6df9a 100644 --- a/src/com/android/launcher3/config/FlagTogglerPrefUi.java +++ b/src/com/android/launcher3/config/FlagTogglerPrefUi.java @@ -52,12 +52,17 @@ public final class FlagTogglerPrefUi { 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 466b2689ae..4906c1dc31 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.LAUNCH 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.appwidget.AppWidgetProviderInfo; 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.AccessibilityEvent; 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.R; 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.WidgetCell; 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 @@ public class AddItemActivity extends BaseActivity 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 @@ public class AddItemActivity extends BaseActivity 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 @@ public class AddItemActivity extends BaseActivity 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 @@ public class AddItemActivity extends BaseActivity } } - 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 @@ public class AddItemActivity extends BaseActivity 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 @@ public class AddItemActivity extends BaseActivity return; } - mPendingBindWidgetId = mAppWidgetHost.allocateAppWidgetId(); + mPendingBindWidgetId = mAppWidgetHolder.allocateAppWidgetId(); AppWidgetProviderInfo widgetProviderInfo = mRequest.getAppWidgetProviderInfo(this); boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed( mPendingBindWidgetId, widgetProviderInfo, mWidgetOptions); @@ -329,7 +361,7 @@ public class AddItemActivity extends BaseActivity } // request bind widget - mAppWidgetHost.startBindFlow(this, mPendingBindWidgetId, + mAppWidgetHolder.startBindFlow(this, mPendingBindWidgetId, mRequest.getAppWidgetProviderInfo(this), REQUEST_BIND_APPWIDGET); } @@ -343,6 +375,15 @@ public class AddItemActivity extends BaseActivity } @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 @@ public class AddItemActivity extends BaseActivity 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 35cdfef465..5368397c6d 100644 --- a/src/com/android/launcher3/dragndrop/DragController.java +++ b/src/com/android/launcher3/dragndrop/DragController.java @@ -31,10 +31,11 @@ import androidx.annotation.Nullable; 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; -import com.android.launcher3.testing.TestProtocol; +import com.android.launcher3.testing.shared.TestProtocol; import com.android.launcher3.util.TouchController; import com.android.launcher3.views.ActivityContext; @@ -90,6 +91,8 @@ public abstract class DragController<T extends ActivityContext> 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 @@ public abstract class DragController<T extends ActivityContext> 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 @@ public abstract class DragController<T extends ActivityContext> } 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 8eeca7d6d8..366870b4c2 100644 --- a/src/com/android/launcher3/dragndrop/DragLayer.java +++ b/src/com/android/launcher3/dragndrop/DragLayer.java @@ -43,26 +43,28 @@ import android.view.accessibility.AccessibilityManager; 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 class DragLayer extends BaseDragLayer<Launcher> { 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 @@ public class DragLayer extends BaseDragLayer<Launcher> { setChildrenDrawingOrderEnabled(true); mFocusIndicatorHelper = new ViewGroupFocusHelper(this); + mIsRtl = Utilities.isRtl(getResources()); } /** @@ -109,6 +114,7 @@ public class DragLayer extends BaseDragLayer<Launcher> { mDragController = dragController; recreateControllers(); mWorkspaceDragScrim = new Scrim(this); + workspace.addOverlayCallback(this); } @Override @@ -237,7 +243,7 @@ public class DragLayer extends BaseDragLayer<Launcher> { 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 @@ public class DragLayer extends BaseDragLayer<Launcher> { 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 e8ff8da058..1ff433541e 100644 --- a/src/com/android/launcher3/dragndrop/DragOptions.java +++ b/src/com/android/launcher3/dragndrop/DragOptions.java @@ -40,6 +40,12 @@ public class DragOptions { /** 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 0264ae21be..f54d05dbdc 100644 --- a/src/com/android/launcher3/dragndrop/DragView.java +++ b/src/com/android/launcher3/dragndrop/DragView.java @@ -363,7 +363,10 @@ public abstract class DragView<T extends Context & ActivityContext> extends Fram // If the content is already removed, ignore return; } - View newContent = getViewFromDrawable(getContext(), crossFadeDrawable); + ImageView newContent = getViewFromDrawable(getContext(), crossFadeDrawable); + // We need to fill the ImageView with the content, otherwise the shapes of the final view + // and the drag view might not match exactly + newContent.setScaleType(ImageView.ScaleType.FIT_XY); newContent.measure(makeMeasureSpec(mWidth, EXACTLY), makeMeasureSpec(mHeight, EXACTLY)); newContent.layout(0, 0, mWidth, mHeight); addViewInLayout(newContent, 0, new LayoutParams(mWidth, mHeight)); @@ -565,7 +568,8 @@ public abstract class DragView<T extends Context & ActivityContext> extends Fram .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) { @@ -573,7 +577,7 @@ public abstract class DragView<T extends Context & ActivityContext> extends Fram } } - private static View getViewFromDrawable(Context context, Drawable drawable) { + private static ImageView getViewFromDrawable(Context context, Drawable drawable) { ImageView iv = new ImageView(context); iv.setImageDrawable(drawable); return iv; diff --git a/src/com/android/launcher3/dragndrop/LauncherDragController.java b/src/com/android/launcher3/dragndrop/LauncherDragController.java index dcbfa502e6..75f4ad687e 100644 --- a/src/com/android/launcher3/dragndrop/LauncherDragController.java +++ b/src/com/android/launcher3/dragndrop/LauncherDragController.java @@ -37,7 +37,7 @@ import com.android.launcher3.Launcher; import com.android.launcher3.R; import com.android.launcher3.accessibility.DragViewStateAnnouncer; import com.android.launcher3.model.data.ItemInfo; -import com.android.launcher3.testing.TestProtocol; +import com.android.launcher3.testing.shared.TestProtocol; /** * Drag controller for Launcher activity diff --git a/src/com/android/launcher3/dragndrop/SpringLoadedDragController.java b/src/com/android/launcher3/dragndrop/SpringLoadedDragController.java index fb8a1bc99e..08e50dd96d 100644 --- a/src/com/android/launcher3/dragndrop/SpringLoadedDragController.java +++ b/src/com/android/launcher3/dragndrop/SpringLoadedDragController.java @@ -20,12 +20,14 @@ import com.android.launcher3.Alarm; import com.android.launcher3.CellLayout; import com.android.launcher3.Launcher; import com.android.launcher3.OnAlarmListener; +import com.android.launcher3.Utilities; import com.android.launcher3.Workspace; public class SpringLoadedDragController implements OnAlarmListener { // how long the user must hover over a mini-screen before it unshrinks - final long ENTER_SPRING_LOAD_HOVER_TIME = 500; - final long ENTER_SPRING_LOAD_CANCEL_HOVER_TIME = 950; + private static final long ENTER_SPRING_LOAD_HOVER_TIME = 500; + private static final long ENTER_SPRING_LOAD_HOVER_TIME_IN_TEST = 1500; + private static final long ENTER_SPRING_LOAD_CANCEL_HOVER_TIME = 950; Alarm mAlarm; @@ -39,6 +41,13 @@ public class SpringLoadedDragController implements OnAlarmListener { mAlarm.setOnAlarmListener(this); } + private long getEnterSpringLoadHoverTime() { + // Some TAPL tests are flaky on Cuttlefish with a low waiting time + return Utilities.IS_RUNNING_IN_TEST_HARNESS + ? ENTER_SPRING_LOAD_HOVER_TIME_IN_TEST + : ENTER_SPRING_LOAD_HOVER_TIME; + } + public void cancel() { mAlarm.cancelAlarm(); } @@ -46,8 +55,8 @@ public class SpringLoadedDragController implements OnAlarmListener { // Set a new alarm to expire for the screen that we are hovering over now public void setAlarm(CellLayout cl) { mAlarm.cancelAlarm(); - mAlarm.setAlarm((cl == null) ? ENTER_SPRING_LOAD_CANCEL_HOVER_TIME : - ENTER_SPRING_LOAD_HOVER_TIME); + mAlarm.setAlarm((cl == null) ? ENTER_SPRING_LOAD_CANCEL_HOVER_TIME + : getEnterSpringLoadHoverTime()); mScreen = cl; } diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java index e68ebdb6e5..94eea3598e 100644 --- a/src/com/android/launcher3/folder/Folder.java +++ b/src/com/android/launcher3/folder/Folder.java @@ -282,7 +282,6 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo 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 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo 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 class Folder extends AbstractFloatingView implements ClipPathView, DragSo public void startEditingFolderName() { post(() -> { - if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) { - showLabelSuggestions(); - } + showLabelSuggestions(); mFolderName.setHint(""); mIsEditingName = true; }); @@ -459,6 +456,13 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo // the folder itself. requestFocus(); super.onAttachedToWindow(); + mFolderName.addOnFocusChangeListener(this); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + mFolderName.removeOnFocusChangeListener(this); } @Override @@ -1080,8 +1084,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo 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 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo 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 61ffd9d00c..05ad57acd4 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.R; 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 @@ public class FolderAnimationManager { 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 @@ public class FolderAnimationManager { final int footerStartDelay; if (isLargeFolder()) { if (mIsOpening) { + mFolder.mFooter.setAlpha(0); footerAlphaDuration = LARGE_FOLDER_FOOTER_DURATION; footerStartDelay = mDuration - footerAlphaDuration; } else { @@ -232,9 +236,9 @@ public class FolderAnimationManager { 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 @@ public class FolderAnimationManager { 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 @@ public class FolderAnimationManager { 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 b1e27019c3..dd00f07807 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.Utilities; import com.android.launcher3.Workspace; import com.android.launcher3.allapps.ActivityAllAppsContainerView; import com.android.launcher3.anim.Interpolators; -import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.celllayout.CellLayoutLayoutParams; import com.android.launcher3.dot.FolderDotInfo; import com.android.launcher3.dragndrop.BaseItemDragListener; import com.android.launcher3.dragndrop.DragLayer; @@ -278,7 +278,7 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel public void onDragEnter(ItemInfo dragInfo) { if (mFolder.isDestroyed() || !willAcceptItem(dragInfo)) return; - CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams(); + CellLayoutLayoutParams lp = (CellLayoutLayoutParams) getLayoutParams(); CellLayout cl = (CellLayout) getParent().getParent(); mBackground.animateToAccept(cl, lp.cellX, lp.cellY); @@ -418,35 +418,23 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel 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 class FolderIcon extends FrameLayout implements FolderListener, IconLabel public void drawDot(Canvas canvas) { if (!mForceHideDot && ((mDotInfo != null && mDotInfo.hasDot()) || mDotScale > 0)) { Rect iconBounds = mDotParams.iconBounds; - - Utilities.setRectToViewCenter(this, mActivity.getDeviceProfile().iconSizePx, - iconBounds); - iconBounds.offsetTo(iconBounds.left, getPaddingTop()); - float iconScale = (float) mBackground.previewSize / iconBounds.width(); + // 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; + + 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 502164473f..bf5959442d 100644 --- a/src/com/android/launcher3/folder/FolderNameProvider.java +++ b/src/com/android/launcher3/folder/FolderNameProvider.java @@ -24,6 +24,7 @@ import android.os.UserHandle; 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 @@ public class FolderNameProvider implements ResourceBasedOverride { 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 3d5aef5f0b..141388f3c5 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.PagedView; import com.android.launcher3.R; import com.android.launcher3.ShortcutAndWidgetContainer; import com.android.launcher3.Utilities; +import com.android.launcher3.celllayout.CellLayoutLayoutParams; import com.android.launcher3.keyboard.ViewGroupFocusHelper; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; @@ -112,6 +113,7 @@ public class FolderPagedView extends PagedView<PageIndicatorDots> implements Cli public void setFolder(Folder folder) { mFolder = folder; mPageIndicator = folder.findViewById(R.id.folder_page_indicator); + mPageIndicator.setShouldAutoHide(false); initParentViews(folder); } @@ -202,7 +204,7 @@ public class FolderPagedView extends PagedView<PageIndicatorDots> implements Cli 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,10 +220,10 @@ public class FolderPagedView extends PagedView<PageIndicatorDots> implements Cli 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; @@ -314,7 +316,7 @@ public class FolderPagedView extends PagedView<PageIndicatorDots> implements Cli } 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 +365,7 @@ public class FolderPagedView extends PagedView<PageIndicatorDots> implements Cli 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/folder/PreviewItemManager.java b/src/com/android/launcher3/folder/PreviewItemManager.java index 6355b62e27..2e5f2e5d60 100644 --- a/src/com/android/launcher3/folder/PreviewItemManager.java +++ b/src/com/android/launcher3/folder/PreviewItemManager.java @@ -79,6 +79,8 @@ public class PreviewItemManager { private int mPrevTopPadding = -1; private Drawable mReferenceDrawable = null; + private int mNumOfPrevItems = 0; + // These hold the first page preview items private ArrayList<PreviewItemDrawingParams> mFirstPageParams = new ArrayList<>(); // These hold the current page preview items. It is empty if the current page is the first page. @@ -254,7 +256,6 @@ public class PreviewItemManager { void buildParamsForPage(int page, ArrayList<PreviewItemDrawingParams> params, boolean animate) { List<WorkspaceItemInfo> items = mIcon.getPreviewItemsOnPage(page); - int prevNumItems = params.size(); // We adjust the size of the list to match the number of items in the preview. while (items.size() < params.size()) { @@ -278,8 +279,9 @@ public class PreviewItemManager { mReferenceDrawable = p.drawable; } } else { - FolderPreviewItemAnim anim = new FolderPreviewItemAnim(this, p, i, prevNumItems, i, - numItemsInFirstPagePreview, DROP_IN_ANIMATION_DURATION, null); + FolderPreviewItemAnim anim = new FolderPreviewItemAnim(this, p, i, + mNumOfPrevItems, i, numItemsInFirstPagePreview, DROP_IN_ANIMATION_DURATION, + null); if (p.anim != null) { if (p.anim.hasEqualFinalState(anim)) { @@ -318,7 +320,9 @@ public class PreviewItemManager { } void updatePreviewItems(boolean animate) { + int numOfPrevItemsAux = mFirstPageParams.size(); buildParamsForPage(0, mFirstPageParams, animate); + mNumOfPrevItems = numOfPrevItemsAux; } void updatePreviewItems(Predicate<WorkspaceItemInfo> itemCheck) { diff --git a/src/com/android/launcher3/graphics/DragPreviewProvider.java b/src/com/android/launcher3/graphics/DragPreviewProvider.java index f027b33f22..7457f3057c 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.util.SafeCloseable; 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 class DragPreviewProvider { public final int blurSizeOutline; - private OutlineGeneratorCallback mOutlineGeneratorCallback; - public Bitmap generatedDragOutline; - public DragPreviewProvider(View view) { this(view, view.getContext()); } @@ -129,15 +116,6 @@ public class DragPreviewProvider { 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 @@ public class DragPreviewProvider { 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 fc8d855801..feadafaf50 100644 --- a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java +++ b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java @@ -1,6 +1,6 @@ package com.android.launcher3.graphics; -import static com.android.launcher3.Utilities.getPrefs; +import static com.android.launcher3.LauncherPrefs.getPrefs; 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; @@ -26,7 +26,6 @@ import android.util.Log; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.InvariantDeviceProfile.GridOption; import com.android.launcher3.Utilities; -import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.util.Executors; /** @@ -143,11 +142,9 @@ public class GridCustomizationsProvider extends ContentProvider { } case ICON_THEMED: case SET_ICON_THEMED: { - if (FeatureFlags.ENABLE_THEMED_ICONS.get()) { - getPrefs(getContext()).edit() - .putBoolean(KEY_THEMED_ICONS, values.getAsBoolean(BOOLEAN_VALUE)) - .apply(); - } + getPrefs(getContext()).edit() + .putBoolean(KEY_THEMED_ICONS, values.getAsBoolean(BOOLEAN_VALUE)) + .apply(); return 1; } default: diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java index d5bcb0cbcb..e7b0446741 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.EXACTLY; 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.ContextWrapper; import android.content.Intent; import android.content.res.TypedArray; import android.graphics.Color; +import android.graphics.PointF; import android.graphics.Rect; import android.graphics.drawable.AdaptiveIconDrawable; import android.graphics.drawable.ColorDrawable; @@ -43,6 +45,8 @@ import android.os.Build; import android.os.Handler; import android.os.Looper; import android.util.AttributeSet; +import android.util.Size; +import android.util.SparseArray; import android.util.SparseIntArray; import android.view.ContextThemeWrapper; import android.view.LayoutInflater; @@ -53,9 +57,13 @@ import android.view.WindowInsets; 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; @@ -65,6 +73,7 @@ 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; @@ -91,11 +100,12 @@ import com.android.launcher3.util.window.WindowManagerProxy; 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; @@ -165,10 +175,12 @@ public class LauncherPreviewRenderer extends ContextWrapper } } + 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; @@ -177,16 +189,27 @@ public class LauncherPreviewRenderer extends ContextWrapper private final Map<Integer, CellLayout> mWorkspaceScreens = new HashMap<>(); private final AppWidgetHost mAppWidgetHost; private final SparseIntArray mWallpaperColorResources; + private final SparseArray<Size> mLauncherWidgetSpanInfo; public LauncherPreviewRenderer(Context context, InvariantDeviceProfile idp, - WallpaperColors wallpaperColorsOverride) { + WallpaperColors wallpaperColorsOverride, + @Nullable final SparseArray<Size> launcherWidgetSpanInfo) { super(context); 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(); @@ -224,6 +247,9 @@ public class LauncherPreviewRenderer extends ContextWrapper mHotseat = mRootView.findViewById(R.id.hotseat); mHotseat.resetLayout(false); + mLauncherWidgetSpanInfo = launcherWidgetSpanInfo == null ? new SparseArray<>() : + launcherWidgetSpanInfo; + CellLayout firstScreen = mRootView.findViewById(R.id.workspace); firstScreen.setPadding(mDp.workspacePadding.left + mDp.cellLayoutPaddingPx.left, mDp.workspacePadding.top + mDp.cellLayoutPaddingPx.top, @@ -307,6 +333,11 @@ public class LauncherPreviewRenderer extends ContextWrapper } @Override + public List<OnDeviceProfileChangeListener> getOnDeviceProfileChangeListeners() { + return mDpChangeListeners; + } + + @Override public Hotseat getHotseat() { return mHotseat; } @@ -381,6 +412,41 @@ public class LauncherPreviewRenderer extends ContextWrapper 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); @@ -474,8 +540,8 @@ public class LauncherPreviewRenderer extends ContextWrapper 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); } @@ -495,7 +561,7 @@ public class LauncherPreviewRenderer extends ContextWrapper 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 d2e4c511f9..de47cb5057 100644 --- a/src/com/android/launcher3/graphics/PreloadIconDrawable.java +++ b/src/com/android/launcher3/graphics/PreloadIconDrawable.java @@ -17,35 +17,43 @@ 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.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.util.Pair; +import android.os.SystemClock; import android.util.Property; -import android.util.SparseArray; -import android.view.ContextThemeWrapper; +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.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.lang.ref.WeakReference; +import java.util.WeakHashMap; +import java.util.function.Function; /** * Extension of {@link FastBitmapDrawable} which shows a progress bar around the icon. */ -public class PreloadIconDrawable extends FastBitmapDrawable { +public class PreloadIconDrawable extends FastBitmapDrawable implements Runnable { private static final Property<PreloadIconDrawable, Float> INTERNAL_STATE = new Property<PreloadIconDrawable, Float>(Float.TYPE, "internalStateProgress") { @@ -61,27 +69,31 @@ public class PreloadIconDrawable extends FastBitmapDrawable { }; private static final int DEFAULT_PATH_SIZE = 100; - private static final float PROGRESS_WIDTH = 7; - private static final float PROGRESS_GAP = 2; private static final int MAX_PAINT_ALPHA = 255; + private static final int TRACK_ALPHA = (int) (0.27f * MAX_PAINT_ALPHA); + 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 int COLOR_TRACK = 0x77EEEEEE; - private static final int COLOR_SHADOW = 0x55000000; - - private static final float SMALL_SCALE = 0.6f; - - private static final SparseArray<WeakReference<Pair<Path, Bitmap>>> sShadowCache = - new SparseArray<>(); + private static final float SMALL_SCALE = ENABLE_DOWNLOAD_APP_UX_V2.get() ? 0.867f : 0.7f; + private static final float PROGRESS_STROKE_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 float OVERLAY_ALPHA_RANGE = 127.5f; + 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(); @@ -94,7 +106,6 @@ public class PreloadIconDrawable extends FastBitmapDrawable { private final Path mScaledProgressPath; private final Paint mProgressPaint; - private Bitmap mShadowBitmap; private final int mIndicatorColor; private final int mSystemAccentColor; private final int mSystemBackgroundColor; @@ -102,10 +113,14 @@ public class PreloadIconDrawable extends FastBitmapDrawable { private int mTrackAlpha; private float mTrackLength; - private float mIconScale; private boolean mRanFinishAnimation; + private final int mRefreshRateMillis; + private final AnimatedFloat mIconScale = new AnimatedFloat(this::invalidateSelf); + private final AnimatedFloat mOverlayAlpha = new AnimatedFloat(this::updateOverlayAlpha); + private boolean mShouldAnimateScaleAndAlpha; + // 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; @@ -119,14 +134,16 @@ public class PreloadIconDrawable extends FastBitmapDrawable { 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); @@ -134,13 +151,19 @@ public class PreloadIconDrawable extends FastBitmapDrawable { mScaledProgressPath = new Path(); mProgressPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); - mProgressPaint.setStyle(Paint.Style.STROKE); mProgressPaint.setStrokeCap(Paint.Cap.ROUND); mIndicatorColor = indicatorColor; 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. + if (ENABLE_DOWNLOAD_APP_UX_V2.get() && info.getProgressLevel() == 0) { + mShouldAnimateScaleAndAlpha = true; + mOverlayAlpha.updateValue(127); + } setLevel(info.getProgressLevel()); setIsStartable(info.isAppStartable()); @@ -149,47 +172,22 @@ public class PreloadIconDrawable extends FastBitmapDrawable { @Override protected void onBoundsChange(Rect bounds) { super.onBoundsChange(bounds); + + float progressWidth = PROGRESS_STROKE_SCALE * bounds.width(); mTmpMatrix.setScale( - (bounds.width() - 2 * PROGRESS_WIDTH - 2 * PROGRESS_GAP) / DEFAULT_PATH_SIZE, - (bounds.height() - 2 * PROGRESS_WIDTH - 2 * PROGRESS_GAP) / DEFAULT_PATH_SIZE); - mTmpMatrix.postTranslate( - bounds.left + PROGRESS_WIDTH + PROGRESS_GAP, - bounds.top + PROGRESS_WIDTH + PROGRESS_GAP); + (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); - float scale = bounds.width() / DEFAULT_PATH_SIZE; - mProgressPaint.setStrokeWidth(PROGRESS_WIDTH * scale); + mProgressPaint.setStrokeWidth(progressWidth); - mShadowBitmap = getShadowBitmap(bounds.width(), bounds.height(), - (PROGRESS_GAP ) * scale); mPathMeasure.setPath(mScaledTrackPath, true); mTrackLength = mPathMeasure.getLength(); setInternalProgress(mInternalStateProgress); } - private Bitmap getShadowBitmap(int width, int height, float shadowRadius) { - int key = ((width << 16) | height) * (mIsDarkMode ? -1 : 1); - WeakReference<Pair<Path, Bitmap>> shadowRef = sShadowCache.get(key); - Pair<Path, Bitmap> cache = shadowRef != null ? shadowRef.get() : null; - Bitmap shadow = cache != null && cache.first.equals(mShapePath) ? cache.second : null; - if (shadow != null) { - return shadow; - } - shadow = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); - Canvas c = new Canvas(shadow); - mProgressPaint.setShadowLayer(shadowRadius, 0, 0, mIsStartable - ? COLOR_SHADOW : mSystemAccentColor); - mProgressPaint.setColor(mIsStartable ? COLOR_TRACK : mSystemBackgroundColor); - mProgressPaint.setAlpha(MAX_PAINT_ALPHA); - c.drawPath(mScaledTrackPath, mProgressPaint); - mProgressPaint.clearShadowLayer(); - c.setBitmap(null); - - sShadowCache.put(key, new WeakReference<>(Pair.create(mShapePath, shadow))); - return shadow; - } - @Override public void drawInternal(Canvas canvas, Rect bounds) { if (mRanFinishAnimation) { @@ -197,18 +195,35 @@ public class PreloadIconDrawable extends FastBitmapDrawable { return; } - // Draw track. + // 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); - if (mShadowBitmap != null) { - canvas.drawBitmap(mShadowBitmap, bounds.left, bounds.top, mProgressPaint); - } canvas.drawPath(mScaledProgressPath, mProgressPaint); int saveCount = canvas.save(); - canvas.scale(mIconScale, mIconScale, bounds.exactCenterX(), bounds.exactCenterY()); + canvas.scale( + mIconScale.value, mIconScale.value, bounds.exactCenterX(), bounds.exactCenterY()); super.drawInternal(canvas, bounds); canvas.restoreToCount(saveCount); + + if (ENABLE_DOWNLOAD_APP_UX_V2.get() && mInternalStateProgress == 0) { + reschedule(); + } + } + + @Override + protected void updateFilter() { + if (!ENABLE_DOWNLOAD_APP_UX_V2.get()) { + setAlpha(mIsDisabled ? DISABLED_ICON_ALPHA : MAX_PAINT_ALPHA); + } } /** @@ -263,7 +278,7 @@ public class PreloadIconDrawable extends FastBitmapDrawable { mCurrentAnim = ObjectAnimator.ofFloat(this, INTERNAL_STATE, finalProgress); mCurrentAnim.setDuration( (long) ((finalProgress - mInternalStateProgress) * DURATION_SCALE)); - mCurrentAnim.setInterpolator(Interpolators.LINEAR); + mCurrentAnim.setInterpolator(LINEAR); if (isFinish) { mCurrentAnim.addListener(new AnimatorListenerAdapter() { @Override @@ -279,13 +294,13 @@ public class PreloadIconDrawable extends FastBitmapDrawable { /** * 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) @@ -297,46 +312,67 @@ public class PreloadIconDrawable extends FastBitmapDrawable { * - only icon is drawn in normal state */ private void setInternalProgress(float progress) { + // Animate scale and alpha from pending to downloading state. + if (ENABLE_DOWNLOAD_APP_UX_V2.get() + && mShouldAnimateScaleAndAlpha && mInternalStateProgress == 0 && progress > 0) { + Animator iconScaleAnimator = mIconScale.animateToValue(SMALL_SCALE); + iconScaleAnimator.setDuration(SCALE_AND_ALPHA_ANIM_DURATION); + iconScaleAnimator.setInterpolator(EMPHASIZED); + iconScaleAnimator.start(); + + Animator overlayAlphaAnimator = mOverlayAlpha.animateToValue(0); + overlayAlphaAnimator.setDuration(SCALE_AND_ALPHA_ANIM_DURATION); + overlayAlphaAnimator.setInterpolator(EMPHASIZED); + overlayAlphaAnimator.start(); + } + mInternalStateProgress = progress; if (progress <= 0) { - mIconScale = SMALL_SCALE; + mIconScale.updateValue(ENABLE_DOWNLOAD_APP_UX_V2.get() ? 1 : SMALL_SCALE); mScaledTrackPath.reset(); mTrackAlpha = MAX_PAINT_ALPHA; - } - - if (progress < 1 && progress > 0) { + } else if (progress < 1) { mPathMeasure.getSegment(0, progress * mTrackLength, mScaledProgressPath, true); - mIconScale = SMALL_SCALE; + if (ENABLE_DOWNLOAD_APP_UX_V2.get()) { + mPathMeasure.getSegment(0, mTrackLength, mScaledTrackPath, true); + } + + if (!ENABLE_DOWNLOAD_APP_UX_V2.get() || !mShouldAnimateScaleAndAlpha) { + mIconScale.updateValue(SMALL_SCALE); + } mTrackAlpha = MAX_PAINT_ALPHA; - } else if (progress >= 1) { + } else { setIsDisabled(mItem.isDisabled()); mScaledTrackPath.set(mScaledProgressPath); float fraction = (progress - 1) / COMPLETE_ANIM_FRACTION; if (fraction >= 1) { // Animation has completed - mIconScale = 1; + mIconScale.updateValue(1); mTrackAlpha = 0; } else { mTrackAlpha = Math.round((1 - fraction) * MAX_PAINT_ALPHA); - mIconScale = SMALL_SCALE + (1 - SMALL_SCALE) * fraction; + mIconScale.updateValue(SMALL_SCALE + (1 - SMALL_SCALE) * fraction); } } invalidateSelf(); } private static int[] getPreloadColors(Context context) { - Context dayNightThemeContext = new ContextThemeWrapper( - context, android.R.style.Theme_DeviceDefault_DayNight); int[] preloadColors = new int[2]; - preloadColors[PRELOAD_ACCENT_COLOR_INDEX] = Themes.getColorAccent(dayNightThemeContext); - preloadColors[PRELOAD_BACKGROUND_COLOR_INDEX] = Themes.getColorBackgroundFloating( - dayNightThemeContext); + preloadColors[PRELOAD_ACCENT_COLOR_INDEX] = Themes.getAttrColor(context, + R.attr.preloadIconAccentColor); + preloadColors[PRELOAD_BACKGROUND_COLOR_INDEX] = Themes.getAttrColor(context, + R.attr.preloadIconBackgroundColor); return preloadColors; } + private static int getRefreshRateMillis(Context context) { + return RefreshRateTracker.getSingleFrameMs(context); + } + /** * Returns a FastBitmapDrawable with the icon. */ @@ -352,7 +388,76 @@ public class PreloadIconDrawable extends FastBitmapDrawable { mItem, mIndicatorColor, new int[] {mSystemAccentColor, mSystemBackgroundColor}, - mIsDarkMode); + mIsDarkMode, + mRefreshRateMillis); + } + + @Override + public void run() { + if (!ENABLE_DOWNLOAD_APP_UX_V2.get() || mInternalStateProgress > 0) { + return; + } + if (!applyPendingIconOverlay()) { + reschedule(); + } + } + + @Override + public boolean setVisible(boolean visible, boolean restart) { + boolean result = super.setVisible(visible, restart); + if (visible) { + reschedule(); + } else { + unscheduleSelf(this); + } + return result; + } + + private void reschedule() { + unscheduleSelf(this); + + if (!isVisible()) { + return; + } + + final long upTime = SystemClock.uptimeMillis(); + scheduleSelf(this, upTime - ((upTime % mRefreshRateMillis)) + mRefreshRateMillis); + } + + + /** + * Apply an overlay on the pending icon with cascading motion based on its position. + * Returns {@code true} if the icon alpha is updated, so that we re-draw. + */ + private boolean applyPendingIconOverlay() { + long waveMotionDelay = (mItem.cellX * WAVE_MOTION_DELAY_FACTOR_MILLIS) + + (mItem.cellY * WAVE_MOTION_DELAY_FACTOR_MILLIS); + long time = SystemClock.uptimeMillis(); + float newAlpha = Utilities.mapBoundToRange( + (float) (time + waveMotionDelay) % ALPHA_DURATION_MILLIS, + 0, + ALPHA_DURATION_MILLIS, + 0, + MAX_PAINT_ALPHA, + LINEAR); + if (newAlpha > OVERLAY_ALPHA_RANGE) { + newAlpha = (OVERLAY_ALPHA_RANGE - (newAlpha % OVERLAY_ALPHA_RANGE)); + } + + boolean invalidate = false; + if ((int) mOverlayAlpha.value != newAlpha) { + mOverlayAlpha.updateValue(newAlpha); + invalidate = true; + } + return invalidate; + } + + private void updateOverlayAlpha() { + int overlayColor = mIsDarkMode ? 0 : 255; + int currArgb = + Color.argb((int) mOverlayAlpha.value, overlayColor, overlayColor, overlayColor); + mPaint.setColorFilter(COLOR_FILTER_MAP.computeIfAbsent(currArgb, FILTER_FACTORY)); + invalidateSelf(); } protected static class PreloadIconConstantState extends FastBitmapConstantState { @@ -362,6 +467,7 @@ public class PreloadIconDrawable extends FastBitmapDrawable { protected final int[] mPreloadColors; protected final boolean mIsDarkMode; protected final int mLevel; + protected final int mRefreshRateMillis; public PreloadIconConstantState( Bitmap bitmap, @@ -369,13 +475,15 @@ public class PreloadIconDrawable extends FastBitmapDrawable { 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 @@ -384,7 +492,8 @@ public class PreloadIconDrawable extends FastBitmapDrawable { 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 fd11b37795..85c0a7a565 100644 --- a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java +++ b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java @@ -22,10 +22,13 @@ import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; import android.app.WallpaperColors; import android.appwidget.AppWidgetProviderInfo; import android.content.Context; +import android.database.Cursor; import android.hardware.display.DisplayManager; import android.os.Bundle; import android.os.IBinder; import android.util.Log; +import android.util.Size; +import android.util.SparseArray; import android.view.ContextThemeWrapper; import android.view.Display; import android.view.SurfaceControlViewHost; @@ -34,6 +37,8 @@ import android.view.View; import android.view.WindowManager.LayoutParams; import android.view.animation.AccelerateDecelerateInterpolator; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.annotation.UiThread; import androidx.annotation.WorkerThread; @@ -45,7 +50,7 @@ import com.android.launcher3.Utilities; 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; @@ -124,6 +129,45 @@ public class PreviewSurfaceRenderer { } /** + * A function that queries for the launcher app widget span info + * + * @param context The context to get the content resolver from, should be related to launcher + * @return A SparseArray with the app widget id being the key and the span info being the values + */ + @WorkerThread + @Nullable + public SparseArray<Size> getLoadedLauncherWidgetInfo( + @NonNull final Context context) { + final SparseArray<Size> widgetInfo = new SparseArray<>(); + final String query = LauncherSettings.Favorites.ITEM_TYPE + " = " + + LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET; + + try (Cursor c = context.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI, + new String[] { + LauncherSettings.Favorites.APPWIDGET_ID, + LauncherSettings.Favorites.SPANX, + LauncherSettings.Favorites.SPANY + }, query, null, null)) { + final int appWidgetIdIndex = c.getColumnIndexOrThrow( + LauncherSettings.Favorites.APPWIDGET_ID); + final int spanXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANX); + final int spanYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANY); + while (c.moveToNext()) { + final int appWidgetId = c.getInt(appWidgetIdIndex); + final int spanX = c.getInt(spanXIndex); + final int spanY = c.getInt(spanYIndex); + + widgetInfo.append(appWidgetId, new Size(spanX, spanY)); + } + } catch (Exception e) { + Log.e(TAG, "Error querying for launcher widget info", e); + return null; + } + + return widgetInfo; + } + + /** * Generates the preview in background */ public void loadAsync() { @@ -174,8 +218,11 @@ public class PreviewSurfaceRenderer { loadWorkspace(new ArrayList<>(), LauncherSettings.Favorites.PREVIEW_CONTENT_URI, query); + final SparseArray<Size> spanInfo = + getLoadedLauncherWidgetInfo(previewContext.getBaseContext()); + MAIN_EXECUTOR.execute(() -> { - renderView(previewContext, mBgDataModel, mWidgetProvidersMap); + renderView(previewContext, mBgDataModel, mWidgetProvidersMap, spanInfo); mOnDestroyCallbacks.add(previewContext::onDestroy); }); } @@ -183,7 +230,8 @@ public class PreviewSurfaceRenderer { } else { LauncherAppState.getInstance(inflationContext).getModel().loadAsync(dataModel -> { if (dataModel != null) { - MAIN_EXECUTOR.execute(() -> renderView(inflationContext, dataModel, null)); + MAIN_EXECUTOR.execute(() -> renderView(inflationContext, dataModel, null, + null)); } else { Log.e(TAG, "Model loading failed"); } @@ -193,20 +241,21 @@ public class PreviewSurfaceRenderer { @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 private void renderView(Context inflationContext, BgDataModel dataModel, - Map<ComponentKey, AppWidgetProviderInfo> widgetProviderInfoMap) { + Map<ComponentKey, AppWidgetProviderInfo> widgetProviderInfoMap, + @Nullable final SparseArray<Size> launcherWidgetSpanInfo) { if (mDestroyed) { return; } - View view = new LauncherPreviewRenderer(inflationContext, mIdp, mWallpaperColors) - .getRenderedView(dataModel, widgetProviderInfoMap); + View view = new LauncherPreviewRenderer(inflationContext, mIdp, mWallpaperColors, + launcherWidgetSpanInfo).getRenderedView(dataModel, widgetProviderInfoMap); // This aspect scales the view to fit in the surface and centers it final float scale = Math.min(mWidth / (float) view.getMeasuredWidth(), mHeight / (float) view.getMeasuredHeight()); diff --git a/src/com/android/launcher3/graphics/ShadowDrawable.java b/src/com/android/launcher3/graphics/ShadowDrawable.java deleted file mode 100644 index d8a7070f29..0000000000 --- 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 f0766c50c7..185b8d3f44 100644 --- a/src/com/android/launcher3/graphics/SysUiScrim.java +++ b/src/com/android/launcher3/graphics/SysUiScrim.java @@ -43,7 +43,7 @@ import android.view.WindowInsets; import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.R; -import com.android.launcher3.ResourceUtils; +import com.android.launcher3.testing.shared.ResourceUtils; import com.android.launcher3.Utilities; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.util.DynamicResource; diff --git a/src/com/android/launcher3/icons/ComponentWithLabel.java b/src/com/android/launcher3/icons/ComponentWithLabel.java index 372c59166e..30575fcbe5 100644 --- a/src/com/android/launcher3/icons/ComponentWithLabel.java +++ b/src/com/android/launcher3/icons/ComponentWithLabel.java @@ -20,6 +20,8 @@ import android.content.Context; 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 @@ public interface ComponentWithLabel { } @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 c8606b1002..0a52dd7191 100644 --- a/src/com/android/launcher3/icons/ComponentWithLabelAndIcon.java +++ b/src/com/android/launcher3/icons/ComponentWithLabelAndIcon.java @@ -41,7 +41,8 @@ public interface ComponentWithLabelAndIcon extends ComponentWithLabel { @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 fe9b633b66..0b4a4a53d4 100644 --- a/src/com/android/launcher3/icons/IconCache.java +++ b/src/com/android/launcher3/icons/IconCache.java @@ -51,7 +51,6 @@ import androidx.core.util.Pair; 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 @@ public class IconCache extends BaseIconCache { } @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 @@ public class IconCache extends BaseIconCache { /** * 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 class IconCache extends BaseIconCache { */ 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 @@ public class IconCache extends BaseIconCache { * 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 @@ public class IconCache extends BaseIconCache { 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 @@ public class IconCache extends BaseIconCache { } @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 4b8c1ad590..406f69758c 100644 --- a/src/com/android/launcher3/icons/LauncherActivityCachingLogic.java +++ b/src/com/android/launcher3/icons/LauncherActivityCachingLogic.java @@ -20,6 +20,8 @@ import android.content.Context; 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 @@ public class LauncherActivityCachingLogic 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 5508c49410..57fa8a256b 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 @@ public class LauncherIcons extends BaseIconFactory implements AutoCloseable { 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 @@ public class LauncherIcons extends BaseIconFactory implements AutoCloseable { } @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 0000000000..511dcc736e --- /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 6a8f34a93d..bb7248fea7 100644 --- a/src/com/android/launcher3/icons/ShortcutCachingLogic.java +++ b/src/com/android/launcher3/icons/ShortcutCachingLogic.java @@ -29,9 +29,10 @@ import android.text.TextUtils; 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 @@ public class ShortcutCachingLogic implements CachingLogic<ShortcutInfo> { 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 3ecfb23c2c..0000000000 --- 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 3c4a644053..5bbe07c9ad 100644 --- a/src/com/android/launcher3/logging/InstanceId.java +++ b/src/com/android/launcher3/logging/InstanceId.java @@ -47,7 +47,6 @@ public final class InstanceId implements Parcelable { 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 0000000000..6dc0a0be22 --- /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 c4ec4e36fd..2159c6b68f 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.ContainerInfo; 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 @@ public class StatsLogManager implements ResourceBasedOverride { private InstanceId mInstanceId; protected @Nullable ActivityContext mActivityContext = null; + private KeyboardStateManager mKeyboardStateManager; /** * Returns event enum based on the two state transition information when swipe @@ -554,6 +558,20 @@ public class StatsLogManager implements ResourceBasedOverride { + "result page etc.") LAUNCHER_ALLAPPS_SCROLLED(985), + @UiEvent(doc = "User scrolled up on one of the all apps surfaces such as A-Z list, search " + + "result page etc.") + LAUNCHER_ALLAPPS_SCROLLED_UP(1229), + + @UiEvent(doc = + "User scrolled down on one of the all apps surfaces such as A-Z list, search " + + "result page etc.") + LAUNCHER_ALLAPPS_SCROLLED_DOWN(1230), + + @UiEvent(doc = "User scrolled on one of the all apps surfaces such as A-Z list, search " + + "result page etc and we don't know the direction since user came back to " + + "original position from which they scrolled.") + LAUNCHER_ALLAPPS_SCROLLED_UNKNOWN_DIRECTION(1231), + @UiEvent(doc = "User tapped taskbar home button") LAUNCHER_TASKBAR_HOME_BUTTON_TAP(1003), @@ -592,6 +610,27 @@ public class StatsLogManager implements ResourceBasedOverride { @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 swiping down") + LAUNCHER_WORK_FAB_BUTTON_COLLAPSE(1276), + + @UiEvent(doc = "User has collapsed the work FAB button by swiping up") + LAUNCHER_WORK_FAB_BUTTON_EXTEND(1277), ; // ADD MORE @@ -713,6 +752,13 @@ public class StatsLogManager implements ResourceBasedOverride { } /** + * 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 +783,10 @@ public class StatsLogManager implements ResourceBasedOverride { 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 +796,6 @@ public class StatsLogManager implements ResourceBasedOverride { public int getId() { return mId; } - } /** @@ -781,6 +828,13 @@ public class StatsLogManager implements ResourceBasedOverride { } /** + * Sets sub event type. + */ + default StatsLatencyLogger withSubEventType(int type) { + return this; + } + + /** * Sets packageId of log message. */ default StatsLatencyLogger withPackageId(int packageId) { @@ -795,6 +849,77 @@ public class StatsLogManager implements ResourceBasedOverride { } /** + * 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 +941,27 @@ public class StatsLogManager implements ResourceBasedOverride { 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 +972,11 @@ public class StatsLogManager implements ResourceBasedOverride { }; } + 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 31ef2e5ea6..0d978e1b23 100644 --- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java +++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java @@ -23,6 +23,9 @@ import android.os.UserHandle; 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; @@ -36,12 +39,13 @@ import com.android.launcher3.model.data.WorkspaceItemFactory; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.pm.InstallSessionHelper; import com.android.launcher3.pm.PackageInstallInfo; -import com.android.launcher3.testing.TestProtocol; +import com.android.launcher3.testing.shared.TestProtocol; import com.android.launcher3.util.IntArray; import com.android.launcher3.util.PackageManagerHelper; import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** * Task to add auto-created workspace items. @@ -50,14 +54,16 @@ public class AddWorkspaceItemsTask extends BaseModelUpdateTask { 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 @@ public class AddWorkspaceItemsTask extends BaseModelUpdateTask { * @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 @@ public class AddWorkspaceItemsTask extends BaseModelUpdateTask { } // 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 @@ public class AddWorkspaceItemsTask extends BaseModelUpdateTask { 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 @@ public class AddWorkspaceItemsTask extends BaseModelUpdateTask { 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 @@ public class AddWorkspaceItemsTask extends BaseModelUpdateTask { * 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 4875d83942..8f85bfbd3d 100644 --- a/src/com/android/launcher3/model/AllAppsList.java +++ b/src/com/android/launcher3/model/AllAppsList.java @@ -64,7 +64,10 @@ public class AllAppsList { /** 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/BaseLoaderResults.java index b50ab587f4..8c6428b028 100644 --- a/src/com/android/launcher3/model/BaseLoaderResults.java +++ b/src/com/android/launcher3/model/BaseLoaderResults.java @@ -33,7 +33,7 @@ import com.android.launcher3.model.BgDataModel.FixedContainerItems; 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.testing.TestProtocol; +import com.android.launcher3.testing.shared.TestProtocol; import com.android.launcher3.util.IntArray; import com.android.launcher3.util.IntSet; import com.android.launcher3.util.LooperExecutor; diff --git a/src/com/android/launcher3/model/BaseModelUpdateTask.java b/src/com/android/launcher3/model/BaseModelUpdateTask.java index 2a6a6919e0..74a2c5d4b5 100644 --- a/src/com/android/launcher3/model/BaseModelUpdateTask.java +++ b/src/com/android/launcher3/model/BaseModelUpdateTask.java @@ -17,6 +17,7 @@ package com.android.launcher3.model; import android.util.Log; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.launcher3.LauncherAppState; @@ -47,14 +48,17 @@ public abstract class BaseModelUpdateTask implements ModelUpdateTask { 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 @@ public abstract class BaseModelUpdateTask implements ModelUpdateTask { @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 @@ public abstract class BaseModelUpdateTask implements ModelUpdateTask { /** * 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 @@ public abstract class BaseModelUpdateTask implements ModelUpdateTask { 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 @@ public abstract class BaseModelUpdateTask implements ModelUpdateTask { .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 de23c4b31f..b0f6e13ae3 100644 --- a/src/com/android/launcher3/model/BgDataModel.java +++ b/src/com/android/launcher3/model/BgDataModel.java @@ -433,26 +433,9 @@ public class BgDataModel { 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 f644d49276..57fefaa8e1 100644 --- a/src/com/android/launcher3/model/CacheDataUpdatedTask.java +++ b/src/com/android/launcher3/model/CacheDataUpdatedTask.java @@ -18,6 +18,8 @@ package com.android.launcher3.model; 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 class CacheDataUpdatedTask extends BaseModelUpdateTask { 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 @@ public class CacheDataUpdatedTask extends BaseModelUpdateTask { 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 35fcb789e7..85d54c0d1c 100644 --- a/src/com/android/launcher3/model/DeviceGridState.java +++ b/src/com/android/launcher3/model/DeviceGridState.java @@ -29,7 +29,7 @@ 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,7 +58,7 @@ public class DeviceGridState implements Comparable<DeviceGridState> { } public DeviceGridState(Context context) { - SharedPreferences prefs = Utilities.getPrefs(context); + SharedPreferences prefs = LauncherPrefs.getPrefs(context); mGridSizeString = prefs.getString(KEY_WORKSPACE_SIZE, ""); mNumHotseat = prefs.getInt(KEY_HOTSEAT_COUNT, -1); mDeviceType = prefs.getInt(KEY_DEVICE_TYPE, TYPE_PHONE); @@ -90,7 +90,7 @@ public class DeviceGridState implements Comparable<DeviceGridState> { * Stores the device state to shared preferences */ public void writeToPrefs(Context context) { - Utilities.getPrefs(context).edit() + LauncherPrefs.getPrefs(context).edit() .putString(KEY_WORKSPACE_SIZE, mGridSizeString) .putInt(KEY_HOTSEAT_COUNT, mNumHotseat) .putInt(KEY_DEVICE_TYPE, mDeviceType) @@ -134,10 +134,13 @@ public class DeviceGridState implements Comparable<DeviceGridState> { * DeviceGridState without migration, or false otherwise. */ public boolean isCompatible(DeviceGridState other) { - if (this == other) return true; - if (other == null) return false; - return mNumHotseat == other.mNumHotseat - && Objects.equals(mGridSizeString, other.mGridSizeString); + if (this == other) { + return true; + } + if (other == null) { + return false; + } + return Objects.equals(mDbFile, other.mDbFile); } public Integer getColumns() { diff --git a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java b/src/com/android/launcher3/model/GridSizeMigrationUtil.java index ef9250c900..eded5ea6a9 100644 --- a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java +++ b/src/com/android/launcher3/model/GridSizeMigrationUtil.java @@ -31,7 +31,7 @@ import android.graphics.Point; 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.util.IntArray; 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; @@ -56,47 +57,19 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.stream.Collectors; /** * 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 } /** @@ -109,9 +82,8 @@ public class GridSizeMigrationTaskV2 { private static boolean needsToMigrate( DeviceGridState srcDeviceState, DeviceGridState destDeviceState) { boolean needsToMigrate = !destDeviceState.isCompatible(srcDeviceState); - // TODO(b/198965093): Revert this change after bug is fixed if (needsToMigrate) { - Log.d("b/198965093", "Migration is needed. destDeviceState: " + destDeviceState + Log.i(TAG, "Migration is needed. destDeviceState: " + destDeviceState + ", srcDeviceState: " + srcDeviceState); } return needsToMigrate; @@ -185,9 +157,8 @@ public class GridSizeMigrationTaskV2 { 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 @@ public class GridSizeMigrationTaskV2 { } } - @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,60 +255,47 @@ public class GridSizeMigrationTaskV2 { 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) { - Set<String> destIntentSet = new HashSet<>(); - Set<Map<String, Integer>> destFolderIntentSet = new HashSet<>(); - for (DbEntry entry : dest) { - if (entry.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) { - destFolderIntentSet.add(getFolderIntents(entry)); - } else { - destIntentSet.add(entry.mIntent); + /** + * 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) { - if (entry.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) { - if (!destFolderIntentSet.contains(getFolderIntents(entry))) { - diff.add(entry); - } - } else { - if (!destIntentSet.contains(entry.mIntent)) { - diff.add(entry); + }); + 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)); } } - } - return diff; - } - - private static Map<String, Integer> getFolderIntents(DbEntry entry) { - Map<String, Integer> folder = new HashMap<>(); - for (String intent : entry.mFolderItems.keySet()) { - folder.put(intent, entry.mFolderItems.get(intent).size()); - } - return folder; + }); } private static void insertEntryInDb(SQLiteDatabase db, Context context, DbEntry entry, @@ -368,144 +367,88 @@ public class GridSizeMigrationTaskV2 { 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; + } + if (findPlacementForEntry(entry, next, trg, occupied, screenId)) { + insertEntryInDb(db, context, entry, srcReader.mTableName, destReader.mTableName); + iterator.remove(); } - return false; } } - 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 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; + 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; + } + + 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; } } } @@ -518,8 +461,6 @@ public class GridSizeMigrationTaskV2 { 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<>(); @@ -531,7 +472,8 @@ public class GridSizeMigrationTaskV2 { 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 @@ -580,14 +522,15 @@ public class GridSizeMigrationTaskV2 { 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 @@ -602,10 +545,6 @@ public class GridSizeMigrationTaskV2 { 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); @@ -681,7 +620,7 @@ public class GridSizeMigrationTaskV2 { entriesToRemove.add(entry.id); continue; } - mWorkspaceEntries.add(entry); + workspaceEntries.add(entry); if (!mWorkspaceEntriesByScreenId.containsKey(entry.screenId)) { mWorkspaceEntriesByScreenId.put(entry.screenId, new ArrayList<>()); } @@ -689,7 +628,7 @@ public class GridSizeMigrationTaskV2 { } removeEntryFromDb(mDb, mTableName, entriesToRemove); c.close(); - return mWorkspaceEntries; + return workspaceEntries; } private int getFolderItemsCount(DbEntry entry) { @@ -765,12 +704,12 @@ public class GridSizeMigrationTaskV2 { 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) { @@ -780,5 +719,58 @@ public class GridSizeMigrationTaskV2 { values.put(LauncherSettings.Favorites.SPANX, spanX); values.put(LauncherSettings.Favorites.SPANY, spanY); } + + /** 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. + */ + public String getEntryMigrationId() { + switch (itemType) { + case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: + 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 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 5a220f74c7..69f9b53090 100644 --- a/src/com/android/launcher3/model/ItemInstallQueue.java +++ b/src/com/android/launcher3/model/ItemInstallQueue.java @@ -49,7 +49,7 @@ import com.android.launcher3.model.data.LauncherAppWidgetInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.shortcuts.ShortcutKey; import com.android.launcher3.shortcuts.ShortcutRequest; -import com.android.launcher3.testing.TestProtocol; +import com.android.launcher3.testing.shared.TestProtocol; import com.android.launcher3.util.MainThreadInitializedObject; import com.android.launcher3.util.PersistedItemArray; import com.android.launcher3.util.Preconditions; @@ -288,6 +288,7 @@ public class ItemInstallQueue { } @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 178fbdb7b8..07a460ce23 100644 --- a/src/com/android/launcher3/model/LoaderCursor.java +++ b/src/com/android/launcher3/model/LoaderCursor.java @@ -291,12 +291,16 @@ public class LoaderCursor extends CursorWrapper { // from the db if (TextUtils.isEmpty(info.title)) { - info.title = getTitle(); - } + if (loadIcon) { + info.title = getTitle(); - // fall back to the class name of the activity - if (info.title == null) { - info.title = componentName.getClassName(); + // fall back to the class name of the activity + if (info.title == null) { + info.title = componentName.getClassName(); + } + } else { + info.title = ""; + } } info.contentDescription = mPM.getUserBadgedLabel(info.title, info.user); @@ -459,7 +463,7 @@ public class LoaderCursor extends CursorWrapper { if (item.screenId == Workspace.FIRST_SCREEN_ID) { // Mark the first row as occupied (if the feature is enabled) // in order to account for the QSB. - int spanY = FeatureFlags.EXPANDED_SMARTSPACE.get() ? 2 : 1; + int spanY = 1; screen.markCells(0, 0, countX + 1, spanY, FeatureFlags.QSB_ON_FIRST_SCREEN); } occupied.put(item.screenId, screen); diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java index f1c5d59fc8..1d6971e27d 100644 --- a/src/com/android/launcher3/model/LoaderTask.java +++ b/src/com/android/launcher3/model/LoaderTask.java @@ -256,12 +256,10 @@ public class LoaderTask implements Runnable { mApp.getModel()::onPackageIconsUpdated); logASplit(logger, "update icon cache"); - if (FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) { - verifyNotStopped(); - logASplit(logger, "save shortcuts in icon cache"); - updateHandler.updateIcons(allShortcuts, new ShortcutCachingLogic(), - mApp.getModel()::onPackageIconsUpdated); - } + verifyNotStopped(); + logASplit(logger, "save shortcuts in icon cache"); + updateHandler.updateIcons(allShortcuts, new ShortcutCachingLogic(), + mApp.getModel()::onPackageIconsUpdated); // Take a break waitForIdle(); @@ -276,12 +274,10 @@ public class LoaderTask implements Runnable { mResults.bindDeepShortcuts(); logASplit(logger, "bindDeepShortcuts"); - if (FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) { - verifyNotStopped(); - logASplit(logger, "save deep shortcuts in icon cache"); - updateHandler.updateIcons(allDeepShortcuts, - new ShortcutCachingLogic(), (pkgs, user) -> { }); - } + verifyNotStopped(); + logASplit(logger, "save deep shortcuts in icon cache"); + updateHandler.updateIcons(allDeepShortcuts, + new ShortcutCachingLogic(), (pkgs, user) -> { }); // Take a break waitForIdle(); @@ -304,9 +300,7 @@ public class LoaderTask implements Runnable { logASplit(logger, "save widgets in icon cache"); // fifth step - if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) { - loadFolderNames(); - } + loadFolderNames(); verifyNotStopped(); updateHandler.finish(); @@ -355,7 +349,7 @@ public class LoaderTask implements Runnable { 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 422af43e19..c21fc38cfb 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; @@ -29,7 +27,7 @@ import com.android.launcher3.icons.BitmapInfo; import com.android.launcher3.icons.LauncherIcons; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; -import com.android.launcher3.testing.TestProtocol; +import com.android.launcher3.testing.shared.TestProtocol; import com.android.launcher3.util.IntArray; import com.android.launcher3.util.IntSet; @@ -149,4 +147,12 @@ public class ModelUtils { 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 0a68d4aa34..f444bd5718 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.ContentWriter; 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 @@ public class ModelWriter { /** * 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 c0dc34abb5..b9fba9db61 100644 --- a/src/com/android/launcher3/model/PackageIncrementalDownloadUpdatedTask.java +++ b/src/com/android/launcher3/model/PackageIncrementalDownloadUpdatedTask.java @@ -17,6 +17,8 @@ package com.android.launcher3.model; 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 @@ import java.util.List; */ 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 b74d0fc482..76a87ed1da 100644 --- a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java +++ b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java @@ -18,6 +18,8 @@ package com.android.launcher3.model; 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 @@ import java.util.List; */ 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 489bc38376..3d9d81ff4e 100644 --- a/src/com/android/launcher3/model/PackageUpdatedTask.java +++ b/src/com/android/launcher3/model/PackageUpdatedTask.java @@ -29,6 +29,8 @@ import android.os.UserHandle; 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; @@ -57,7 +59,9 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Objects; import java.util.function.Predicate; +import java.util.stream.Collectors; /** * Handles updates due to changes in package manager (app installed/updated/removed) @@ -78,17 +82,23 @@ public class PackageUpdatedTask extends BaseModelUpdateTask { 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(); @@ -343,7 +353,12 @@ public class PackageUpdatedTask extends BaseModelUpdateTask { .or(ItemInfoMatcher.ofComponents(removedComponents, mUser)) .and(ItemInfoMatcher.ofItemIds(forceKeepShortcuts).negate()); deleteAndBindComponentsRemoved(removeMatch, - "removed because the corresponding package or component is removed"); + "removed because the corresponding package or component is removed. " + + "mOp=" + mOp + " removedPackages=" + removedPackages.stream().collect( + Collectors.joining(",", "[", "]")) + + " removedComponents=" + removedComponents.stream() + .filter(Objects::nonNull).map(ComponentName::toShortString) + .collect(Collectors.joining(",", "[", "]"))); // Remove any queued items from the install queue ItemInstallQueue.INSTANCE.get(context) diff --git a/src/com/android/launcher3/model/ReloadStringCacheTask.java b/src/com/android/launcher3/model/ReloadStringCacheTask.java index f4d42988bf..34f7057dad 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 @@ import com.android.launcher3.LauncherAppState; * {@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 1026e0bd5b..a6a04a7291 100644 --- a/src/com/android/launcher3/model/ShortcutsChangedTask.java +++ b/src/com/android/launcher3/model/ShortcutsChangedTask.java @@ -19,6 +19,8 @@ import android.content.Context; 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 @@ import java.util.stream.Collectors; */ 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 @@ public class ShortcutsChangedTask 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) { 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 1565b19f4c..63ca35b79c 100644 --- a/src/com/android/launcher3/model/UserLockStateChangedTask.java +++ b/src/com/android/launcher3/model/UserLockStateChangedTask.java @@ -21,6 +21,8 @@ import android.content.Context; 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 @@ import java.util.Iterator; */ 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 5b2bcf5819..34972e71ff 100644 --- a/src/com/android/launcher3/model/data/AppInfo.java +++ b/src/com/android/launcher3/model/data/AppInfo.java @@ -55,6 +55,7 @@ public class AppInfo extends ItemInfoWithIcon implements WorkspaceItemFactory { */ public Intent intent; + @NonNull public ComponentName componentName; // Section name used for indexing. @@ -65,6 +66,7 @@ public class AppInfo extends ItemInfoWithIcon implements WorkspaceItemFactory { } @Override + @Nullable public Intent getIntent() { return intent; } @@ -151,7 +153,7 @@ public class AppInfo extends ItemInfoWithIcon implements WorkspaceItemFactory { | 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 efebce342f..524b769603 100644 --- a/src/com/android/launcher3/model/data/FolderInfo.java +++ b/src/com/android/launcher3/model/data/FolderInfo.java @@ -26,11 +26,11 @@ import static com.android.launcher3.logger.LauncherAtom.Attribute.SUGGESTED_LABE 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 @@ public class FolderInfo extends ItemInfo { } @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 @@ public class FolderInfo extends ItemInfo { 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 @@ public class FolderInfo extends ItemInfo { : LabelState.SUGGESTED; } + @NonNull @Override public ItemInfo makeShallowCopy() { FolderInfo folderInfo = new FolderInfo(); @@ -273,6 +275,7 @@ public class FolderInfo extends ItemInfo { /** * Returns {@link LauncherAtom.FolderIcon} wrapped as {@link LauncherAtom.ItemInfo} for logging. */ + @NonNull @Override public LauncherAtom.ItemInfo buildProto() { return buildProto(null); @@ -321,12 +324,6 @@ public class FolderInfo extends ItemInfo { 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 1e8e3cad83..2dd44a4000 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_DESKTOP 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 static com.android.launcher3.shortcuts.ShortcutKey.EXTRA_SHORTCUT_ID; 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; 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.LauncherAtom.WallpapersContainer; 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 @@ public class ItemInfo { // 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 @@ public class ItemInfo { /** * 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 @@ public class ItemInfo { mComponentName = info.getTargetComponent(); } + @Nullable public Intent getIntent() { return null; } @@ -209,7 +219,7 @@ public class ItemInfo { : 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 @@ public class ItemInfo { .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 @@ public class ItemInfo { /** * 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 @@ public class ItemInfo { } @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 @@ public class ItemInfo { /** * 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 @@ public class ItemInfo { 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 @@ public class ItemInfo { 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 @@ public class ItemInfo { /** * Returns {@link ContainerInfo} used when logging this item. */ + @NonNull public ContainerInfo getContainerInfo() { switch (container) { case CONTAINER_HOTSEAT: @@ -415,10 +437,6 @@ public class ItemInfo { 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 @@ public class ItemInfo { 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 @@ public class ItemInfo { * 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 @@ public class ItemInfo { /** * Returns shallow copy of the object. */ + @NonNull public ItemInfo makeShallowCopy() { ItemInfo itemInfo = new ItemInfo(); itemInfo.copyFrom(this); @@ -463,7 +485,8 @@ public class ItemInfo { /** * 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 76a0c4d64c..e5fb015f2e 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 @@ public abstract class ItemInfoWithIcon extends ItemInfo { /** 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 e57a895ddd..1fbe04f84d 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.Intent; 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 @@ public class LauncherAppWidgetInfo extends ItemInfo { } @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 @@ public class LauncherAppWidgetInfo extends ItemInfo { } } + @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 e879313f5e..0000000000 --- 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 2b3da335c3..59ef320120 100644 --- a/src/com/android/launcher3/model/data/WorkspaceItemInfo.java +++ b/src/com/android/launcher3/model/data/WorkspaceItemInfo.java @@ -75,6 +75,7 @@ public class WorkspaceItemInfo extends ItemInfoWithIcon { /** * The intent used to start the application. */ + @NonNull public Intent intent; /** @@ -130,7 +131,7 @@ public class WorkspaceItemInfo extends ItemInfoWithIcon { } @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 @@ public class WorkspaceItemInfo extends ItemInfoWithIcon { } @Override + @NonNull public Intent getIntent() { return intent; } @@ -164,7 +166,8 @@ public class WorkspaceItemInfo extends ItemInfoWithIcon { return isPromise() && !hasStatusFlag(FLAG_SUPPORTS_WEB_UI); } - public void updateFromDeepShortcutInfo(ShortcutInfo shortcutInfo, Context context) { + public void updateFromDeepShortcutInfo(@NonNull final ShortcutInfo shortcutInfo, + @NonNull final Context context) { // {@link ShortcutInfo#getActivity} can change during an update. Recreate the intent intent = ShortcutKey.makeIntent(shortcutInfo); title = shortcutInfo.getShortLabel(); diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java index 29eefe2a03..c324ce3cf5 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,21 @@ import android.graphics.Canvas; 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.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 +52,89 @@ import com.android.launcher3.util.Themes; * {@link PageIndicator} which shows dots per page. The active page is shown with the current * accent color. */ -public class PageIndicatorDots extends View implements PageIndicator { +public class PageIndicatorDots extends View implements Insettable, PageIndicator { private static final float SHIFT_PER_ANIMATION = 0.5f; private static final float SHIFT_THRESHOLD = 0.1f; private static final long ANIMATION_DURATION = 150; + private static final int PAGINATION_FADE_DELAY = ViewConfiguration.getScrollDefaultDelay(); + private static final int ALPHA_ANIMATE_DURATION = ViewConfiguration.getScrollBarFadeDuration(); private static final int ENTER_ANIMATION_START_DELAY = 300; private static final int ENTER_ANIMATION_STAGGERED_DELAY = 150; private static final int ENTER_ANIMATION_DURATION = 400; - private static final int DOT_ACTIVE_ALPHA = 255; - private static final int DOT_INACTIVE_ALPHA = 128; + private static final int PAGE_INDICATOR_ALPHA = 255; + private static final int DOT_ALPHA = 128; + private static final int DOT_GAP_FACTOR = 3; + private static final int VISIBLE_ALPHA = 1; + private static final int INVISIBLE_ALPHA = 0; + private Paint mPaginationPaint; // This value approximately overshoots to 1.5 times the original size. private static final float ENTER_ANIMATION_OVERSHOOT_TENSION = 4.9f; private static final RectF sTempRect = new RectF(); - private static final Property<PageIndicatorDots, Float> CURRENT_POSITION - = new Property<PageIndicatorDots, Float>(float.class, "current_position") { - @Override - public Float get(PageIndicatorDots obj) { - return obj.mCurrentPosition; - } + private static final FloatProperty<PageIndicatorDots> CURRENT_POSITION = + new FloatProperty<PageIndicatorDots>("current_position") { + @Override + public Float get(PageIndicatorDots obj) { + return obj.mCurrentPosition; + } - @Override - public void set(PageIndicatorDots obj, Float pos) { - obj.mCurrentPosition = pos; - obj.invalidate(); - obj.invalidateOutline(); - } - }; + @Override + public void setValue(PageIndicatorDots obj, float pos) { + obj.mCurrentPosition = pos; + obj.invalidate(); + obj.invalidateOutline(); + } + }; + + private static final FloatProperty<PageIndicatorDots> PAGINATION_ALPHA = + new FloatProperty<PageIndicatorDots>("pagination_alpha") { + @Override + public Float get(PageIndicatorDots obj) { + return obj.getAlpha(); + } - private final Paint mCirclePaint; + @Override + public void setValue(PageIndicatorDots obj, float alpha) { + obj.setAlpha(alpha); + obj.invalidate(); + } + }; + + 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 mCurrentScroll; + private int mTotalScroll; + private boolean mShouldAutoHide = true; + private int mToAlpha; /** * The current position of the active dot including the animation progress. * For ex: - * 0.0 => Active dot is at position 0 - * 0.33 => Active dot is at position 0 and is moving towards 1 - * 0.50 => Active dot is at position [0, 1] - * 0.77 => Active dot has left position 0 and is collapsing towards position 1 - * 1.0 => Active dot is at position 1 + * 0.0 => Active dot is at position 0 + * 0.33 => Active dot is at position 0 and is moving towards 1 + * 0.50 => Active dot is at position [0, 1] + * 0.77 => Active dot has left position 0 and is collapsing towards position 1 + * 1.0 => Active dot is at position 1 */ private float mCurrentPosition; private float mFinalPosition; private ObjectAnimator mAnimator; + private @Nullable ObjectAnimator mAlphaAnimator; private float[] mEntryAnimationRadiusFactors; + private Runnable mHidePaginationRunnable = () -> animatePaginationToAlpha(INVISIBLE_ALPHA); + public PageIndicatorDots(Context context) { this(context, null); } @@ -109,37 +146,111 @@ public class PageIndicatorDots extends View implements PageIndicator { public PageIndicatorDots(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); - mCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mCirclePaint.setStyle(Style.FILL); - mCirclePaint.setColor(Themes.getAttrColor(context, R.attr.folderPaginationColor)); + mPaginationPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mPaginationPaint.setStyle(Style.FILL); + mPaginationPaint.setColor(Themes.getAttrColor(context, R.attr.folderPaginationColor)); mDotRadius = getResources().getDimension(R.dimen.page_indicator_dot_size) / 2; + 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; + if (SHOW_DOT_PAGINATION.get()) { + animatePaginationToAlpha(VISIBLE_ALPHA); + } + + if (mNumPages <= 1) { + mCurrentScroll = 0; + return; + } + + if (mIsRtl) { + currentScroll = totalScroll - currentScroll; + } + + mTotalScroll = totalScroll; + + int scrollPerPage = totalScroll / (mNumPages - 1); + int pageToLeft = currentScroll / scrollPerPage; + int pageToLeftScroll = pageToLeft * scrollPerPage; + int pageToRightScroll = pageToLeftScroll + scrollPerPage; + + float scrollThreshold = SHIFT_THRESHOLD * scrollPerPage; + if (currentScroll < pageToLeftScroll + scrollThreshold) { + // scroll is within the left page's threshold + animateToPosition(pageToLeft); + if (SHOW_DOT_PAGINATION.get()) { + hideAfterDelay(); + } + } else if (currentScroll > pageToRightScroll - scrollThreshold) { + // scroll is far enough from left page to go to the right page + animateToPosition(pageToLeft + 1); + if (SHOW_DOT_PAGINATION.get()) { + hideAfterDelay(); } - int scrollPerPage = totalScroll / (mNumPages - 1); - int pageToLeft = currentScroll / scrollPerPage; - int pageToLeftScroll = pageToLeft * scrollPerPage; - int pageToRightScroll = pageToLeftScroll + scrollPerPage; - - float scrollThreshold = SHIFT_THRESHOLD * scrollPerPage; - if (currentScroll < pageToLeftScroll + scrollThreshold) { - // scroll is within the left page's threshold - animateToPosition(pageToLeft); - } 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); + } else { + // scroll is between left and right page + animateToPosition(pageToLeft + SHIFT_PER_ANIMATION); + } + } + + @Override + public void setShouldAutoHide(boolean shouldAutoHide) { + mShouldAutoHide = shouldAutoHide; + if (shouldAutoHide && this.getAlpha() > INVISIBLE_ALPHA) { + hideAfterDelay(); + } else if (!shouldAutoHide) { + mDelayedPaginationFadeHandler.removeCallbacksAndMessages(null); + } + } + + private void hideAfterDelay() { + mDelayedPaginationFadeHandler.removeCallbacksAndMessages(null); + mDelayedPaginationFadeHandler.postDelayed(mHidePaginationRunnable, PAGINATION_FADE_DELAY); + } + + private void animatePaginationToAlpha(int alpha) { + if (alpha == mToAlpha) { + // Ignore the new animation if it is going to the same alpha as the current animation. + return; + } + mToAlpha = alpha; + + if (mAlphaAnimator != null) { + mAlphaAnimator.cancel(); + } + mAlphaAnimator = ObjectAnimator.ofFloat(this, PAGINATION_ALPHA, + alpha); + mAlphaAnimator.setDuration(ALPHA_ANIMATE_DURATION); + mAlphaAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mAlphaAnimator = null; } + }); + mAlphaAnimator.start(); + + } + + /** + * Pauses all currently running animations. + */ + @Override + public void pauseAnimations() { + if (mAlphaAnimator != null) { + mAlphaAnimator.pause(); + } + } + + /** + * Force-ends all currently running or paused animations. + */ + @Override + public void skipAnimationsToEnd() { + if (mAlphaAnimator != null) { + mAlphaAnimator.end(); } } @@ -177,7 +288,7 @@ public class PageIndicatorDots extends View implements PageIndicator { } public void playEntryAnimation() { - int count = mEntryAnimationRadiusFactors.length; + int count = mEntryAnimationRadiusFactors.length; if (count == 0) { mEntryAnimationRadiusFactors = null; invalidate(); @@ -231,16 +342,20 @@ public class PageIndicatorDots extends View implements PageIndicator { // Add extra spacing of mDotRadius on all sides so than entry animation could be run. int width = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY ? MeasureSpec.getSize(widthMeasureSpec) : (int) ((mNumPages * 3 + 2) * mDotRadius); - int height= MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY ? - MeasureSpec.getSize(heightMeasureSpec) : (int) (4 * mDotRadius); + int height = MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY + ? MeasureSpec.getSize(heightMeasureSpec) : (int) (4 * mDotRadius); setMeasuredDimension(width, height); } @Override protected void onDraw(Canvas canvas) { + if ((mShouldAutoHide && mTotalScroll == 0) || mNumPages < 2) { + return; + } + // Draw all page indicators; - float circleGap = 3 * mDotRadius; - float startX = (getWidth() - mNumPages * circleGap + mDotRadius) / 2; + float circleGap = mCircleGap; + float startX = (getWidth() - (mNumPages * circleGap) + mDotRadius) / 2; float x = startX + mDotRadius; float y = getHeight() / 2; @@ -252,19 +367,22 @@ public class PageIndicatorDots extends View implements PageIndicator { 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); + // Here we draw the dots + mPaginationPaint.setAlpha(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(PAGE_INDICATOR_ALPHA); + canvas.drawRoundRect(getActiveRect(), mDotRadius, mDotRadius, mPaginationPaint); } } @@ -272,23 +390,23 @@ public class PageIndicatorDots extends View implements PageIndicator { 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 +414,7 @@ public class PageIndicatorDots extends View implements PageIndicator { sTempRect.right = getWidth() - sTempRect.left; sTempRect.left = sTempRect.right - rectWidth; } + return sTempRect; } @@ -336,4 +455,12 @@ public class PageIndicatorDots extends View implements PageIndicator { } } } + + /** + * 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 1681ea5758..bde4e525a1 100644 --- a/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java +++ b/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java @@ -14,12 +14,9 @@ import android.os.Handler; 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 @@ public class WorkspacePageIndicator extends View implements Insettable, PageIndi } } + /** + * 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 + insets.bottom; - } - setLayoutParams(lp); } } diff --git a/src/com/android/launcher3/pm/InstallSessionHelper.java b/src/com/android/launcher3/pm/InstallSessionHelper.java index 618f926bc3..150bca4058 100644 --- a/src/com/android/launcher3/pm/InstallSessionHelper.java +++ b/src/com/android/launcher3/pm/InstallSessionHelper.java @@ -16,7 +16,7 @@ package com.android.launcher3.pm; -import static com.android.launcher3.Utilities.getPrefs; +import static com.android.launcher3.LauncherPrefs.getPrefs; import android.content.Context; import android.content.pm.ApplicationInfo; @@ -30,6 +30,7 @@ import android.text.TextUtils; import android.util.Log; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import androidx.annotation.WorkerThread; @@ -39,7 +40,7 @@ import com.android.launcher3.Utilities; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.logging.FileLog; import com.android.launcher3.model.ItemInstallQueue; -import com.android.launcher3.testing.TestProtocol; +import com.android.launcher3.testing.shared.TestProtocol; import com.android.launcher3.util.IntArray; import com.android.launcher3.util.IntSet; import com.android.launcher3.util.MainThreadInitializedObject; @@ -51,38 +52,50 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.Objects; /** * Utility class to tracking install sessions */ public class InstallSessionHelper { + @NonNull private static final String LOG = "InstallSessionHelper"; // Set<String> of session ids of promise icons that have been added to the home screen // as FLAG_PROMISE_NEW_INSTALLS. + @NonNull protected static final String PROMISE_ICON_IDS = "promise_icon_ids"; private static final boolean DEBUG = false; + @NonNull public static final MainThreadInitializedObject<InstallSessionHelper> INSTANCE = new MainThreadInitializedObject<>(InstallSessionHelper::new); + @Nullable private final LauncherApps mLauncherApps; + + @NonNull private final Context mAppContext; + @NonNull private final PackageInstaller mInstaller; + + @NonNull private final HashMap<String, Boolean> mSessionVerifiedMap = new HashMap<>(); + @Nullable private IntSet mPromiseIconIds; - public InstallSessionHelper(Context context) { + public InstallSessionHelper(@NonNull final Context context) { mInstaller = context.getPackageManager().getPackageInstaller(); mAppContext = context.getApplicationContext(); mLauncherApps = context.getSystemService(LauncherApps.class); } @WorkerThread + @NonNull private IntSet getPromiseIconIds() { Preconditions.assertWorkerThread(); if (mPromiseIconIds != null) { @@ -108,6 +121,7 @@ public class InstallSessionHelper { return mPromiseIconIds; } + @NonNull public HashMap<PackageUserKey, SessionInfo> getActiveSessions() { HashMap<PackageUserKey, SessionInfo> activePackages = new HashMap<>(); for (SessionInfo info : getAllVerifiedSessions()) { @@ -117,6 +131,7 @@ public class InstallSessionHelper { return activePackages; } + @Nullable public SessionInfo getActiveSessionInfo(UserHandle user, String pkg) { for (SessionInfo info : getAllVerifiedSessions()) { boolean match = pkg.equals(info.getAppPackageName()); @@ -136,11 +151,13 @@ public class InstallSessionHelper { .apply(); } - SessionInfo getVerifiedSessionInfo(int sessionId) { + @Nullable + SessionInfo getVerifiedSessionInfo(final int sessionId) { return verify(mInstaller.getSessionInfo(sessionId)); } - private SessionInfo verify(SessionInfo sessionInfo) { + @Nullable + private SessionInfo verify(@Nullable final SessionInfo sessionInfo) { if (sessionInfo == null || sessionInfo.getInstallerPackageName() == null || TextUtils.isEmpty(sessionInfo.getAppPackageName())) { @@ -167,9 +184,10 @@ public class InstallSessionHelper { return mSessionVerifiedMap.get(pkg) ? sessionInfo : null; } + @NonNull public List<SessionInfo> getAllVerifiedSessions() { List<SessionInfo> list = new ArrayList<>(Utilities.ATLEAST_Q - ? mLauncherApps.getAllPackageInstallerSessions() + ? Objects.requireNonNull(mLauncherApps).getAllPackageInstallerSessions() : mInstaller.getAllSessions()); Iterator<SessionInfo> it = list.iterator(); while (it.hasNext()) { @@ -201,12 +219,12 @@ public class InstallSessionHelper { } @WorkerThread - public boolean promiseIconAddedForId(int sessionId) { + public boolean promiseIconAddedForId(final int sessionId) { return getPromiseIconIds().contains(sessionId); } @WorkerThread - public void removePromiseIconId(int sessionId) { + public void removePromiseIconId(final int sessionId) { if (promiseIconAddedForId(sessionId)) { getPromiseIconIds().getArray().removeValue(sessionId); updatePromiseIconPrefs(); @@ -222,17 +240,15 @@ public class InstallSessionHelper { * - A promise icon for the session has not already been created */ @WorkerThread - void tryQueuePromiseAppIcon(PackageInstaller.SessionInfo sessionInfo) { + void tryQueuePromiseAppIcon(@Nullable final PackageInstaller.SessionInfo sessionInfo) { if (TestProtocol.sDebugTracing) { Log.d(TestProtocol.MISSING_PROMISE_ICON, LOG + " tryQueuePromiseAppIcon" - + ", FeatureFlags=" + FeatureFlags.PROMISE_APPS_NEW_INSTALLS.get() + ", SessionCommitReceiveEnabled" + SessionCommitReceiver.isEnabled(mAppContext) + ", verifySessionInfo(sessionInfo)=" + verifySessionInfo(sessionInfo) + ", !promiseIconAdded=" + (sessionInfo != null && !promiseIconAddedForId(sessionInfo.getSessionId()))); } - if (FeatureFlags.PROMISE_APPS_NEW_INSTALLS.get() - && SessionCommitReceiver.isEnabled(mAppContext) + if (SessionCommitReceiver.isEnabled(mAppContext) && verifySessionInfo(sessionInfo) && !promiseIconAddedForId(sessionInfo.getSessionId())) { FileLog.d(LOG, "Adding package name to install queue: " @@ -246,7 +262,7 @@ public class InstallSessionHelper { } } - public boolean verifySessionInfo(PackageInstaller.SessionInfo sessionInfo) { + public boolean verifySessionInfo(@Nullable final PackageInstaller.SessionInfo sessionInfo) { if (TestProtocol.sDebugTracing) { boolean appNotInstalled = sessionInfo == null || !new PackageManagerHelper(mAppContext) @@ -269,14 +285,15 @@ public class InstallSessionHelper { 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 75cf7a845b..aeaa320ec9 100644 --- a/src/com/android/launcher3/pm/InstallSessionTracker.java +++ b/src/com/android/launcher3/pm/InstallSessionTracker.java @@ -28,12 +28,15 @@ import android.os.UserHandle; 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.TestProtocol; +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 @@ public class InstallSessionTracker extends PackageInstaller.SessionCallback { // 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 @@ public class InstallSessionTracker extends PackageInstaller.SessionCallback { } @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 @@ public class InstallSessionTracker extends PackageInstaller.SessionCallback { } @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 @@ public class InstallSessionTracker extends PackageInstaller.SessionCallback { } @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 @@ public class InstallSessionTracker extends PackageInstaller.SessionCallback { } @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 @@ public class InstallSessionTracker extends PackageInstaller.SessionCallback { } } - 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 @@ public class InstallSessionTracker extends PackageInstaller.SessionCallback { 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 @@ public class InstallSessionTracker extends PackageInstaller.SessionCallback { 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 @@ public class InstallSessionTracker extends PackageInstaller.SessionCallback { 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 196cc56be7..9a745ab9f4 100644 --- a/src/com/android/launcher3/popup/ArrowPopup.java +++ b/src/com/android/launcher3/popup/ArrowPopup.java @@ -21,14 +21,12 @@ import static androidx.core.content.ContextCompat.getColorStateList; import static com.android.launcher3.anim.Interpolators.ACCELERATED_EASE; import static com.android.launcher3.anim.Interpolators.DECELERATED_EASE; import static com.android.launcher3.anim.Interpolators.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.Rect; 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 androidx.annotation.Nullable; 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 @@ public abstract class ArrowPopup<T extends Context & ActivityContext> 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 @@ public abstract class ArrowPopup<T extends Context & ActivityContext> // 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 @@ public abstract class ArrowPopup<T extends Context & ActivityContext> 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 @@ public abstract class ArrowPopup<T extends Context & ActivityContext> 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 @@ public abstract class ArrowPopup<T extends Context & ActivityContext> } /** - * 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 @@ public abstract class ArrowPopup<T extends Context & ActivityContext> 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 @@ public abstract class ArrowPopup<T extends Context & ActivityContext> } } - if (!ENABLE_LOCAL_COLOR_POPUPS.get()) { - setChildColor(view, backgroundColor, colorAnimator); - } - + setChildColor(view, backgroundColor, colorAnimator); numVisibleChild++; } } @@ -320,85 +286,6 @@ public abstract class ArrowPopup<T extends Context & ActivityContext> 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 @@ public abstract class ArrowPopup<T extends Context & ActivityContext> if (reverseOrder) { reverseOrder(viewsToFlip); } - onInflationComplete(reverseOrder); assignMarginsAndBackgrounds(this); if (shouldAddArrow()) { addArrow(); @@ -438,7 +324,6 @@ public abstract class ArrowPopup<T extends Context & ActivityContext> */ public void show() { setupForDisplay(); - onInflationComplete(false); assignMarginsAndBackgrounds(this); if (shouldAddArrow()) { addArrow(); @@ -819,9 +704,6 @@ public abstract class ArrowPopup<T extends Context & ActivityContext> 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 49d97d21c1..4da588e839 100644 --- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java +++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java @@ -69,9 +69,9 @@ import com.android.launcher3.views.ActivityContext; 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; import java.util.stream.Collectors; /** @@ -235,32 +235,34 @@ public class PopupContainerWithArrow<T extends Context & ActivityContext> 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) { + if (shortcuts.isEmpty()) { + return; + } + // If there is only 1 shortcut, add it to its own container so it can show text and icon + if (shortcuts.size() == 1) { + initializeSystemShortcut(R.layout.system_shortcut, this, shortcuts.get(0)); + return; + } + mSystemShortcutContainer = inflateAndAdd(R.layout.system_shortcut_icons, this); + for (SystemShortcut shortcut : shortcuts) { + initializeSystemShortcut( + R.layout.system_shortcut_icon_only, mSystemShortcutContainer, + shortcut); + } } @TargetApi(Build.VERSION_CODES.P) public void populateAndShow(final BubbleTextView originalIcon, int shortcutCount, - final List<NotificationKeyData> notificationKeys, List<SystemShortcut> systemShortcuts) { + final List<NotificationKeyData> notificationKeys, List<SystemShortcut> shortcuts) { mNumNotifications = notificationKeys.size(); mOriginalIcon = originalIcon; boolean hasDeepShortcuts = shortcutCount > 0; mContainerWidth = getResources().getDimensionPixelSize(R.dimen.bg_popup_item_width); - // if there are deep shortcuts, we might want to increase the width of shortcuts to fit - // horizontally laid out system shortcuts. - if (hasDeepShortcuts) { - mContainerWidth = Math.max(mContainerWidth, - systemShortcuts.size() * getResources() - .getDimensionPixelSize(R.dimen.system_shortcut_header_icon_touch_size) - ); - } // Add views if (mNumNotifications > 0) { // Add notification entries @@ -279,6 +281,24 @@ public class PopupContainerWithArrow<T extends Context & ActivityContext> mDeepShortcutContainer = findViewById(R.id.deep_shortcuts_container); } if (hasDeepShortcuts) { + // Remove the widget shortcut fom the list + List<SystemShortcut> systemShortcuts = shortcuts + .stream() + .filter(shortcut -> !(shortcut instanceof SystemShortcut.Widgets)) + .collect(Collectors.toList()); + Optional<SystemShortcut.Widgets> widgetShortcutOpt = shortcuts + .stream() + .filter(shortcut -> shortcut instanceof SystemShortcut.Widgets) + .map(SystemShortcut.Widgets.class::cast) + .findFirst(); + + // if there are deep shortcuts, we might want to increase the width of shortcuts to fit + // horizontally laid out system shortcuts. + mContainerWidth = Math.max(mContainerWidth, + systemShortcuts.size() * getResources() + .getDimensionPixelSize(R.dimen.system_shortcut_header_icon_touch_size) + ); + mDeepShortcutContainer.setVisibility(View.VISIBLE); for (int i = shortcutCount; i > 0; i--) { @@ -288,30 +308,19 @@ public class PopupContainerWithArrow<T extends Context & ActivityContext> } updateHiddenShortcuts(); - if (!systemShortcuts.isEmpty()) { - for (SystemShortcut shortcut : systemShortcuts) { - if (shortcut instanceof SystemShortcut.Widgets) { - if (mWidgetContainer == null) { - mWidgetContainer = inflateAndAdd(R.layout.widget_shortcut_container, - this); - } - initializeWidgetShortcut(mWidgetContainer, shortcut); - } - } - mSystemShortcutContainer = inflateAndAdd(R.layout.system_shortcut_icons, this); - - for (SystemShortcut shortcut : systemShortcuts) { - if (!(shortcut instanceof SystemShortcut.Widgets)) { - initializeSystemShortcut( - R.layout.system_shortcut_icon_only, mSystemShortcutContainer, - shortcut); - } + if (widgetShortcutOpt.isPresent()) { + if (mWidgetContainer == null) { + mWidgetContainer = inflateAndAdd(R.layout.widget_shortcut_container, + this); } + initializeWidgetShortcut(mWidgetContainer, widgetShortcutOpt.get()); } + + initializeSystemShortcuts(systemShortcuts); } else { mDeepShortcutContainer.setVisibility(View.GONE); - if (!systemShortcuts.isEmpty()) { - for (SystemShortcut shortcut : systemShortcuts) { + if (!shortcuts.isEmpty()) { + for (SystemShortcut shortcut : shortcuts) { initializeSystemShortcut(R.layout.system_shortcut, this, shortcut); } } diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java index 48b3acfbf1..5e97b2dedd 100644 --- a/src/com/android/launcher3/provider/RestoreDbTask.java +++ b/src/com/android/launcher3/provider/RestoreDbTask.java @@ -36,6 +36,7 @@ import androidx.annotation.NonNull; 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 +48,7 @@ import com.android.launcher3.model.data.WorkspaceItemInfo; 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; @@ -85,7 +87,7 @@ public class RestoreDbTask { // 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.getPrefs(context).edit().remove(RESTORED_DEVICE_TYPE).commit(); idp.reinitializeAfterRestore(context); } @@ -238,7 +240,7 @@ public class RestoreDbTask { } // If restored from a single display backup, remove gaps between screenIds - if (Utilities.getPrefs(context).getInt(RESTORED_DEVICE_TYPE, TYPE_PHONE) + if (LauncherPrefs.getPrefs(context).getInt(RESTORED_DEVICE_TYPE, TYPE_PHONE) != TYPE_MULTI_DISPLAY) { removeScreenIdGaps(db); } @@ -337,7 +339,7 @@ public class RestoreDbTask { } public static boolean isPending(Context context) { - return Utilities.getPrefs(context).contains(RESTORED_DEVICE_TYPE); + return LauncherPrefs.getPrefs(context).contains(RESTORED_DEVICE_TYPE); } /** @@ -345,17 +347,20 @@ public class RestoreDbTask { */ public static void setPending(Context context) { FileLog.d(TAG, "Restore data received through full backup "); - Utilities.getPrefs(context).edit() + LauncherPrefs.getPrefs(context).edit() .putInt(RESTORED_DEVICE_TYPE, new DeviceGridState(context).getDeviceType()) .commit(); } private void restoreAppWidgetIdsIfExists(Context context) { - SharedPreferences prefs = Utilities.getPrefs(context); + SharedPreferences prefs = LauncherPrefs.getPrefs(context); if (prefs.contains(APPWIDGET_OLD_IDS) && prefs.contains(APPWIDGET_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(prefs.getString(APPWIDGET_IDS, "")).toArray(), + holder); + holder.destroy(); } else { FileLog.d(TAG, "No app widget ids to restore."); } @@ -366,7 +371,7 @@ public class RestoreDbTask { public static void setRestoredAppWidgetIds(Context context, @NonNull int[] oldIds, @NonNull int[] newIds) { - Utilities.getPrefs(context).edit() + LauncherPrefs.getPrefs(context).edit() .putString(APPWIDGET_OLD_IDS, IntArray.wrap(oldIds).toConcatString()) .putString(APPWIDGET_IDS, IntArray.wrap(newIds).toConcatString()) .commit(); diff --git a/src/com/android/launcher3/qsb/QsbContainerView.java b/src/com/android/launcher3/qsb/QsbContainerView.java index 23ee251772..f2952041f7 100644 --- a/src/com/android/launcher3/qsb/QsbContainerView.java +++ b/src/com/android/launcher3/qsb/QsbContainerView.java @@ -43,8 +43,8 @@ import androidx.annotation.Nullable; 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 @@ public class QsbContainerView extends FrameLayout { 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 @@ public class QsbContainerView extends FrameLayout { } 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 495a303a7e..cf7ab1033a 100644 --- a/src/com/android/launcher3/search/SearchCallback.java +++ b/src/com/android/launcher3/search/SearchCallback.java @@ -24,6 +24,11 @@ import java.util.ArrayList; */ 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 @@ public interface SearchCallback<T> { 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 acab52bbe8..c66f3a19e4 100644 --- a/src/com/android/launcher3/search/StringMatcherUtility.java +++ b/src/com/android/launcher3/search/StringMatcherUtility.java @@ -24,8 +24,8 @@ import java.text.Collator; 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 @@ public class StringMatcherUtility { 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 @@ public class StringMatcherUtility { } /** - * 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 class StringMatcherUtility { 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/PinnedAppsAdapter.java b/src/com/android/launcher3/secondarydisplay/PinnedAppsAdapter.java index a0ed77e38f..f03c62ac5c 100644 --- a/src/com/android/launcher3/secondarydisplay/PinnedAppsAdapter.java +++ b/src/com/android/launcher3/secondarydisplay/PinnedAppsAdapter.java @@ -168,15 +168,18 @@ public class PinnedAppsAdapter extends BaseAdapter implements OnSharedPreference mPrefs.unregisterOnSharedPreferenceChangeListener(this); } - private void update(ItemInfo info, Function<ComponentKey, Boolean> op) { + /** + * Pins or unpins apps from home screen + */ + public void update(ItemInfo info, Function<ComponentKey, Boolean> op) { ComponentKey key = new ComponentKey(info.getTargetComponent(), info.user); if (op.apply(key)) { createFilteredAppsList(); Set<ComponentKey> copy = new HashSet<>(mPinnedApps); Executors.MODEL_EXECUTOR.submit(() -> mPrefs.edit().putStringSet(PINNED_APPS_KEY, - copy.stream().map(this::encode).collect(Collectors.toSet())) - .apply()); + copy.stream().map(this::encode).collect(Collectors.toSet())) + .apply()); } } @@ -210,6 +213,13 @@ public class PinnedAppsAdapter extends BaseAdapter implements OnSharedPreference mPinnedApps.contains(new ComponentKey(info.getTargetComponent(), info.user))); } + /** + * Pins app to home screen + */ + public void addPinnedApp(ItemInfo info) { + update(info, mPinnedApps::add); + } + private class PinUnPinShortcut extends SystemShortcut<SecondaryDisplayLauncher> { private final boolean mIsPinned; diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java index a2ab7f9d53..a2353d8259 100644 --- a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java +++ b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java @@ -18,6 +18,9 @@ package com.android.launcher3.secondarydisplay; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.content.Intent; +import android.graphics.Point; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; @@ -26,11 +29,21 @@ import android.view.inputmethod.InputMethodManager; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.BaseDraggingActivity; +import com.android.launcher3.BubbleTextView; +import com.android.launcher3.DragSource; +import com.android.launcher3.DropTarget; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherModel; +import com.android.launcher3.LauncherPrefs; +import com.android.launcher3.LauncherSettings; import com.android.launcher3.R; import com.android.launcher3.allapps.ActivityAllAppsContainerView; +import com.android.launcher3.dragndrop.DragController; +import com.android.launcher3.dragndrop.DragOptions; +import com.android.launcher3.dragndrop.DraggableView; +import com.android.launcher3.graphics.DragPreviewProvider; +import com.android.launcher3.icons.FastBitmapDrawable; import com.android.launcher3.model.BgDataModel; import com.android.launcher3.model.StringCache; import com.android.launcher3.model.data.AppInfo; @@ -38,7 +51,10 @@ import com.android.launcher3.model.data.ItemInfo; 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.views.BaseDragLayer; @@ -48,11 +64,11 @@ import java.util.HashMap; * Launcher activity for secondary displays */ public class SecondaryDisplayLauncher extends BaseDraggingActivity - implements BgDataModel.Callbacks { + implements BgDataModel.Callbacks, DragController.DragListener { private LauncherModel mModel; - private BaseDragLayer mDragLayer; + private SecondaryDragController mDragController; private ActivityAllAppsContainerView<SecondaryDisplayLauncher> mAppsView; private View mAppsButton; @@ -61,11 +77,19 @@ public class SecondaryDisplayLauncher extends BaseDraggingActivity private boolean mAppDrawerShown = false; private StringCache mStringCache; + private OnboardingPrefs<?> mOnboardingPrefs; + private boolean mBindingItems = false; + private SecondaryDisplayPredictions mSecondaryDisplayPredictions; + + private final int[] mTempXY = new int[2]; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mModel = LauncherAppState.getInstance(this).getModel(); + mDragController = new SecondaryDragController(this); + mOnboardingPrefs = new OnboardingPrefs<>(this, LauncherPrefs.getPrefs(this)); + mSecondaryDisplayPredictions = SecondaryDisplayPredictions.newInstance(this); if (getWindow().getDecorView().isAttachedToWindow()) { initUi(); } @@ -77,6 +101,12 @@ public class SecondaryDisplayLauncher extends BaseDraggingActivity initUi(); } + @Override + public void onDetachedFromWindow() { + super.onDetachedFromWindow(); + this.getDragController().removeDragListener(this); + } + private void initUi() { if (mDragLayer != null) { return; @@ -97,6 +127,7 @@ public class SecondaryDisplayLauncher extends BaseDraggingActivity mAppsView = findViewById(R.id.apps_view); mAppsButton = findViewById(R.id.all_apps_button); + mDragController.addDragListener(this); mPopupDataProvider = new PopupDataProvider( mAppsView.getAppsStore()::updateNotificationDots); @@ -104,6 +135,12 @@ public class SecondaryDisplayLauncher extends BaseDraggingActivity } @Override + protected void onPause() { + super.onPause(); + mDragController.cancelDrag(); + } + + @Override public void onNewIntent(Intent intent) { super.onNewIntent(intent); @@ -120,12 +157,21 @@ public class SecondaryDisplayLauncher extends BaseDraggingActivity showAppDrawer(false); } + public DragController getDragController() { + return mDragController; + } + @Override public void onBackPressed() { if (finishAutoCancelActionMode()) { return; } + if (mDragController.isDragging()) { + mDragController.cancelDrag(); + return; + } + // Note: There should be at most one log per method call. This is enforced implicitly // by using if-else statements. AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(this); @@ -193,7 +239,7 @@ public class SecondaryDisplayLauncher extends BaseDraggingActivity float closeR = Themes.getDialogCornerRadius(this); float startR = mAppsButton.getWidth() / 2f; - float[] buttonPos = new float[] { startR, startR}; + float[] buttonPos = new float[]{startR, startR}; mDragLayer.getDescendantCoordRelativeToSelf(mAppsButton, buttonPos); mDragLayer.mapCoordInSelfToDescendant(mAppsView, buttonPos); final Animator animator = ViewAnimationUtils.createCircularReveal(mAppsView, @@ -204,6 +250,7 @@ public class SecondaryDisplayLauncher extends BaseDraggingActivity mAppDrawerShown = true; mAppsView.setVisibility(View.VISIBLE); mAppsButton.setVisibility(View.INVISIBLE); + mSecondaryDisplayPredictions.updateAppDivider(); } else { mAppDrawerShown = false; animator.addListener(new AnimatorListenerAdapter() { @@ -219,6 +266,27 @@ public class SecondaryDisplayLauncher extends BaseDraggingActivity } @Override + public OnboardingPrefs<?> getOnboardingPrefs() { + return mOnboardingPrefs; + } + + @Override + public void startBinding() { + mBindingItems = true; + mDragController.cancelDrag(); + } + + @Override + public boolean isBindingItems() { + return mBindingItems; + } + + @Override + public void finishBindingItems(IntSet pagesBoundFirst) { + mBindingItems = false; + } + + @Override public void bindDeepShortcutMap(HashMap<ComponentKey, Integer> deepShortcutMap) { mPopupDataProvider.setDeepShortcutMap(deepShortcutMap); } @@ -230,6 +298,17 @@ public class SecondaryDisplayLauncher extends BaseDraggingActivity } @Override + public void bindExtraContainerItems(BgDataModel.FixedContainerItems item) { + if (item.containerId == LauncherSettings.Favorites.CONTAINER_PREDICTION) { + mSecondaryDisplayPredictions.setPredictedApps(item); + } + } + + public SecondaryDisplayPredictions getSecondaryDisplayPredictions() { + return mSecondaryDisplayPredictions; + } + + @Override public StringCache getStringCache() { return mStringCache; } @@ -254,12 +333,14 @@ public class SecondaryDisplayLauncher extends BaseDraggingActivity 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 && (((ItemInfoWithIcon) item).runtimeStatusFlags - & ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0) { + & ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0) { ItemInfoWithIcon appInfo = (ItemInfoWithIcon) item; intent = appInfo.getMarketIntent(this); } else { @@ -271,4 +352,101 @@ public class SecondaryDisplayLauncher extends BaseDraggingActivity startActivitySafely(v, intent, item); } } + + /** + * Core functionality for beginning a drag operation for an item that will be dropped within + * the secondary display grid home screen + */ + public void beginDragShared(View child, DragSource source, DragOptions options) { + Object dragObject = child.getTag(); + if (!(dragObject instanceof ItemInfo)) { + String msg = "Drag started with a view that has no tag set. This " + + "will cause a crash (issue 11627249) down the line. " + + "View: " + child + " tag: " + child.getTag(); + throw new IllegalStateException(msg); + } + beginDragShared(child, source, (ItemInfo) dragObject, + new DragPreviewProvider(child), options); + } + + private void beginDragShared(View child, DragSource source, + ItemInfo dragObject, DragPreviewProvider previewProvider, DragOptions options) { + + float iconScale = 1f; + if (child instanceof BubbleTextView) { + FastBitmapDrawable icon = ((BubbleTextView) child).getIcon(); + if (icon != null) { + iconScale = icon.getAnimatedScale(); + } + } + + // clear pressed state if necessary + child.clearFocus(); + child.setPressed(false); + if (child instanceof BubbleTextView) { + BubbleTextView icon = (BubbleTextView) child; + icon.clearPressedBackground(); + } + + DraggableView draggableView = null; + if (child instanceof DraggableView) { + draggableView = (DraggableView) child; + } + + final View contentView = previewProvider.getContentView(); + final float scale; + // The draggable drawable follows the touch point around on the screen + final Drawable drawable; + if (contentView == null) { + drawable = previewProvider.createDrawable(); + scale = previewProvider.getScaleAndPosition(drawable, mTempXY); + } else { + drawable = null; + scale = previewProvider.getScaleAndPosition(contentView, mTempXY); + } + int halfPadding = previewProvider.previewPadding / 2; + int dragLayerX = mTempXY[0]; + int dragLayerY = mTempXY[1]; + + Point dragVisualizeOffset = null; + Rect dragRect = new Rect(); + if (draggableView != null) { + draggableView.getSourceVisualDragBounds(dragRect); + dragLayerY += dragRect.top; + dragVisualizeOffset = new Point(-halfPadding, halfPadding); + } + if (contentView != null) { + mDragController.startDrag( + contentView, + draggableView, + dragLayerX, + dragLayerY, + source, + dragObject, + dragVisualizeOffset, + dragRect, + scale * iconScale, + scale, + options); + } else { + mDragController.startDrag( + drawable, + draggableView, + dragLayerX, + dragLayerY, + source, + dragObject, + dragVisualizeOffset, + dragRect, + scale * iconScale, + scale, + options); + } + } + + @Override + public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) { } + + @Override + public void onDragEnd() { } } diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayPredictions.java b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayPredictions.java new file mode 100644 index 0000000000..21c50d38e8 --- /dev/null +++ b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayPredictions.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.secondarydisplay; + +import android.content.Context; +import android.view.View; + +import com.android.launcher3.R; +import com.android.launcher3.allapps.ActivityAllAppsContainerView; +import com.android.launcher3.model.BgDataModel; +import com.android.launcher3.util.ResourceBasedOverride; + +/** + * Exposes Quickstep app prediction row APIs to {@link SecondaryDisplayLauncher}. + */ +public class SecondaryDisplayPredictions implements ResourceBasedOverride { + /** + * Creates a {@link SecondaryDisplayPredictions} instance. + */ + static SecondaryDisplayPredictions newInstance(Context context) { + return Overrides.getObject( + SecondaryDisplayPredictions.class, context, + R.string.secondary_display_predictions_class); + } + + /** + * Setup/update app divider separating app predictions from All Apps. + */ + void updateAppDivider() { + } + + /** + * Set predicted apps in top of app drawer. + */ + public void setPredictedApps(BgDataModel.FixedContainerItems item) { + } + + /** + * Set long click listener for predicted apps in top of app drawer. + */ + public void setLongClickListener( + ActivityAllAppsContainerView<?> appsView, + View.OnLongClickListener onIconLongClickListener) { + } +} diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDragController.java b/src/com/android/launcher3/secondarydisplay/SecondaryDragController.java new file mode 100644 index 0000000000..9bf27642ef --- /dev/null +++ b/src/com/android/launcher3/secondarydisplay/SecondaryDragController.java @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.secondarydisplay; + +import android.content.res.Resources; +import android.graphics.Point; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.util.Log; +import android.view.HapticFeedbackConstants; +import android.view.View; + +import androidx.annotation.Nullable; + +import com.android.launcher3.AbstractFloatingView; +import com.android.launcher3.DragSource; +import com.android.launcher3.DropTarget; +import com.android.launcher3.R; +import com.android.launcher3.accessibility.DragViewStateAnnouncer; +import com.android.launcher3.dragndrop.DragController; +import com.android.launcher3.dragndrop.DragDriver; +import com.android.launcher3.dragndrop.DragOptions; +import com.android.launcher3.dragndrop.DragView; +import com.android.launcher3.dragndrop.DraggableView; +import com.android.launcher3.model.data.ItemInfo; +import com.android.launcher3.testing.shared.TestProtocol; + +/** + * Drag controller for Secondary Launcher activity + */ +public class SecondaryDragController extends DragController<SecondaryDisplayLauncher> { + + private static final boolean PROFILE_DRAWING_DURING_DRAG = false; + + public SecondaryDragController(SecondaryDisplayLauncher secondaryLauncher) { + super(secondaryLauncher); + } + + @Override + protected DragView startDrag(@Nullable Drawable drawable, @Nullable View view, + DraggableView originalView, int dragLayerX, int dragLayerY, DragSource source, + ItemInfo dragInfo, Point dragOffset, Rect dragRegion, float initialDragViewScale, + float dragViewScaleOnDrop, DragOptions options) { + + if (TestProtocol.sDebugTracing) { + Log.d(TestProtocol.NO_DROP_TARGET, "5"); + } + + if (PROFILE_DRAWING_DURING_DRAG) { + android.os.Debug.startMethodTracing("Launcher"); + } + mActivity.hideKeyboard(); + + mOptions = options; + if (mOptions.simulatedDndStartPoint != null) { + mLastTouch.x = mMotionDown.x = mOptions.simulatedDndStartPoint.x; + mLastTouch.y = mMotionDown.y = mOptions.simulatedDndStartPoint.y; + } + + final int registrationX = mMotionDown.x - dragLayerX; + final int registrationY = mMotionDown.y - dragLayerY; + + final int dragRegionLeft = dragRegion == null ? 0 : dragRegion.left; + final int dragRegionTop = dragRegion == null ? 0 : dragRegion.top; + + mLastDropTarget = null; + + mDragObject = new DropTarget.DragObject(mActivity.getApplicationContext()); + mDragObject.originalView = originalView; + + mIsInPreDrag = mOptions.preDragCondition != null + && !mOptions.preDragCondition.shouldStartDrag(0); + + final Resources res = mActivity.getResources(); + final float scaleDps = mIsInPreDrag + ? res.getDimensionPixelSize(R.dimen.pre_drag_view_scale) : 0f; + + final DragView dragView = mDragObject.dragView = drawable != null + ? new SecondaryDragView( + mActivity, + drawable, + registrationX, + registrationY, + initialDragViewScale, + dragViewScaleOnDrop, + scaleDps) + : new SecondaryDragView( + mActivity, + view, + view.getMeasuredWidth(), + view.getMeasuredHeight(), + registrationX, + registrationY, + initialDragViewScale, + dragViewScaleOnDrop, + scaleDps); + dragView.setItemInfo(dragInfo); + mDragObject.dragComplete = false; + + mDragObject.xOffset = mMotionDown.x - (dragLayerX + dragRegionLeft); + mDragObject.yOffset = mMotionDown.y - (dragLayerY + dragRegionTop); + + mDragDriver = DragDriver.create(this, mOptions, ev -> { + }); + if (!mOptions.isAccessibleDrag) { + mDragObject.stateAnnouncer = DragViewStateAnnouncer.createFor(dragView); + } + + mDragObject.dragSource = source; + mDragObject.dragInfo = dragInfo; + mDragObject.originalDragInfo = mDragObject.dragInfo.makeShallowCopy(); + + if (dragOffset != null) { + dragView.setDragVisualizeOffset(new Point(dragOffset)); + } + if (dragRegion != null) { + dragView.setDragRegion(new Rect(dragRegion)); + } + + mActivity.getDragLayer().performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); + dragView.show(mLastTouch.x, mLastTouch.y); + mDistanceSinceScroll = 0; + + if (!mIsInPreDrag) { + callOnDragStart(); + } else if (mOptions.preDragCondition != null) { + mOptions.preDragCondition.onPreDragStart(mDragObject); + } + + handleMoveEvent(mLastTouch.x, mLastTouch.y); + return dragView; + } + + @Override + protected void exitDrag() { } + + @Override + protected DropTarget getDefaultDropTarget(int[] dropCoordinates) { + DropTarget target = new DropTarget() { + @Override + public boolean isDropEnabled() { + return true; + } + + @Override + public void onDrop(DragObject dragObject, DragOptions options) { + ((SecondaryDragLayer) mActivity.getDragLayer()).getPinnedAppsAdapter().addPinnedApp( + dragObject.dragInfo); + dragObject.dragView.remove(); + } + + @Override + public void onDragEnter(DragObject dragObject) { + if (getDistanceDragged() > mActivity.getResources().getDimensionPixelSize( + R.dimen.drag_distanceThreshold)) { + mActivity.showAppDrawer(false); + AbstractFloatingView.closeAllOpenViews(mActivity); + } + } + + @Override + public void onDragOver(DragObject dragObject) { } + + @Override + public void onDragExit(DragObject dragObject) { } + + @Override + public boolean acceptDrop(DragObject dragObject) { + return true; + } + + @Override + public void prepareAccessibilityDrop() { } + + @Override + public void getHitRectRelativeToDragLayer(Rect outRect) { } + }; + return target; + } +} diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java b/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java index c79d70dacc..c8455b8a2b 100644 --- a/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java +++ b/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java @@ -29,17 +29,23 @@ import android.widget.GridView; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.BubbleTextView; import com.android.launcher3.DeviceProfile; +import com.android.launcher3.DropTarget; import com.android.launcher3.R; import com.android.launcher3.allapps.ActivityAllAppsContainerView; +import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.dragndrop.DragOptions; +import com.android.launcher3.dragndrop.DragView; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.popup.PopupContainerWithArrow; import com.android.launcher3.popup.PopupDataProvider; +import com.android.launcher3.popup.SystemShortcut; import com.android.launcher3.util.ShortcutUtil; import com.android.launcher3.util.TouchController; import com.android.launcher3.views.BaseDragLayer; -import java.util.Arrays; +import java.util.ArrayList; import java.util.Collections; +import java.util.List; /** * DragLayer for Secondary launcher @@ -59,7 +65,8 @@ public class SecondaryDragLayer extends BaseDragLayer<SecondaryDisplayLauncher> @Override public void recreateControllers() { - mControllers = new TouchController[] {new CloseAllAppsTouchController()}; + mControllers = new TouchController[]{new CloseAllAppsTouchController(), + mActivity.getDragController()}; } /** @@ -72,7 +79,8 @@ public class SecondaryDragLayer extends BaseDragLayer<SecondaryDisplayLauncher> mAppsView = findViewById(R.id.apps_view); mAppsView.setOnIconLongClickListener(this::onIconLongClicked); - + mActivity.getSecondaryDisplayPredictions() + .setLongClickListener(mAppsView, this::onIconLongClicked); // Setup workspace mWorkspace = findViewById(R.id.workspace_grid); mPinnedAppsAdapter = new PinnedAppsAdapter(mActivity, mAppsView.getAppsStore(), @@ -166,6 +174,10 @@ public class SecondaryDragLayer extends BaseDragLayer<SecondaryDisplayLauncher> } } + public PinnedAppsAdapter getPinnedAppsAdapter() { + return mPinnedAppsAdapter; + } + private boolean onIconLongClicked(View v) { if (!(v instanceof BubbleTextView)) { return false; @@ -183,16 +195,58 @@ public class SecondaryDragLayer extends BaseDragLayer<SecondaryDisplayLauncher> if (popupDataProvider == null) { return false; } + + List<SystemShortcut> systemShortcuts = new ArrayList<>(); + + // Hide redundant pin shortcut for app drawer icons if drag-n-drop is enabled. + if (!FeatureFlags.SECONDARY_DRAG_N_DROP_TO_PIN.get() || !mActivity.isAppDrawerShown()) { + systemShortcuts.add(mPinnedAppsAdapter.getSystemShortcut(item, v)); + } + systemShortcuts.add(APP_INFO.getShortcut(mActivity, item, v)); + final PopupContainerWithArrow container = (PopupContainerWithArrow) mActivity.getLayoutInflater().inflate( R.layout.popup_container, mActivity.getDragLayer(), false); container.populateAndShow((BubbleTextView) v, popupDataProvider.getShortcutCountForItem(item), - Collections.emptyList(), - Arrays.asList(mPinnedAppsAdapter.getSystemShortcut(item, v), - APP_INFO.getShortcut(mActivity, item, v))); - v.getParent().requestDisallowInterceptTouchEvent(true); + Collections.emptyList(), systemShortcuts); + container.requestFocus(); + + if (!FeatureFlags.SECONDARY_DRAG_N_DROP_TO_PIN.get() || !mActivity.isAppDrawerShown()) { + return true; + } + + DragOptions options = new DragOptions(); + DeviceProfile grid = mActivity.getDeviceProfile(); + options.intrinsicIconScaleFactor = (float) grid.allAppsIconSizePx / grid.iconSizePx; + options.preDragCondition = container.createPreDragCondition(false); + if (options.preDragCondition == null) { + options.preDragCondition = new DragOptions.PreDragCondition() { + private DragView<SecondaryDisplayLauncher> mDragView; + + @Override + public boolean shouldStartDrag(double distanceDragged) { + return mDragView != null && mDragView.isAnimationFinished(); + } + + @Override + public void onPreDragStart(DropTarget.DragObject dragObject) { + mDragView = dragObject.dragView; + if (!shouldStartDrag(0)) { + mDragView.setOnAnimationEndCallback(() -> { + mActivity.beginDragShared(v, mActivity.getAppsView(), options); + }); + } + } + + @Override + public void onPreDragEnd(DropTarget.DragObject dragObject, boolean dragStarted) { + mDragView = null; + } + }; + } + mActivity.beginDragShared(v, mActivity.getAppsView(), options); return true; } } diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDragView.java b/src/com/android/launcher3/secondarydisplay/SecondaryDragView.java new file mode 100644 index 0000000000..0168b8fc0d --- /dev/null +++ b/src/com/android/launcher3/secondarydisplay/SecondaryDragView.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.secondarydisplay; + +import android.graphics.drawable.Drawable; +import android.view.View; + +import com.android.launcher3.R; +import com.android.launcher3.dragndrop.DragView; + +/** + * A DragView drawn/used by the Secondary Launcher activity. + */ +public class SecondaryDragView extends DragView<SecondaryDisplayLauncher> { + + public SecondaryDragView(SecondaryDisplayLauncher launcher, + Drawable drawable, + int registrationX, int registrationY, float initialScale, float scaleOnDrop, + float finalScaleDps) { + super(launcher, drawable, registrationX, registrationY, initialScale, scaleOnDrop, + finalScaleDps); + } + + public SecondaryDragView(SecondaryDisplayLauncher launcher, View content, int width, int height, + int registrationX, int registrationY, float initialScale, float scaleOnDrop, + float finalScaleDps) { + super(launcher, content, width, height, registrationX, registrationY, initialScale, + scaleOnDrop, finalScaleDps); + } + + @Override + public void animateTo(int toTouchX, int toTouchY, Runnable onCompleteRunnable, int duration) { + Runnable onAnimationEnd = () -> { + if (onCompleteRunnable != null) { + onCompleteRunnable.run(); + } + mActivity.getDragLayer().removeView(this); + }; + + duration = Math.max(duration, + getResources().getInteger(R.integer.config_dropAnimMinDuration)); + + animate() + .translationX(toTouchX - mRegistrationX) + .translationY(toTouchY - mRegistrationY) + .scaleX(mScaleOnDrop) + .scaleY(mScaleOnDrop) + .withEndAction(onAnimationEnd) + .setDuration(duration) + .start(); + } +} diff --git a/src/com/android/launcher3/settings/DeveloperOptionsFragment.java b/src/com/android/launcher3/settings/DeveloperOptionsFragment.java index b06b8a10bf..c81214e67b 100644 --- a/src/com/android/launcher3/settings/DeveloperOptionsFragment.java +++ b/src/com/android/launcher3/settings/DeveloperOptionsFragment.java @@ -59,10 +59,11 @@ import androidx.preference.PreferenceScreen; 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; @@ -369,6 +370,16 @@ public class DeveloperOptionsFragment extends PreferenceFragmentCompat { return true; }); sandboxCategory.addPreference(launchSandboxModeTutorialPreference); + + Preference launchSecondaryDisplayPreference = new Preference(context); + launchSecondaryDisplayPreference.setKey("launchSecondaryDisplay"); + launchSecondaryDisplayPreference.setTitle("Launch Secondary Display"); + launchSecondaryDisplayPreference.setSummary("Launch secondary display activity"); + launchSecondaryDisplayPreference.setOnPreferenceClickListener(preference -> { + startActivity(new Intent(context, SecondaryDisplayLauncher.class)); + return true; + }); + sandboxCategory.addPreference(launchSecondaryDisplayPreference); } private void addOnboardingPrefsCatergory() { @@ -381,7 +392,8 @@ public class DeveloperOptionsFragment extends PreferenceFragmentCompat { 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 49d27b7ed4..7ab3013da1 100644 --- a/src/com/android/launcher3/settings/SettingsActivity.java +++ b/src/com/android/launcher3/settings/SettingsActivity.java @@ -18,6 +18,7 @@ package com.android.launcher3.settings; 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 androidx.recyclerview.widget.RecyclerView; 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; @@ -112,7 +115,8 @@ public class SettingsActivity extends FragmentActivity // 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 +211,11 @@ public class SettingsActivity extends FragmentActivity 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 +268,14 @@ public class SettingsActivity extends FragmentActivity 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 c166bfc83a..030673024a 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.graphics.drawable.Drawable; 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 @@ public class ShortcutDragPreviewProvider extends DragPreviewProvider { @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 f9a36ad17c..239042550a 100644 --- a/src/com/android/launcher3/statemanager/BaseState.java +++ b/src/com/android/launcher3/statemanager/BaseState.java @@ -18,7 +18,7 @@ package com.android.launcher3.statemanager; 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 @@ public interface BaseState<T extends BaseState> { /** * @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 @@ public interface BaseState<T extends BaseState> { 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 2aa9ddeb50..ad1e7f02b5 100644 --- a/src/com/android/launcher3/statemanager/StateManager.java +++ b/src/com/android/launcher3/statemanager/StateManager.java @@ -376,12 +376,16 @@ public class StateManager<STATE_TYPE extends BaseState<STATE_TYPE>> { } 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; } diff --git a/src/com/android/launcher3/statemanager/StatefulActivity.java b/src/com/android/launcher3/statemanager/StatefulActivity.java index c554d069f3..520f33ca74 100644 --- a/src/com/android/launcher3/statemanager/StatefulActivity.java +++ b/src/com/android/launcher3/statemanager/StatefulActivity.java @@ -15,19 +15,27 @@ */ 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; import androidx.annotation.CallSuper; +import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.LauncherRootView; 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; @@ -45,6 +53,17 @@ public abstract class StatefulActivity<STATE_TYPE extends BaseState<STATE_TYPE>> 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 */ @@ -87,6 +106,10 @@ public abstract class StatefulActivity<STATE_TYPE extends BaseState<STATE_TYPE>> if (mDeferredResumePending) { handleDeferredResume(); } + + if (state.hasFlag(FLAG_CLOSE_POPUPS)) { + AbstractFloatingView.closeAllOpenViews(this, !state.hasFlag(FLAG_NON_INTERACTIVE)); + } } /** @@ -180,4 +203,38 @@ public abstract class StatefulActivity<STATE_TYPE extends BaseState<STATE_TYPE>> 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 38b62d466e..e5b4ebae1d 100644 --- a/src/com/android/launcher3/states/RotationHelper.java +++ b/src/com/android/launcher3/states/RotationHelper.java @@ -21,34 +21,39 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; 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 class RotationHelper implements OnSharedPreferenceChangeListener, 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 class RotationHelper implements OnSharedPreferenceChangeListener, 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 @@ public class RotationHelper implements OnSharedPreferenceChangeListener, 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 class RotationHelper implements OnSharedPreferenceChangeListener, 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 class RotationHelper implements OnSharedPreferenceChangeListener, 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,8 +210,17 @@ public class RotationHelper implements OnSharedPreferenceChangeListener, } 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; } /** diff --git a/src/com/android/launcher3/states/SpringLoadedState.java b/src/com/android/launcher3/states/SpringLoadedState.java index a205ab55ca..f5d511c7d3 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 @@ public class SpringLoadedState extends LauncherState { 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 @@ public class SpringLoadedState extends LauncherState { @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 @@ public class SpringLoadedState extends LauncherState { } 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 @@ public class SpringLoadedState extends LauncherState { @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 f99519d6e9..54735f0094 100644 --- a/src/com/android/launcher3/states/StateAnimationConfig.java +++ b/src/com/android/launcher3/states/StateAnimationConfig.java @@ -64,6 +64,8 @@ public class StateAnimationConfig { 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 class StateAnimationConfig { 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 242d2d423c..acb7eb38a2 100644 --- a/src/com/android/launcher3/testing/TestInformationHandler.java +++ b/src/com/android/launcher3/testing/TestInformationHandler.java @@ -33,6 +33,7 @@ import androidx.annotation.Nullable; import com.android.launcher3.CellLayout; import com.android.launcher3.DeviceProfile; +import com.android.launcher3.Hotseat; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppState; @@ -40,10 +41,15 @@ import com.android.launcher3.LauncherState; import com.android.launcher3.R; import com.android.launcher3.Workspace; import com.android.launcher3.dragndrop.DragLayer; +import com.android.launcher3.testing.shared.HotseatCellCenterRequest; +import com.android.launcher3.testing.shared.TestProtocol; +import com.android.launcher3.testing.shared.WorkspaceCellCenterRequest; 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; @@ -108,12 +114,12 @@ public class TestInformationHandler implements ResourceBasedOverride { 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: { @@ -153,10 +159,6 @@ public class TestInformationHandler implements ResourceBasedOverride { mDeviceProfile.isTwoPanels); return response; - case TestProtocol.REQUEST_SET_FORCE_PAUSE_TIMEOUT: - TestProtocol.sForcePauseTimeout = Long.parseLong(arg); - return response; - case TestProtocol.REQUEST_GET_HAD_NONTEST_EVENTS: response.putBoolean( TestProtocol.TEST_INFO_RESPONSE_FIELD, TestLogging.sHadEventsNotFromTest); @@ -185,7 +187,7 @@ public class TestInformationHandler implements ResourceBasedOverride { return new int[]{cellLayout.getCountX(), cellLayout.getCountY()}; }); - case TestProtocol.REQUEST_WORKSPACE_CELL_CENTER: + case TestProtocol.REQUEST_WORKSPACE_CELL_CENTER: { final WorkspaceCellCenterRequest request = extra.getParcelable( TestProtocol.TEST_INFO_REQUEST_FIELD); return getLauncherUIProperty(Bundle::putParcelable, launcher -> { @@ -197,13 +199,32 @@ public class TestInformationHandler implements ResourceBasedOverride { cellLayout, request.cellX, request.cellY, request.spanX, request.spanY); return new Point(cellRect.centerX(), cellRect.centerY()); }); + } + + case TestProtocol.REQUEST_HOTSEAT_CELL_CENTER: { + final HotseatCellCenterRequest request = extra.getParcelable( + TestProtocol.TEST_INFO_REQUEST_FIELD); + return getLauncherUIProperty(Bundle::putParcelable, launcher -> { + final Hotseat hotseat = launcher.getHotseat(); + final Rect cellRect = getDescendantRectRelativeToDragLayerForCell(launcher, + hotseat, request.cellInd, /* cellY= */ 0, + /* spanX= */ 1, /* spanY= */ 1); + // TODO(b/234322284): return the real center point. + return new Point(cellRect.left + (cellRect.right - cellRect.left) / 3, + cellRect.top + (cellRect.bottom - cellRect.top) / 3); + }); + } 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; } @@ -246,17 +267,24 @@ public class TestInformationHandler implements ResourceBasedOverride { */ 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 bcc7c2de4e..5444d928bf 100644 --- a/src/com/android/launcher3/testing/TestInformationProvider.java +++ b/src/com/android/launcher3/testing/TestInformationProvider.java @@ -21,10 +21,14 @@ import android.content.ContentValues; 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 @@ public class TestInformationProvider extends ContentProvider { 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/TestLogging.java b/src/com/android/launcher3/testing/TestLogging.java index 103b565140..c1516065fc 100644 --- a/src/com/android/launcher3/testing/TestLogging.java +++ b/src/com/android/launcher3/testing/TestLogging.java @@ -22,6 +22,7 @@ import android.view.KeyEvent; import android.view.MotionEvent; import com.android.launcher3.Utilities; +import com.android.launcher3.testing.shared.TestProtocol; import java.util.function.BiConsumer; diff --git a/src/com/android/launcher3/testing/shared/HotseatCellCenterRequest.java b/src/com/android/launcher3/testing/shared/HotseatCellCenterRequest.java new file mode 100644 index 0000000000..7eb035a81f --- /dev/null +++ b/src/com/android/launcher3/testing/shared/HotseatCellCenterRequest.java @@ -0,0 +1,99 @@ +/* + * 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.testing.shared; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Request object for querying a hotseat cell region in Rect. + */ +public class HotseatCellCenterRequest implements TestInformationRequest { + public final int cellInd; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(cellInd); + } + + @Override + public String getRequestName() { + return TestProtocol.REQUEST_HOTSEAT_CELL_CENTER; + } + + public static final Parcelable.Creator<HotseatCellCenterRequest> CREATOR = + new Parcelable.Creator<HotseatCellCenterRequest>() { + + @Override + public HotseatCellCenterRequest createFromParcel(Parcel source) { + return new HotseatCellCenterRequest(source); + } + + @Override + public HotseatCellCenterRequest[] newArray(int size) { + return new HotseatCellCenterRequest[size]; + } + }; + + private HotseatCellCenterRequest(int cellInd) { + this.cellInd = cellInd; + } + + private HotseatCellCenterRequest(Parcel in) { + this(in.readInt()); + } + + /** + * Create a builder for HotseatCellCenterRequest. + * + * @return HotseatCellCenterRequest builder. + */ + public static HotseatCellCenterRequest.Builder builder() { + return new HotseatCellCenterRequest.Builder(); + } + + /** + * HotseatCellCenterRequest Builder. + */ + public static final class Builder { + private int mCellInd; + + private Builder() { + mCellInd = 0; + } + + /** + * Set the index of hotseat cells. + */ + public HotseatCellCenterRequest.Builder setCellInd(int i) { + this.mCellInd = i; + return this; + } + + /** + * build the HotseatCellCenterRequest. + */ + public HotseatCellCenterRequest build() { + return new HotseatCellCenterRequest(mCellInd); + } + } +} diff --git a/src/com/android/launcher3/ResourceUtils.java b/src/com/android/launcher3/testing/shared/ResourceUtils.java index f709acabed..d0ae2581c7 100644 --- a/src/com/android/launcher3/ResourceUtils.java +++ b/src/com/android/launcher3/testing/shared/ResourceUtils.java @@ -14,13 +14,14 @@ * limitations under the License. */ -package com.android.launcher3; +package com.android.launcher3.testing.shared; import android.content.res.Resources; import android.util.DisplayMetrics; 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 class ResourceUtils { 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 class ResourceUtils { } 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/TestInformationRequest.java b/src/com/android/launcher3/testing/shared/TestInformationRequest.java index 272ae56a30..3828203280 100644 --- a/src/com/android/launcher3/testing/TestInformationRequest.java +++ b/src/com/android/launcher3/testing/shared/TestInformationRequest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.launcher3.testing; +package com.android.launcher3.testing.shared; import android.os.Parcelable; diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/shared/TestProtocol.java index 3a030a8b87..f5ee91b297 100644 --- a/src/com/android/launcher3/testing/TestProtocol.java +++ b/src/com/android/launcher3/testing/shared/TestProtocol.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.launcher3.testing; +package com.android.launcher3.testing.shared; /** * Protocol for custom accessibility events for communication with UI Automation tests. @@ -84,8 +84,13 @@ public final class TestProtocol { 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 final class TestProtocol { 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,19 +117,23 @@ public final class TestProtocol { "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_HOTSEAT_CELL_CENTER = "hotseat-cell-center"; + public static final String REQUEST_GET_FOCUSED_TASK_HEIGHT_FOR_TABLET = "get-focused-task-height-for-tablet"; public static final String REQUEST_GET_GRID_TASK_SIZE_RECT_FOR_TABLET = "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 Long sForcePauseTimeout; - public static final String REQUEST_SET_FORCE_PAUSE_TIMEOUT = "set-force-pause-timeout"; + 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"; @@ -137,6 +147,11 @@ public final class TestProtocol { 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"; + public static final String REQUEST_IS_EMULATE_DISPLAY_RUNNING = "is-emulate-display-running"; + public static final String REQUEST_EMULATE_PRINT_DEVICE = "emulate-print-device"; } diff --git a/src/com/android/launcher3/testing/WorkspaceCellCenterRequest.java b/src/com/android/launcher3/testing/shared/WorkspaceCellCenterRequest.java index 71ab09f3f2..80dbef86b9 100644 --- a/src/com/android/launcher3/testing/WorkspaceCellCenterRequest.java +++ b/src/com/android/launcher3/testing/shared/WorkspaceCellCenterRequest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.launcher3.testing; +package com.android.launcher3.testing.shared; import android.os.Parcel; import android.os.Parcelable; diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java index 09b8228182..8f7a4ecfd1 100644 --- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java +++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java @@ -32,6 +32,7 @@ import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFram import android.animation.Animator.AnimatorListener; import android.animation.ValueAnimator; +import android.util.Log; import android.view.MotionEvent; import com.android.launcher3.Launcher; @@ -211,6 +212,10 @@ public abstract class AbstractStateChangeTouchController mFlingBlockCheck.blockFling(); } } + if (mFromState == LauncherState.ALL_APPS) { + mAllAppsOvershootStarted = true; + mLauncher.getAppsView().onPull(-progress , -progress); + } } else if (progress >= 1) { if (reinitCurrentAnimation(true, isDragTowardPositive)) { mDisplacementShift = displacement; diff --git a/src/com/android/launcher3/touch/AllAppsSwipeController.java b/src/com/android/launcher3/touch/AllAppsSwipeController.java index db43baa4d8..5279dec73b 100644 --- a/src/com/android/launcher3/touch/AllAppsSwipeController.java +++ b/src/com/android/launcher3/touch/AllAppsSwipeController.java @@ -18,6 +18,7 @@ package com.android.launcher3.touch; 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; @@ -47,48 +48,88 @@ import com.android.launcher3.states.StateAnimationConfig; */ public class AllAppsSwipeController extends AbstractStateChangeTouchController { - private static final float ALLAPPS_STAGGERED_FADE_THRESHOLD = 0.5f; + private static final float ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD = 0.8f; + private static final float ALL_APPS_CONTENT_FADE_MIN_CLAMPING_THRESHOLD = 0.5f; + private static final float ALL_APPS_SCRIM_VISIBLE_THRESHOLD = 0.1f; + private static final float ALL_APPS_STAGGERED_FADE_THRESHOLD = 0.5f; - // Custom timing for NORMAL -> ALL_APPS on phones only. - private static final float WORKSPACE_MOTION_START = 0.1667f; - private static final float ALL_APPS_STATE_TRANSITION = 0.305f; - private static final float ALL_APPS_FADE_END = 0.4717f; + public static final Interpolator ALL_APPS_SCRIM_RESPONDER = + Interpolators.clampToProgress( + LINEAR, ALL_APPS_SCRIM_VISIBLE_THRESHOLD, ALL_APPS_STAGGERED_FADE_THRESHOLD); + public static final Interpolator ALL_APPS_CLAMPING_RESPONDER = + Interpolators.clampToProgress( + LINEAR, + 1 - ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD, + 1 - ALL_APPS_CONTENT_FADE_MIN_CLAMPING_THRESHOLD); + + // ---- 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; private static final float ALL_APPS_FULL_DEPTH_PROGRESS = 0.5f; - public static final Interpolator ALLAPPS_STAGGERED_FADE_EARLY_RESPONDER = - Interpolators.clampToProgress(LINEAR, 0, ALLAPPS_STAGGERED_FADE_THRESHOLD); - public static final Interpolator ALLAPPS_STAGGERED_FADE_LATE_RESPONDER = - Interpolators.clampToProgress(LINEAR, ALLAPPS_STAGGERED_FADE_THRESHOLD, 1f); + private static final Interpolator LINEAR_EARLY_MANUAL = + Interpolators.clampToProgress(LINEAR, 0f, ALL_APPS_STATE_TRANSITION_MANUAL); + private static final Interpolator STEP_TRANSITION_ATOMIC = + Interpolators.clampToProgress(FINAL_FRAME, 0f, ALL_APPS_STATE_TRANSITION_ATOMIC); + private static final Interpolator STEP_TRANSITION_MANUAL = + Interpolators.clampToProgress(FINAL_FRAME, 0f, ALL_APPS_STATE_TRANSITION_MANUAL); - // Custom interpolators for NORMAL -> ALL_APPS on phones only. // The blur to All Apps is set to be complete when the interpolator is at 0.5. - public static final Interpolator BLUR = + private static final Interpolator BLUR_ADJUSTED = + Interpolators.mapToProgress(LINEAR, 0f, ALL_APPS_FULL_DEPTH_PROGRESS); + public static final Interpolator BLUR_ATOMIC = Interpolators.clampToProgress( - Interpolators.mapToProgress( - LINEAR, 0f, ALL_APPS_FULL_DEPTH_PROGRESS), - WORKSPACE_MOTION_START, ALL_APPS_STATE_TRANSITION); - public static final Interpolator WORKSPACE_FADE = - Interpolators.clampToProgress(FINAL_FRAME, 0f, ALL_APPS_STATE_TRANSITION); - public static final Interpolator WORKSPACE_SCALE = + BLUR_ADJUSTED, WORKSPACE_MOTION_START_ATOMIC, ALL_APPS_STATE_TRANSITION_ATOMIC); + public static final Interpolator BLUR_MANUAL = + Interpolators.clampToProgress(BLUR_ADJUSTED, 0f, ALL_APPS_STATE_TRANSITION_MANUAL); + + public static final Interpolator WORKSPACE_FADE_ATOMIC = STEP_TRANSITION_ATOMIC; + public static final Interpolator WORKSPACE_FADE_MANUAL = STEP_TRANSITION_MANUAL; + + public static final Interpolator WORKSPACE_SCALE_ATOMIC = Interpolators.clampToProgress( - EMPHASIZED_ACCELERATE, WORKSPACE_MOTION_START, ALL_APPS_STATE_TRANSITION); - public static final Interpolator HOTSEAT_FADE = WORKSPACE_FADE; - public static final Interpolator HOTSEAT_SCALE = HOTSEAT_FADE; - public static final Interpolator HOTSEAT_TRANSLATE = + EMPHASIZED_ACCELERATE, WORKSPACE_MOTION_START_ATOMIC, + ALL_APPS_STATE_TRANSITION_ATOMIC); + public static final Interpolator WORKSPACE_SCALE_MANUAL = LINEAR_EARLY_MANUAL; + + 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 = Interpolators.clampToProgress( - EMPHASIZED_ACCELERATE, WORKSPACE_MOTION_START, ALL_APPS_STATE_TRANSITION); - public static final Interpolator SCRIM_FADE = + EMPHASIZED_ACCELERATE, WORKSPACE_MOTION_START_ATOMIC, + ALL_APPS_STATE_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, ALL_APPS_STATE_TRANSITION); - public static final Interpolator ALL_APPS_FADE = + WORKSPACE_MOTION_START_ATOMIC, ALL_APPS_STATE_TRANSITION_ATOMIC); + public static final Interpolator SCRIM_FADE_MANUAL = LINEAR_EARLY_MANUAL; + + public static final Interpolator ALL_APPS_FADE_ATOMIC = + Interpolators.clampToProgress( + Interpolators.mapToProgress(DECELERATED_EASE, 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); + + public static final Interpolator ALL_APPS_VERTICAL_PROGRESS_ATOMIC = Interpolators.clampToProgress( - Interpolators.mapToProgress(DECELERATED_EASE, 0.2f, 1.0f), - ALL_APPS_STATE_TRANSITION, ALL_APPS_FADE_END); - public static final Interpolator ALL_APPS_VERTICAL_PROGRESS = + Interpolators.mapToProgress(EMPHASIZED_DECELERATE, 0.4f, 1f), + ALL_APPS_STATE_TRANSITION_ATOMIC, 1f); + public static final Interpolator ALL_APPS_VERTICAL_PROGRESS_MANUAL = Interpolators.clampToProgress( - Interpolators.mapToProgress(EMPHASIZED_DECELERATE, 0.4f, 1.0f), - ALL_APPS_STATE_TRANSITION, 1.0f); + Interpolators.mapToProgress(LINEAR, ALL_APPS_STATE_TRANSITION_MANUAL, 1f), + ALL_APPS_STATE_TRANSITION_MANUAL, 1f); + + // -------- public AllAppsSwipeController(Launcher l) { super(l, SingleAxisSwipeDetector.VERTICAL); @@ -141,6 +182,7 @@ public class AllAppsSwipeController extends AbstractStateChangeTouchController { protected StateAnimationConfig getConfigForStates(LauncherState fromState, LauncherState toState) { StateAnimationConfig config = super.getConfigForStates(fromState, toState); + config.userControlled = true; if (fromState == NORMAL && toState == ALL_APPS) { applyNormalToAllAppsAnimConfig(mLauncher, config); } else if (fromState == ALL_APPS && toState == NORMAL) { @@ -150,36 +192,79 @@ public class AllAppsSwipeController extends AbstractStateChangeTouchController { } /** - * Applies Animation config values for transition from all apps to home + * Applies Animation config values for transition from all apps to home. */ public static void applyAllAppsToNormalConfig(Launcher launcher, StateAnimationConfig config) { - boolean isTablet = launcher.getDeviceProfile().isTablet; - config.setInterpolator(ANIM_SCRIM_FADE, ALLAPPS_STAGGERED_FADE_LATE_RESPONDER); - config.setInterpolator(ANIM_ALL_APPS_FADE, isTablet - ? FINAL_FRAME : ALLAPPS_STAGGERED_FADE_EARLY_RESPONDER); - if (!isTablet) { - config.setInterpolator(ANIM_WORKSPACE_FADE, INSTANT); + if (launcher.getDeviceProfile().isTablet) { + config.setInterpolator(ANIM_SCRIM_FADE, + Interpolators.reverse(ALL_APPS_SCRIM_RESPONDER)); + config.setInterpolator(ANIM_ALL_APPS_FADE, FINAL_FRAME); + if (!config.userControlled) { + 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)); + config.setInterpolator(ANIM_WORKSPACE_FADE, + Interpolators.reverse(WORKSPACE_FADE_MANUAL)); + config.setInterpolator(ANIM_WORKSPACE_SCALE, + Interpolators.reverse(WORKSPACE_SCALE_MANUAL)); + config.setInterpolator(ANIM_HOTSEAT_FADE, + Interpolators.reverse(HOTSEAT_FADE_MANUAL)); + config.setInterpolator(ANIM_HOTSEAT_SCALE, + Interpolators.reverse(HOTSEAT_SCALE_MANUAL)); + config.setInterpolator(ANIM_HOTSEAT_TRANSLATE, + Interpolators.reverse(HOTSEAT_TRANSLATE_MANUAL)); + config.setInterpolator(ANIM_SCRIM_FADE, Interpolators.reverse(SCRIM_FADE_MANUAL)); + config.setInterpolator(ANIM_ALL_APPS_FADE, + Interpolators.reverse(ALL_APPS_FADE_MANUAL)); + config.setInterpolator(ANIM_VERTICAL_PROGRESS, + Interpolators.reverse(ALL_APPS_VERTICAL_PROGRESS_MANUAL)); + } else { + config.setInterpolator(ANIM_SCRIM_FADE, + Interpolators.reverse(ALL_APPS_SCRIM_RESPONDER)); + config.setInterpolator(ANIM_ALL_APPS_FADE, ALL_APPS_CLAMPING_RESPONDER); + config.setInterpolator(ANIM_WORKSPACE_FADE, INSTANT); + config.setInterpolator(ANIM_VERTICAL_PROGRESS, EMPHASIZED_ACCELERATE); + } } } /** - * Applies Animation config values for transition from home to all apps + * Applies Animation config values for transition from home to all apps. */ - public static void applyNormalToAllAppsAnimConfig(Launcher launcher, - StateAnimationConfig config) { + public static void applyNormalToAllAppsAnimConfig( + Launcher launcher, StateAnimationConfig config) { if (launcher.getDeviceProfile().isTablet) { - config.setInterpolator(ANIM_SCRIM_FADE, ALLAPPS_STAGGERED_FADE_EARLY_RESPONDER); 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); + } + config.setInterpolator(ANIM_WORKSPACE_SCALE, EMPHASIZED); + config.setInterpolator(ANIM_DEPTH, EMPHASIZED); } else { - config.setInterpolator(ANIM_DEPTH, BLUR); - config.setInterpolator(ANIM_WORKSPACE_FADE, WORKSPACE_FADE); - config.setInterpolator(ANIM_WORKSPACE_SCALE, WORKSPACE_SCALE); - config.setInterpolator(ANIM_HOTSEAT_FADE, HOTSEAT_FADE); - config.setInterpolator(ANIM_HOTSEAT_SCALE, HOTSEAT_SCALE); - config.setInterpolator(ANIM_HOTSEAT_TRANSLATE, HOTSEAT_TRANSLATE); - config.setInterpolator(ANIM_SCRIM_FADE, SCRIM_FADE); - config.setInterpolator(ANIM_ALL_APPS_FADE, ALL_APPS_FADE); - config.setInterpolator(ANIM_VERTICAL_PROGRESS, ALL_APPS_VERTICAL_PROGRESS); + config.setInterpolator(ANIM_DEPTH, config.userControlled ? BLUR_MANUAL : BLUR_ATOMIC); + config.setInterpolator(ANIM_WORKSPACE_FADE, + config.userControlled ? WORKSPACE_FADE_MANUAL : WORKSPACE_FADE_ATOMIC); + config.setInterpolator(ANIM_WORKSPACE_SCALE, + config.userControlled ? WORKSPACE_SCALE_MANUAL : WORKSPACE_SCALE_ATOMIC); + config.setInterpolator(ANIM_HOTSEAT_FADE, + config.userControlled ? HOTSEAT_FADE_MANUAL : HOTSEAT_FADE_ATOMIC); + config.setInterpolator(ANIM_HOTSEAT_SCALE, + config.userControlled ? HOTSEAT_SCALE_MANUAL : HOTSEAT_SCALE_ATOMIC); + config.setInterpolator(ANIM_HOTSEAT_TRANSLATE, + config.userControlled ? HOTSEAT_TRANSLATE_MANUAL : HOTSEAT_TRANSLATE_ATOMIC); + config.setInterpolator(ANIM_SCRIM_FADE, + config.userControlled ? SCRIM_FADE_MANUAL : SCRIM_FADE_ATOMIC); + config.setInterpolator(ANIM_ALL_APPS_FADE, + config.userControlled ? ALL_APPS_FADE_MANUAL : ALL_APPS_FADE_ATOMIC); + config.setInterpolator(ANIM_VERTICAL_PROGRESS, + config.userControlled + ? ALL_APPS_VERTICAL_PROGRESS_MANUAL + : ALL_APPS_VERTICAL_PROGRESS_ATOMIC); } } } diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java index e95a787cfd..b7e01057b9 100644 --- a/src/com/android/launcher3/touch/ItemClickHandler.java +++ b/src/com/android/launcher3/touch/ItemClickHandler.java @@ -17,8 +17,6 @@ package com.android.launcher3.touch; 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; @@ -27,10 +25,8 @@ import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_SA import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED; 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; @@ -56,12 +52,11 @@ import com.android.launcher3.model.data.FolderInfo; 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; import com.android.launcher3.testing.TestLogging; -import com.android.launcher3.testing.TestProtocol; +import com.android.launcher3.testing.shared.TestProtocol; import com.android.launcher3.util.ItemInfoMatcher; import com.android.launcher3.util.PackageManagerHelper; import com.android.launcher3.views.FloatingIconView; @@ -105,8 +100,8 @@ public class ItemClickHandler { 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); } } @@ -248,13 +243,19 @@ public class ItemClickHandler { 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 +305,6 @@ public class ItemClickHandler { 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 +345,15 @@ public class ItemClickHandler { } 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 6bae745ef8..1421eced17 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; 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; @@ -36,8 +35,9 @@ import com.android.launcher3.folder.Folder; import com.android.launcher3.logging.StatsLogManager.StatsLogger; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.testing.TestLogging; -import com.android.launcher3.testing.TestProtocol; +import com.android.launcher3.testing.shared.TestProtocol; import com.android.launcher3.views.BubbleTextHolder; +import com.android.launcher3.widget.LauncherAppWidgetHostView; /** * Class to handle long-clicks on workspace items and start drag as a result. @@ -51,7 +51,11 @@ public class ItemLongClickListener { ItemLongClickListener::onAllAppsItemLongClick; private static boolean onWorkspaceItemLongClick(View v) { - TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onWorkspaceItemLongClick"); + if (v instanceof LauncherAppWidgetHostView) { + TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "Widgets.onLongClick"); + } else { + TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onWorkspaceItemLongClick"); + } Launcher launcher = Launcher.getLauncher(v.getContext()); if (!canStartDrag(launcher)) return false; if (!launcher.isInState(NORMAL) && !launcher.isInState(OVERVIEW)) return false; @@ -113,10 +117,7 @@ public class ItemLongClickListener { } }); - 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 b477905dff..097823b078 100644 --- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java +++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java @@ -19,6 +19,7 @@ package com.android.launcher3.touch; import static android.view.Gravity.BOTTOM; import static android.view.Gravity.CENTER_VERTICAL; import static android.view.Gravity.END; +import static android.view.Gravity.LEFT; import static android.view.Gravity.START; import static android.view.Gravity.TOP; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; @@ -29,6 +30,7 @@ import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y; 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; @@ -50,9 +52,9 @@ import com.android.launcher3.DeviceProfile; import com.android.launcher3.R; import com.android.launcher3.Utilities; 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.StagedSplitBounds; import com.android.launcher3.views.BaseDragLayer; import java.util.Collections; @@ -264,20 +266,32 @@ public class LandscapePagedViewHandler implements PagedOrientationHandler { } @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 @@ -299,19 +313,8 @@ public class LandscapePagedViewHandler implements PagedOrientationHandler { } @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, StagedSplitBounds splitBounds, DeviceProfile deviceProfile, + int taskViewHeight, SplitBounds splitBounds, DeviceProfile deviceProfile, View[] thumbnailViews, int desiredTaskId, View banner) { boolean isRtl = banner.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; float translationX = 0; @@ -410,8 +413,8 @@ public class LandscapePagedViewHandler implements PagedOrientationHandler { // 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 @@ -424,15 +427,45 @@ public class LandscapePagedViewHandler implements PagedOrientationHandler { } @Override - public void updateStagedSplitIconParams(View out, float onScreenRectCenterX, + public void updateSplitIconParams(View out, float onScreenRectCenterX, 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(splitInstructionsHeight); + out.setRotation(getDegreesRotated()); + int distanceToEdge = out.getResources().getDimensionPixelSize( + R.dimen.split_instructions_bottom_margin_phone_landscape); + // Adjust for any insets on the left edge + 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(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 + // view to be screen-left when phone is in landscape, regardless of the RtL setting. + lp.gravity = LEFT | CENTER_VERTICAL; + out.setLayoutParams(lp); } @Override @@ -447,7 +480,7 @@ public class LandscapePagedViewHandler implements PagedOrientationHandler { @Override public void setSplitTaskSwipeRect(DeviceProfile dp, Rect outRect, - StagedSplitBounds splitInfo, int desiredStagePosition) { + SplitBounds splitInfo, int desiredStagePosition) { float topLeftTaskPercent = splitInfo.appsStackedVertically ? splitInfo.topTaskPercent : splitInfo.leftTaskPercent; @@ -464,13 +497,13 @@ public class LandscapePagedViewHandler implements PagedOrientationHandler { @Override public void measureGroupedTaskViewThumbnailBounds(View primarySnapshot, View secondarySnapshot, - int parentWidth, int parentHeight, StagedSplitBounds splitBoundsConfig, + int parentWidth, int parentHeight, SplitBounds splitBoundsConfig, 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; @@ -500,13 +533,14 @@ public class LandscapePagedViewHandler implements PagedOrientationHandler { iconParams.rightMargin = -taskIconHeight - taskIconMargin / 2; iconParams.leftMargin = 0; iconParams.topMargin = thumbnailTopMargin / 2; + iconParams.bottomMargin = 0; } @Override public void setSplitIconParams(View primaryIconView, View secondaryIconView, int taskIconHeight, int primarySnapshotWidth, int primarySnapshotHeight, int groupedTaskViewHeight, int groupedTaskViewWidth, boolean isRtl, - DeviceProfile deviceProfile, StagedSplitBounds splitConfig) { + DeviceProfile deviceProfile, SplitBounds splitConfig) { FrameLayout.LayoutParams primaryIconParams = (FrameLayout.LayoutParams) primaryIconView.getLayoutParams(); FrameLayout.LayoutParams secondaryIconParams = @@ -515,7 +549,8 @@ public class LandscapePagedViewHandler implements PagedOrientationHandler { // We calculate the "midpoint" of the thumbnail area, and place the icons there. // This is the place where the thumbnail area splits by default, in a near-50/50 split. // It is usually not exactly 50/50, due to insets/screen cutouts. - int fullscreenInsetThickness = deviceProfile.getInsets().top; + int fullscreenInsetThickness = deviceProfile.getInsets().top + - deviceProfile.getInsets().bottom; int fullscreenMidpointFromBottom = ((deviceProfile.heightPx - fullscreenInsetThickness) / 2); float midpointFromBottomPct = (float) fullscreenMidpointFromBottom / deviceProfile.heightPx; @@ -556,4 +591,22 @@ public class LandscapePagedViewHandler implements PagedOrientationHandler { 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 ca46aa8a84..623446288f 100644 --- a/src/com/android/launcher3/touch/PagedOrientationHandler.java +++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java @@ -34,9 +34,9 @@ import android.widget.LinearLayout; 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.StagedSplitBounds; import java.util.List; @@ -129,12 +129,22 @@ public interface PagedOrientationHandler { * @param dp The device profile, used to report rotation and hardware insets. * @param stagePosition 0 if the staging area is pinned to top/left, 1 for bottom/right. */ - void updateStagedSplitIconParams(View out, float onScreenRectCenterX, + void updateSplitIconParams(View out, float onScreenRectCenterX, float onScreenRectCenterY, float fullscreenScaleX, float fullscreenScaleY, int drawableWidth, int drawableHeight, DeviceProfile dp, @StagePosition int stagePosition); /** + * Sets positioning and rotation for a SplitInstructionsView. + * @param out The SplitInstructionsView that needs to be positioned. + * @param dp The device profile, used to report rotation and device type. + * @param splitInstructionsHeight The SplitInstructionView's height. + * @param splitInstructionsWidth The SplitInstructionView's width. + */ + void setSplitInstructionsParams(View out, DeviceProfile dp, int splitInstructionsHeight, + int splitInstructionsWidth, int threeButtonNavShift); + + /** * @param splitDividerSize height of split screen drag handle in portrait, width in landscape * @param stagePosition the split position option (top/left, bottom/right) of the first * task selected for entering split @@ -153,12 +163,12 @@ public interface PagedOrientationHandler { * @param desiredStagePosition Which stage position (topLeft/rightBottom) we want to resize * outRect for */ - void setSplitTaskSwipeRect(DeviceProfile dp, Rect outRect, StagedSplitBounds splitInfo, + void setSplitTaskSwipeRect(DeviceProfile dp, Rect outRect, SplitBounds splitInfo, @SplitConfigurationOptions.StagePosition int desiredStagePosition); void measureGroupedTaskViewThumbnailBounds(View primarySnapshot, View secondarySnapshot, int parentWidth, int parentHeight, - StagedSplitBounds splitBoundsConfig, DeviceProfile dp, boolean isRtl); + SplitBounds splitBoundsConfig, DeviceProfile dp, boolean isRtl); // Overview TaskMenuView methods void setTaskIconParams(FrameLayout.LayoutParams iconParams, @@ -166,7 +176,7 @@ public interface PagedOrientationHandler { void setSplitIconParams(View primaryIconView, View secondaryIconView, int taskIconHeight, int primarySnapshotWidth, int primarySnapshotHeight, int groupedTaskViewHeight, int groupedTaskViewWidth, boolean isRtl, - DeviceProfile deviceProfile, StagedSplitBounds splitConfig); + DeviceProfile deviceProfile, SplitBounds splitConfig); /* * The following two methods try to center the TaskMenuView in landscape by finding the center @@ -174,9 +184,12 @@ public interface PagedOrientationHandler { * 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. @@ -190,16 +203,6 @@ public interface PagedOrientationHandler { */ 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 @@ -207,7 +210,7 @@ public interface PagedOrientationHandler { * @return A Pair of Floats representing the proper x and y translations. */ Pair<Float, Float> getDwbLayoutTranslations(int taskViewWidth, - int taskViewHeight, StagedSplitBounds splitBounds, DeviceProfile deviceProfile, + int taskViewHeight, SplitBounds splitBounds, DeviceProfile deviceProfile, View[] thumbnailViews, int desiredTaskId, View banner); // The following are only used by TaskViewTouchHandler. @@ -232,6 +235,41 @@ public interface PagedOrientationHandler { */ 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 508823c4d0..316cf0eab1 100644 --- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java +++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java @@ -27,6 +27,7 @@ import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; 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.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; @@ -47,12 +48,14 @@ import android.widget.FrameLayout; import android.widget.LinearLayout; 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.StagedSplitBounds; -import com.android.launcher3.views.BaseDragLayer; import java.util.List; @@ -261,26 +264,28 @@ public class PortraitPagedViewHandler implements PagedOrientationHandler { } @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 @@ -301,20 +306,8 @@ public class PortraitPagedViewHandler implements PagedOrientationHandler { } @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, StagedSplitBounds splitBounds, DeviceProfile deviceProfile, + int taskViewHeight, SplitBounds splitBounds, DeviceProfile deviceProfile, View[] thumbnailViews, int desiredTaskId, View banner) { float translationX = 0; float translationY = 0; @@ -421,13 +414,9 @@ public class PortraitPagedViewHandler implements PagedOrientationHandler { 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); @@ -467,32 +456,102 @@ public class PortraitPagedViewHandler implements PagedOrientationHandler { } @Override - public void updateStagedSplitIconParams(View out, float onScreenRectCenterX, + public void updateSplitIconParams(View out, float onScreenRectCenterX, float onScreenRectCenterY, float fullscreenScaleX, float fullscreenScaleY, 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(splitInstructionsHeight); + out.setRotation(getDegreesRotated()); + int distanceToEdge; + if ((DisplayController.getNavigationMode(out.getContext()) == THREE_BUTTONS) + && (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); + } else { + // If 3-button nav is not active, set bottom margin according to spec. + if (dp.isPhone) { + if (dp.isLandscape) { + distanceToEdge = out.getResources().getDimensionPixelSize( + R.dimen.split_instructions_bottom_margin_phone_landscape); + } else { + distanceToEdge = out.getResources().getDimensionPixelSize( + R.dimen.split_instructions_bottom_margin_phone_portrait); + } + } else if (dp.isTwoPanels) { + if (dp.isLandscape) { + distanceToEdge = out.getResources().getDimensionPixelSize( + R.dimen.split_instructions_bottom_margin_twopanels_landscape); + } else { + distanceToEdge = out.getResources().getDimensionPixelSize( + R.dimen.split_instructions_bottom_margin_twopanels_portrait); + } + } else { + if (dp.isLandscape) { + distanceToEdge = out.getResources().getDimensionPixelSize( + R.dimen.split_instructions_bottom_margin_tablet_landscape); + } else { + distanceToEdge = out.getResources().getDimensionPixelSize( + R.dimen.split_instructions_bottom_margin_tablet_portrait); + } + } + } + + // Center the view in case of unbalanced insets on left or right of screen + 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 - taskbarCorrectionY); + FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) out.getLayoutParams(); + lp.gravity = CENTER_HORIZONTAL | BOTTOM; + out.setLayoutParams(lp); + } + @Override public void getFinalSplitPlaceholderBounds(int splitDividerSize, DeviceProfile dp, @StagePosition int stagePosition, Rect out1, Rect out2) { @@ -525,8 +584,7 @@ public class PortraitPagedViewHandler implements PagedOrientationHandler { @Override public void setSplitTaskSwipeRect(DeviceProfile dp, Rect outRect, - StagedSplitBounds splitInfo, int desiredStagePosition) { - boolean isLandscape = dp.isLandscape; + SplitBounds splitInfo, int desiredStagePosition) { float topLeftTaskPercent = splitInfo.appsStackedVertically ? splitInfo.topTaskPercent : splitInfo.leftTaskPercent; @@ -534,30 +592,38 @@ public class PortraitPagedViewHandler implements PagedOrientationHandler { ? 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)); } } } @Override public void measureGroupedTaskViewThumbnailBounds(View primarySnapshot, View secondarySnapshot, - int parentWidth, int parentHeight, StagedSplitBounds splitBoundsConfig, + int parentWidth, int parentHeight, SplitBounds splitBoundsConfig, 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; @@ -566,7 +632,7 @@ public class PortraitPagedViewHandler implements PagedOrientationHandler { splitBoundsConfig.topTaskPercent : splitBoundsConfig.leftTaskPercent; if (dp.isLandscape) { primarySnapshotHeight = totalThumbnailHeight; - primarySnapshotWidth = (int) (parentWidth * taskPercent); + primarySnapshotWidth = Math.round(parentWidth * taskPercent); secondarySnapshotHeight = totalThumbnailHeight; secondarySnapshotWidth = parentWidth - primarySnapshotWidth - dividerBar; @@ -580,13 +646,29 @@ public class PortraitPagedViewHandler implements PagedOrientationHandler { } 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); } @@ -603,15 +685,16 @@ public class PortraitPagedViewHandler implements PagedOrientationHandler { 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 public void setSplitIconParams(View primaryIconView, View secondaryIconView, int taskIconHeight, int primarySnapshotWidth, int primarySnapshotHeight, int groupedTaskViewHeight, int groupedTaskViewWidth, boolean isRtl, - DeviceProfile deviceProfile, StagedSplitBounds splitConfig) { + DeviceProfile deviceProfile, SplitBounds splitConfig) { FrameLayout.LayoutParams primaryIconParams = (FrameLayout.LayoutParams) primaryIconView.getLayoutParams(); FrameLayout.LayoutParams secondaryIconParams = @@ -701,4 +784,36 @@ public class PortraitPagedViewHandler implements PagedOrientationHandler { 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 74b6a5b28e..05683bd11b 100644 --- a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java +++ b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java @@ -19,10 +19,12 @@ package com.android.launcher3.touch; import static android.view.Gravity.BOTTOM; import static android.view.Gravity.CENTER_VERTICAL; import static android.view.Gravity.END; +import static android.view.Gravity.RIGHT; import static android.view.Gravity.START; 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; @@ -32,13 +34,13 @@ import android.util.Pair; 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; import com.android.launcher3.Utilities; +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.StagedSplitBounds; import com.android.launcher3.views.BaseDragLayer; import java.util.Collections; @@ -83,33 +85,47 @@ public class SeascapePagedViewHandler extends LandscapePagedViewHandler { } @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; + 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 - public void setTaskMenuAroundTaskView(LinearLayout taskView, float margin) { - BaseDragLayer.LayoutParams lp = (BaseDragLayer.LayoutParams) taskView.getLayoutParams(); - lp.bottomMargin += margin; + public void setSplitTaskSwipeRect(DeviceProfile dp, Rect outRect, SplitBounds splitInfo, + int desiredStagePosition) { + float topLeftTaskPercent = splitInfo.appsStackedVertically + ? splitInfo.topTaskPercent + : splitInfo.leftTaskPercent; + float dividerBarPercent = splitInfo.appsStackedVertically + ? splitInfo.dividerHeightPercent + : splitInfo.dividerWidthPercent; + + // In seascape, the primary thumbnail is counterintuitively placed at the physical bottom of + // 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() * ((1 - topLeftTaskPercent))); + } else { + outRect.bottom -= (int) (outRect.height() * (topLeftTaskPercent + dividerBarPercent)); + } } @Override - public PointF getAdditionalInsetForTaskMenu(float margin) { - return new PointF(-margin, margin); - } - - - - @Override public Pair<Float, Float> getDwbLayoutTranslations(int taskViewWidth, - int taskViewHeight, StagedSplitBounds splitBounds, DeviceProfile deviceProfile, + int taskViewHeight, SplitBounds splitBounds, DeviceProfile deviceProfile, View[] thumbnailViews, int desiredTaskId, View banner) { boolean isRtl = banner.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; float translationX = 0; @@ -167,19 +183,42 @@ public class SeascapePagedViewHandler extends LandscapePagedViewHandler { } @Override + public void setSplitInstructionsParams(View out, DeviceProfile dp, int splitInstructionsHeight, + int splitInstructionsWidth, int threeButtonNavShift) { + out.setPivotX(0); + out.setPivotY(splitInstructionsHeight); + out.setRotation(getDegreesRotated()); + int distanceToEdge = out.getResources().getDimensionPixelSize( + R.dimen.split_instructions_bottom_margin_phone_landscape); + // Adjust for any insets on the right edge + 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 - 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 + // view to be screen-right when phone is in seascape, regardless of the RtL setting. + lp.gravity = RIGHT | CENTER_VERTICAL; + out.setLayoutParams(lp); + } + + @Override public void setTaskIconParams(FrameLayout.LayoutParams iconParams, int taskIconMargin, int taskIconHeight, int thumbnailTopMargin, boolean isRtl) { iconParams.gravity = (isRtl ? END : START) | CENTER_VERTICAL; iconParams.leftMargin = -taskIconHeight - taskIconMargin / 2; iconParams.rightMargin = 0; iconParams.topMargin = thumbnailTopMargin / 2; + iconParams.bottomMargin = 0; } @Override public void setSplitIconParams(View primaryIconView, View secondaryIconView, int taskIconHeight, int primarySnapshotWidth, int primarySnapshotHeight, int groupedTaskViewHeight, int groupedTaskViewWidth, boolean isRtl, - DeviceProfile deviceProfile, StagedSplitBounds splitConfig) { + DeviceProfile deviceProfile, SplitBounds splitConfig) { super.setSplitIconParams(primaryIconView, secondaryIconView, taskIconHeight, primarySnapshotWidth, primarySnapshotHeight, groupedTaskViewHeight, groupedTaskViewWidth, isRtl, deviceProfile, splitConfig); @@ -191,7 +230,8 @@ public class SeascapePagedViewHandler extends LandscapePagedViewHandler { // We calculate the "midpoint" of the thumbnail area, and place the icons there. // This is the place where the thumbnail area splits by default, in a near-50/50 split. // It is usually not exactly 50/50, due to insets/screen cutouts. - int fullscreenInsetThickness = deviceProfile.getInsets().top; + int fullscreenInsetThickness = deviceProfile.getInsets().top + - deviceProfile.getInsets().bottom; int fullscreenMidpointFromBottom = ((deviceProfile.heightPx - fullscreenInsetThickness) / 2); float midpointFromBottomPct = (float) fullscreenMidpointFromBottom / deviceProfile.heightPx; @@ -208,20 +248,63 @@ public class SeascapePagedViewHandler extends LandscapePagedViewHandler { if (splitConfig.initiatedFromSeascape) { // if the split was initiated from seascape, // the task on the right (secondary) is slightly larger - primaryIconView.setTranslationY(-bottomToMidpointOffset - insetOffset); - secondaryIconView.setTranslationY(-bottomToMidpointOffset - insetOffset + primaryIconView.setTranslationY(-bottomToMidpointOffset - insetOffset + taskIconHeight); + secondaryIconView.setTranslationY(-bottomToMidpointOffset - insetOffset); } else { // if not, // the task on the left (primary) is slightly larger - primaryIconView.setTranslationY(-bottomToMidpointOffset); - secondaryIconView.setTranslationY(-bottomToMidpointOffset + taskIconHeight); + primaryIconView.setTranslationY(-bottomToMidpointOffset + taskIconHeight); + secondaryIconView.setTranslationY(-bottomToMidpointOffset); } primaryIconView.setLayoutParams(primaryIconParams); 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 8c875010ae..7dd9b7ba16 100644 --- a/src/com/android/launcher3/touch/WorkspaceTouchListener.java +++ b/src/com/android/launcher3/touch/WorkspaceTouchListener.java @@ -15,7 +15,7 @@ */ package com.android.launcher3.touch; -import static android.provider.Settings.Secure.DOUBLE_TAP_SLEEP_GESTURE; +import static android.provider.Settings.System.DOUBLE_TAP_SLEEP_GESTURE; import static android.view.MotionEvent.ACTION_CANCEL; import static android.view.MotionEvent.ACTION_DOWN; @@ -49,7 +49,8 @@ import com.android.launcher3.Workspace; import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.logger.LauncherAtom; import com.android.launcher3.testing.TestLogging; -import com.android.launcher3.testing.TestProtocol; +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 @@ -117,6 +118,11 @@ public class WorkspaceTouchListener extends GestureDetector.SimpleOnGestureListe 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); @@ -197,6 +203,10 @@ public class WorkspaceTouchListener extends GestureDetector.SimpleOnGestureListe @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 1483c43a11..adc3c7d885 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 @@ public abstract class BgObjectWithLooper { */ @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 0000000000..758b3a962f --- /dev/null +++ b/src/com/android/launcher3/util/DimensionUtils.kt @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.launcher3.util + +import android.content.res.Resources +import android.graphics.Point +import android.view.ViewGroup +import com.android.launcher3.DeviceProfile +import com.android.launcher3.R + +object DimensionUtils { + /** + * Point where x is width, and y is height of taskbar based on provided [deviceProfile] + * x or y could also be -1 to indicate there is no dimension specified + */ + @JvmStatic + fun getTaskbarPhoneDimensions(deviceProfile: DeviceProfile, res: Resources, + isPhoneMode: Boolean): Point { + val p = Point() + // Taskbar for large screen + if (!isPhoneMode) { + p.x = ViewGroup.LayoutParams.MATCH_PARENT + p.y = deviceProfile.taskbarSize + return p + } + + // Taskbar on phone using gesture nav, it will always be stashed + if (deviceProfile.isGestureMode) { + p.x = ViewGroup.LayoutParams.MATCH_PARENT + p.y = res.getDimensionPixelSize(R.dimen.taskbar_stashed_size) + return p + } + + // Taskbar on phone, portrait + if (!deviceProfile.isLandscape) { + p.x = ViewGroup.LayoutParams.MATCH_PARENT + p.y = res.getDimensionPixelSize(R.dimen.taskbar_size) + return p + } + + // Taskbar on phone, landscape + p.x = res.getDimensionPixelSize(R.dimen.taskbar_size) + p.y = ViewGroup.LayoutParams.MATCH_PARENT + return p + } +}
\ No newline at end of file diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java index 7c73be51c6..c52890fd18 100644 --- a/src/com/android/launcher3/util/DisplayController.java +++ b/src/com/android/launcher3/util/DisplayController.java @@ -19,11 +19,9 @@ import static android.content.Intent.ACTION_CONFIGURATION_CHANGED; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; -import static com.android.launcher3.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; @@ -41,21 +39,21 @@ import android.os.Build; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; -import android.util.Pair; import android.view.Display; import androidx.annotation.AnyThread; import androidx.annotation.UiThread; +import androidx.annotation.VisibleForTesting; -import com.android.launcher3.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; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; +import java.util.Map; import java.util.Objects; import java.util.Set; @@ -66,6 +64,8 @@ import java.util.Set; public class DisplayController implements ComponentCallbacks, SafeCloseable { 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); @@ -80,7 +80,6 @@ public class DisplayController implements ComponentCallbacks, SafeCloseable { | 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; @@ -116,8 +115,9 @@ public class DisplayController implements ComponentCallbacks, SafeCloseable { getPackageFilter(TARGET_OVERLAY_PACKAGE, ACTION_OVERLAY_CHANGED)); WindowManagerProxy wmProxy = WindowManagerProxy.INSTANCE.get(context); - mInfo = new Info(getDisplayInfoContext(display), display, - wmProxy, wmProxy.estimateInternalDisplayBounds(context)); + Context displayInfoContext = getDisplayInfoContext(display); + mInfo = new Info(displayInfoContext, wmProxy, + wmProxy.estimateInternalDisplayBounds(displayInfoContext)); } /** @@ -127,6 +127,37 @@ public class DisplayController implements ComponentCallbacks, SafeCloseable { 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; @@ -215,18 +246,18 @@ public class DisplayController implements ComponentCallbacks, SafeCloseable { WindowManagerProxy wmProxy = WindowManagerProxy.INSTANCE.get(mContext); Info oldInfo = mInfo; - Context displayContext = getDisplayInfoContext(display); - Info newInfo = new Info(displayContext, display, wmProxy, oldInfo.mPerDisplayBounds); + Context displayInfoContext = getDisplayInfoContext(display); + Info newInfo = new Info(displayInfoContext, wmProxy, oldInfo.mPerDisplayBounds); if (newInfo.densityDpi != oldInfo.densityDpi || newInfo.fontScale != oldInfo.fontScale || newInfo.navigationMode != oldInfo.navigationMode) { // Cache may not be valid anymore, recreate without cache - newInfo = new Info(displayContext, display, wmProxy, - wmProxy.estimateInternalDisplayBounds(displayContext)); + newInfo = new Info(displayInfoContext, wmProxy, + wmProxy.estimateInternalDisplayBounds(displayInfoContext)); } int change = 0; - if (!newInfo.displayId.equals(oldInfo.displayId)) { + if (!newInfo.normalizedDisplayInfo.equals(oldInfo.normalizedDisplayInfo)) { change |= CHANGE_ACTIVE_SCREEN; } if (newInfo.rotation != oldInfo.rotation) { @@ -238,34 +269,18 @@ public class DisplayController implements ComponentCallbacks, SafeCloseable { if (newInfo.navigationMode != oldInfo.navigationMode) { change |= CHANGE_NAVIGATION_MODE; } - if (!newInfo.supportedBounds.equals(oldInfo.supportedBounds)) { + if (!newInfo.supportedBounds.equals(oldInfo.supportedBounds) + || !newInfo.mPerDisplayBounds.equals(oldInfo.mPerDisplayBounds)) { change |= CHANGE_SUPPORTED_BOUNDS; - - Point currentS = newInfo.currentSize; - Pair<CachedDisplayInfo, WindowBounds[]> cachedBounds = - oldInfo.mPerDisplayBounds.get(newInfo.displayId); - Point expectedS = cachedBounds == null ? null : cachedBounds.first.size; - if (newInfo.supportedBounds.size() != oldInfo.supportedBounds.size()) { - Log.e("b/198965093", - "Inconsistent number of displays" - + "\ndisplay state: " + display.getState() - + "\noldInfo.supportedBounds: " + oldInfo.supportedBounds - + "\nnewInfo.supportedBounds: " + newInfo.supportedBounds); - } - if (expectedS != null - && (Math.min(currentS.x, currentS.y) != Math.min(expectedS.x, expectedS.y) - || Math.max(currentS.x, currentS.y) != Math.max(expectedS.x, expectedS.y)) - && display.getState() == Display.STATE_OFF) { - Log.e("b/198965093", - "Display size changed while display is off, ignoring change"); - return; - } + } + if (DEBUG) { + Log.d(TAG, "handleInfoChange - change: 0b" + Integer.toBinaryString(change)); } if (change != 0) { mInfo = newInfo; final int flags = change; - MAIN_EXECUTOR.execute(() -> notifyChange(displayContext, flags)); + MAIN_EXECUTOR.execute(() -> notifyChange(displayInfoContext, flags)); } } @@ -283,8 +298,8 @@ public class DisplayController implements ComponentCallbacks, SafeCloseable { public static class Info { // Cached property + public final CachedDisplayInfo normalizedDisplayInfo; public final int rotation; - public final String displayId; public final Point currentSize; public final Rect cutout; @@ -292,56 +307,71 @@ public class DisplayController implements ComponentCallbacks, SafeCloseable { 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<String, Pair<CachedDisplayInfo, WindowBounds[]>> mPerDisplayBounds = + private final ArrayMap<CachedDisplayInfo, WindowBounds[]> mPerDisplayBounds = new ArrayMap<>(); - public Info(Context context, Display display) { + public Info(Context displayInfoContext) { /* don't need system overrides for external displays */ - this(context, display, new WindowManagerProxy(), new ArrayMap<>()); + this(displayInfoContext, new WindowManagerProxy(), new ArrayMap<>()); } // Used for testing - public Info(Context context, Display display, + public Info(Context displayInfoContext, WindowManagerProxy wmProxy, - ArrayMap<String, Pair<CachedDisplayInfo, WindowBounds[]>> perDisplayBoundsCache) { - CachedDisplayInfo displayInfo = wmProxy.getDisplayInfo(context, display); + Map<CachedDisplayInfo, WindowBounds[]> perDisplayBoundsCache) { + CachedDisplayInfo displayInfo = wmProxy.getDisplayInfo(displayInfoContext); + normalizedDisplayInfo = displayInfo.normalize(); rotation = displayInfo.rotation; currentSize = displayInfo.size; - displayId = displayInfo.id; cutout = displayInfo.cutout; - Configuration config = context.getResources().getConfiguration(); + Configuration config = displayInfoContext.getResources().getConfiguration(); fontScale = config.fontScale; densityDpi = config.densityDpi; mScreenSizeDp = new PortraitSize(config.screenHeightDp, config.screenWidthDp); - navigationMode = parseNavigationMode(context); + navigationMode = wmProxy.getNavigationMode(displayInfoContext); mPerDisplayBounds.putAll(perDisplayBoundsCache); - Pair<CachedDisplayInfo, WindowBounds[]> cachedValue = mPerDisplayBounds.get(displayId); + WindowBounds[] cachedValue = mPerDisplayBounds.get(normalizedDisplayInfo); - WindowBounds realBounds = wmProxy.getRealBounds(context, display, displayInfo); + realBounds = wmProxy.getRealBounds(displayInfoContext, displayInfo); if (cachedValue == null) { - supportedBounds.add(realBounds); - } else { + // Unexpected normalizedDisplayInfo is found, recreate the cache + Log.e(TAG, "Unexpected normalizedDisplayInfo found, invalidating cache"); + mPerDisplayBounds.clear(); + mPerDisplayBounds.putAll(wmProxy.estimateInternalDisplayBounds(displayInfoContext)); + cachedValue = mPerDisplayBounds.get(normalizedDisplayInfo); + if (cachedValue == null) { + Log.e(TAG, "normalizedDisplayInfo not found in estimation: " + + normalizedDisplayInfo); + supportedBounds.add(realBounds); + } + } + + if (cachedValue != null) { // Verify that the real bounds are a match - WindowBounds expectedBounds = cachedValue.second[displayInfo.rotation]; + WindowBounds expectedBounds = cachedValue[displayInfo.rotation]; if (!realBounds.equals(expectedBounds)) { WindowBounds[] clone = new WindowBounds[4]; - System.arraycopy(cachedValue.second, 0, clone, 0, 4); + System.arraycopy(cachedValue, 0, clone, 0, 4); clone[displayInfo.rotation] = realBounds; - cachedValue = Pair.create(displayInfo.normalize(), clone); - mPerDisplayBounds.put(displayId, cachedValue); + mPerDisplayBounds.put(normalizedDisplayInfo, clone); } } mPerDisplayBounds.values().forEach( - pair -> Collections.addAll(supportedBounds, pair.second)); - Log.d("b/211775278", "displayId: " + displayId + ", currentSize: " + currentSize); - Log.d("b/211775278", "perDisplayBounds: " + mPerDisplayBounds); + windowBounds -> Collections.addAll(supportedBounds, windowBounds)); + if (DEBUG) { + Log.d(TAG, "displayInfo: " + displayInfo); + Log.d(TAG, "realBounds: " + realBounds); + Log.d(TAG, "normalizedDisplayInfo: " + normalizedDisplayInfo); + mPerDisplayBounds.forEach((key, value) -> Log.d(TAG, + "perDisplayBounds - " + key + ": " + Arrays.deepToString(value))); + } } /** @@ -369,13 +399,14 @@ public class DisplayController implements ComponentCallbacks, SafeCloseable { public void dump(PrintWriter pw) { Info info = mInfo; pw.println("DisplayController.Info:"); - pw.println(" id=" + info.displayId); + pw.println(" normalizedDisplayInfo=" + info.normalizedDisplayInfo); pw.println(" rotation=" + info.rotation); pw.println(" fontScale=" + info.fontScale); pw.println(" densityDpi=" + info.densityDpi); pw.println(" navigationMode=" + info.navigationMode.name()); pw.println(" currentSize=" + info.currentSize); - pw.println(" supportedBounds=" + info.supportedBounds); + info.mPerDisplayBounds.forEach((key, value) -> pw.println( + " perDisplayBounds - " + key + ": " + Arrays.deepToString(value))); } /** @@ -403,35 +434,4 @@ public class DisplayController implements ComponentCallbacks, SafeCloseable { } } - 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/HorizontalInsettableView.java b/src/com/android/launcher3/util/HorizontalInsettableView.java index 7979bc0f25..486b73dd5f 100644 --- a/src/com/android/launcher3/util/HorizontalInsettableView.java +++ b/src/com/android/launcher3/util/HorizontalInsettableView.java @@ -15,6 +15,8 @@ */ package com.android.launcher3.util; +import android.util.FloatProperty; + /** * Allows the implementing view to add insets to the left and right. */ @@ -32,4 +34,22 @@ public interface HorizontalInsettableView { */ void setHorizontalInsets(float insetPercentage); + /** + * Returns the width percentage to inset the content from the left and from the right. See + * {@link #setHorizontalInsets}; + */ + float getHorizontalInsets(); + + FloatProperty<HorizontalInsettableView> HORIZONTAL_INSETS = + new FloatProperty<HorizontalInsettableView>("horizontalInsets") { + @Override + public Float get(HorizontalInsettableView view) { + return view.getHorizontalInsets(); + } + + @Override + public void setValue(HorizontalInsettableView view, float insetPercentage) { + view.setHorizontalInsets(insetPercentage); + } + }; } diff --git a/src/com/android/launcher3/util/LogConfig.java b/src/com/android/launcher3/util/LogConfig.java index 6bc26e7315..5abf95c000 100644 --- a/src/com/android/launcher3/util/LogConfig.java +++ b/src/com/android/launcher3/util/LogConfig.java @@ -40,4 +40,19 @@ public class LogConfig { * 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 50f7027407..0000000000 --- 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 0000000000..f34c4c2451 --- /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 11cd07c838..ac016a8595 100644 --- a/src/com/android/launcher3/util/MultiValueAlpha.java +++ b/src/com/android/launcher3/util/MultiValueAlpha.java @@ -16,61 +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.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 static final FloatProperty<AlphaProperty> VALUE = - new FloatProperty<AlphaProperty>("value") { - - @Override - public Float get(AlphaProperty alphaProperty) { - return alphaProperty.mValue; - } +public class MultiValueAlpha extends MultiPropertyFactory<View> { - @Override - public void setValue(AlphaProperty object, float value) { - object.setValue(value); - } - }; + private static final FloatBiFunction ALPHA_AGGREGATOR = (a, b) -> a * b; - 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. */ @@ -78,74 +41,11 @@ public class MultiValueAlpha { mUpdateVisibility = updateVisibility; } - 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 0000000000..37dd41c274 --- /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 f4cf21efe5..f4893c7602 100644 --- a/src/com/android/launcher3/util/OnboardingPrefs.java +++ b/src/com/android/launcher3/util/OnboardingPrefs.java @@ -41,15 +41,16 @@ public class OnboardingPrefs<T extends ActivityContext> { 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"; // When adding a new key, add it here as well, to be able to reset it from Developer Options. public static final Map<String, String[]> ALL_PREF_KEYS = Map.of( "All Apps Bounce", new String[] { HOME_BOUNCE_SEEN, HOME_BOUNCE_COUNT }, "Hybrid Hotseat Education", new String[] { HOTSEAT_DISCOVERY_TIP_COUNT, HOTSEAT_LONGPRESS_TIP_SEEN }, "Search Education", new String[] { SEARCH_KEYBOARD_EDU_SEEN, SEARCH_SNACKBAR_COUNT, - SEARCH_ONBOARDING_COUNT}, + SEARCH_ONBOARDING_COUNT, QSB_SEARCH_ONBOARDING_CARD_DISMISSED}, "Taskbar Education", new String[] { TASKBAR_EDU_SEEN }, "All Apps Visited Count", new String[] {ALL_APPS_VISITED_COUNT} ); @@ -61,7 +62,8 @@ public class OnboardingPrefs<T extends ActivityContext> { HOME_BOUNCE_SEEN, HOTSEAT_LONGPRESS_TIP_SEEN, SEARCH_KEYBOARD_EDU_SEEN, - TASKBAR_EDU_SEEN + TASKBAR_EDU_SEEN, + QSB_SEARCH_ONBOARDING_CARD_DISMISSED }) @Retention(RetentionPolicy.SOURCE) public @interface EventBoolKey {} diff --git a/src/com/android/launcher3/util/PackageManagerHelper.java b/src/com/android/launcher3/util/PackageManagerHelper.java index f42d30453b..140440eee2 100644 --- a/src/com/android/launcher3/util/PackageManagerHelper.java +++ b/src/com/android/launcher3/util/PackageManagerHelper.java @@ -16,8 +16,6 @@ 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; @@ -31,7 +29,6 @@ import android.content.pm.PackageInfo; 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; @@ -40,9 +37,11 @@ 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 +53,7 @@ import com.android.launcher3.model.data.WorkspaceItemInfo; import java.net.URISyntaxException; import java.util.List; +import java.util.Objects; /** * Utility methods using package manager @@ -62,22 +62,28 @@ public class PackageManagerHelper { 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 +92,27 @@ public class PackageManagerHelper { * 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 +126,8 @@ public class PackageManagerHelper { return mPm.isSafeMode(); } - public Intent getAppLaunchIntent(String pkg, UserHandle user) { + @Nullable + public Intent getAppLaunchIntent(@Nullable final String pkg, @NonNull final UserHandle user) { List<LauncherActivityInfo> activities = mLauncherApps.getActivityList(pkg, user); return activities.isEmpty() ? null : AppInfo.makeLaunchIntent(activities.get(0)); @@ -251,7 +262,8 @@ public class PackageManagerHelper { 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 +292,6 @@ public class PackageManagerHelper { } /** - * 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 0000000000..220ab566aa --- /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 new file mode 100644 index 0000000000..58c3be55f9 --- /dev/null +++ b/src/com/android/launcher3/util/PendingSplitSelectInfo.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.util; + +import com.android.launcher3.logging.StatsLogManager; +import com.android.launcher3.util.SplitConfigurationOptions.StagePosition; + +/** + * Utility class to store information regarding a split select request. This includes the taskId of + * the originating task, plus the stage position. + * This information is intended to be saved across launcher instances, e.g. when Launcher needs to + * recover straight into a split select state. + */ +public class PendingSplitSelectInfo { + + private final int mStagedTaskId; + private final int mStagePosition; + private final StatsLogManager.EventEnum mSource; + + public PendingSplitSelectInfo(int stagedTaskId, int stagePosition, + StatsLogManager.EventEnum source) { + this.mStagedTaskId = stagedTaskId; + this.mStagePosition = stagePosition; + this.mSource = source; + } + + public int getStagedTaskId() { + return mStagedTaskId; + } + + public @StagePosition int getStagePosition() { + return mStagePosition; + } + + public StatsLogManager.EventEnum getSource() { + return mSource; + } +} diff --git a/src/com/android/launcher3/util/ScrollableLayoutManager.java b/src/com/android/launcher3/util/ScrollableLayoutManager.java new file mode 100644 index 0000000000..9bc4ddce38 --- /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 4dfa5ccdeb..0a23506692 100644 --- a/src/com/android/launcher3/util/SimpleBroadcastReceiver.java +++ b/src/com/android/launcher3/util/SimpleBroadcastReceiver.java @@ -52,4 +52,15 @@ public class SimpleBroadcastReceiver extends BroadcastReceiver { } context.registerReceiver(this, filter, flags); } + + /** + * Unregisters the receiver ignoring any errors + */ + public void unregisterReceiverSafely(Context context) { + try { + context.unregisterReceiver(this); + } catch (IllegalArgumentException e) { + // It was probably never registered or already unregistered. Ignore. + } + } } diff --git a/src/com/android/launcher3/util/SplitConfigurationOptions.java b/src/com/android/launcher3/util/SplitConfigurationOptions.java index 6a336cce28..19a39483e1 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,8 +100,9 @@ public final class SplitConfigurationOptions { * 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 StagedSplitBounds { + public static class SplitBounds { public final Rect leftTopBounds; public final Rect rightBottomBounds; /** This rect represents the actual gap between the two apps */ @@ -124,7 +130,7 @@ public final class SplitConfigurationOptions { public final int leftTopTaskId; public final int rightBottomTaskId; - public StagedSplitBounds(Rect leftTopBounds, Rect rightBottomBounds, int leftTopTaskId, + public SplitBounds(Rect leftTopBounds, Rect rightBottomBounds, int leftTopTaskId, int rightBottomTaskId) { this.leftTopBounds = leftTopBounds; this.rightBottomBounds = rightBottomBounds; @@ -163,11 +169,25 @@ public final class SplitConfigurationOptions { } } - public static class StagedSplitTaskPosition { + public static class SplitStageInfo { public int taskId = -1; @StagePosition public int stagePosition = STAGE_POSITION_UNDEFINED; @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 630df7e46b..6945983920 100644 --- a/src/com/android/launcher3/util/SystemUiController.java +++ b/src/com/android/launcher3/util/SystemUiController.java @@ -19,6 +19,10 @@ package com.android.launcher3.util; import android.view.View; import android.view.Window; +import androidx.annotation.IntDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.Arrays; /** @@ -31,15 +35,26 @@ public class SystemUiController { 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_ALLAPPS = 4; public static final int FLAG_LIGHT_NAV = 1 << 0; public static final int FLAG_DARK_NAV = 1 << 1; public static final int FLAG_LIGHT_STATUS = 1 << 2; public static final int FLAG_DARK_STATUS = 1 << 3; + /** + * Security type based on WifiConfiguration.KeyMgmt + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, value = { + FLAG_LIGHT_NAV, + FLAG_DARK_NAV, + FLAG_LIGHT_STATUS, + FLAG_DARK_STATUS, + }) + public @interface SystemUiControllerFlags {} + private final Window mWindow; - private final int[] mStates = new int[5]; + private final int[] mStates = new int[4]; public SystemUiController(Window window) { mWindow = window; @@ -50,7 +65,7 @@ public class SystemUiController { ? (FLAG_LIGHT_NAV | FLAG_LIGHT_STATUS) : (FLAG_DARK_NAV | FLAG_DARK_STATUS)); } - public void updateUiState(int uiState, int flags) { + public void updateUiState(int uiState, @SystemUiControllerFlags int flags) { if (mStates[uiState] == flags) { return; } diff --git a/src/com/android/launcher3/util/Themes.java b/src/com/android/launcher3/util/Themes.java index 53a584d8de..585bea93ad 100644 --- a/src/com/android/launcher3/util/Themes.java +++ b/src/com/android/launcher3/util/Themes.java @@ -30,9 +30,9 @@ import android.util.AttributeSet; 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 +74,7 @@ public class Themes { * 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.getPrefs(context).getBoolean(KEY_THEMED_ICONS, false); } public static String getDefaultBodyFont(Context context) { diff --git a/src/com/android/launcher3/util/TouchController.java b/src/com/android/launcher3/util/TouchController.java index 9c397c0f74..fc1d819f78 100644 --- a/src/com/android/launcher3/util/TouchController.java +++ b/src/com/android/launcher3/util/TouchController.java @@ -32,10 +32,5 @@ public interface TouchController { */ 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 0000000000..b18a2ef485 --- /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 7e6711f806..0000000000 --- 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 index 211bd08b33..932bcfc292 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 class VibratorWrapper { 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/WindowBounds.java b/src/com/android/launcher3/util/WindowBounds.java index a15679a822..91480e17c4 100644 --- a/src/com/android/launcher3/util/WindowBounds.java +++ b/src/com/android/launcher3/util/WindowBounds.java @@ -76,6 +76,7 @@ public class WindowBounds { + "bounds=" + bounds + ", insets=" + insets + ", availableSize=" + availableSize + + ", rotationHint=" + rotationHint + '}'; } diff --git a/src/com/android/launcher3/util/window/CachedDisplayInfo.java b/src/com/android/launcher3/util/window/CachedDisplayInfo.java index 06b9829270..23f37aa2b2 100644 --- a/src/com/android/launcher3/util/window/CachedDisplayInfo.java +++ b/src/com/android/launcher3/util/window/CachedDisplayInfo.java @@ -30,7 +30,6 @@ import java.util.Objects; */ public class CachedDisplayInfo { - public final String id; public final Point size; public final int rotation; public final Rect cutout; @@ -40,11 +39,10 @@ public class CachedDisplayInfo { } public CachedDisplayInfo(Point size, int rotation) { - this("", size, rotation, new Rect()); + this(size, rotation, new Rect()); } - public CachedDisplayInfo(String id, Point size, int rotation, Rect cutout) { - this.id = id; + public CachedDisplayInfo(Point size, int rotation, Rect cutout) { this.size = size; this.rotation = rotation; this.cutout = cutout; @@ -62,16 +60,15 @@ public class CachedDisplayInfo { Rect newCutout = new Rect(cutout); rotateRect(newCutout, deltaRotation(rotation, Surface.ROTATION_0)); - return new CachedDisplayInfo(id, newSize, Surface.ROTATION_0, newCutout); + return new CachedDisplayInfo(newSize, Surface.ROTATION_0, newCutout); } @Override public String toString() { return "CachedDisplayInfo{" - + "id='" + id + '\'' - + ", size=" + size - + ", rotation=" + rotation + + "size=" + size + ", cutout=" + cutout + + ", rotation=" + rotation + '}'; } @@ -80,13 +77,13 @@ public class CachedDisplayInfo { if (this == o) return true; if (!(o instanceof CachedDisplayInfo)) return false; CachedDisplayInfo that = (CachedDisplayInfo) o; - return rotation == that.rotation && Objects.equals(id, that.id) - && Objects.equals(size, that.size) && Objects.equals(cutout, - that.cutout); + return rotation == that.rotation + && Objects.equals(size, that.size) + && Objects.equals(cutout, that.cutout); } @Override public int hashCode() { - return Objects.hash(id, size, rotation, cutout); + return Objects.hash(size, rotation, cutout); } } diff --git a/src/com/android/launcher3/util/window/WindowManagerProxy.java b/src/com/android/launcher3/util/window/WindowManagerProxy.java index 9665bf91c5..fb2ae732b7 100644 --- a/src/com/android/launcher3/util/window/WindowManagerProxy.java +++ b/src/com/android/launcher3/util/window/WindowManagerProxy.java @@ -16,16 +16,16 @@ package com.android.launcher3.util.window; import static android.view.Display.DEFAULT_DISPLAY; -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; - -import static com.android.launcher3.ResourceUtils.INVALID_RESOURCE_HANDLE; -import static com.android.launcher3.ResourceUtils.NAVBAR_HEIGHT; -import static com.android.launcher3.ResourceUtils.NAVBAR_HEIGHT_LANDSCAPE; -import static com.android.launcher3.ResourceUtils.NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE; -import static com.android.launcher3.ResourceUtils.STATUS_BAR_HEIGHT; -import static com.android.launcher3.ResourceUtils.STATUS_BAR_HEIGHT_LANDSCAPE; -import static com.android.launcher3.ResourceUtils.STATUS_BAR_HEIGHT_PORTRAIT; + 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.util.MainThreadInitializedObject.forOverride; import static com.android.launcher3.util.RotationUtils.deltaRotation; import static com.android.launcher3.util.RotationUtils.rotateRect; @@ -41,7 +41,7 @@ import android.graphics.Rect; import android.hardware.display.DisplayManager; import android.os.Build; import android.util.ArrayMap; -import android.util.Pair; +import android.util.Log; import android.view.Display; import android.view.DisplayCutout; import android.view.Surface; @@ -50,9 +50,10 @@ import android.view.WindowManager; import android.view.WindowMetrics; import com.android.launcher3.R; -import com.android.launcher3.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; @@ -61,6 +62,7 @@ import com.android.launcher3.util.WindowBounds; */ 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 = @@ -88,20 +90,12 @@ public class WindowManagerProxy implements ResourceBasedOverride { * Returns a map of normalized info of internal displays to estimated window bounds * for that display */ - public ArrayMap<String, Pair<CachedDisplayInfo, WindowBounds[]>> estimateInternalDisplayBounds( - Context context) { - Display[] displays = getDisplays(context); - ArrayMap<String, Pair<CachedDisplayInfo, WindowBounds[]>> result = new ArrayMap<>(); - for (Display display : displays) { - if (isInternalDisplay(display)) { - Context displayContext = Utilities.ATLEAST_S - ? context.createWindowContext(display, TYPE_APPLICATION, null) - : context.createDisplayContext(display); - CachedDisplayInfo info = getDisplayInfo(displayContext, display).normalize(); - WindowBounds[] bounds = estimateWindowBounds(context, info); - result.put(info.id, Pair.create(info, bounds)); - } - } + public ArrayMap<CachedDisplayInfo, WindowBounds[]> estimateInternalDisplayBounds( + Context displayInfoContext) { + CachedDisplayInfo info = getDisplayInfo(displayInfoContext).normalize(); + WindowBounds[] bounds = estimateWindowBounds(displayInfoContext, info); + ArrayMap<CachedDisplayInfo, WindowBounds[]> result = new ArrayMap<>(); + result.put(info, bounds); return result; } @@ -109,12 +103,11 @@ public class WindowManagerProxy implements ResourceBasedOverride { * Returns the real bounds for the provided display after applying any insets normalization */ @TargetApi(Build.VERSION_CODES.R) - public WindowBounds getRealBounds(Context windowContext, - Display display, CachedDisplayInfo info) { + public WindowBounds getRealBounds(Context displayInfoContext, CachedDisplayInfo info) { if (!Utilities.ATLEAST_R) { Point smallestSize = new Point(); Point largestSize = new Point(); - display.getCurrentSizeRange(smallestSize, largestSize); + getDisplay(displayInfoContext).getCurrentSizeRange(smallestSize, largestSize); if (info.size.y > info.size.x) { // Portrait @@ -122,17 +115,16 @@ public class WindowManagerProxy implements ResourceBasedOverride { info.rotation); } else { // Landscape - new WindowBounds(info.size.x, info.size.y, largestSize.x, smallestSize.y, + return new WindowBounds(info.size.x, info.size.y, largestSize.x, smallestSize.y, info.rotation); } } - WindowMetrics wm = windowContext.getSystemService(WindowManager.class) + WindowMetrics windowMetrics = displayInfoContext.getSystemService(WindowManager.class) .getMaximumWindowMetrics(); - Rect insets = new Rect(); - normalizeWindowInsets(windowContext, wm.getWindowInsets(), insets); - return new WindowBounds(wm.getBounds(), insets, info.rotation); + normalizeWindowInsets(displayInfoContext, windowMetrics.getWindowInsets(), insets); + return new WindowBounds(windowMetrics.getBounds(), insets, info.rotation); } /** @@ -169,12 +161,9 @@ public class WindowManagerProxy implements ResourceBasedOverride { 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), @@ -202,21 +191,14 @@ public class WindowManagerProxy implements ResourceBasedOverride { } /** - * Returns true if the display is an internal displays - */ - protected boolean isInternalDisplay(Display display) { - return display.getDisplayId() == Display.DEFAULT_DISPLAY; - } - - /** * Returns a list of possible WindowBounds for the display keyed on the 4 surface rotations */ - public WindowBounds[] estimateWindowBounds(Context context, CachedDisplayInfo display) { + protected WindowBounds[] estimateWindowBounds(Context context, CachedDisplayInfo displayInfo) { int densityDpi = context.getResources().getConfiguration().densityDpi; - int rotation = display.rotation; - Rect safeCutout = display.cutout; + int rotation = displayInfo.rotation; + Rect safeCutout = displayInfo.cutout; - int minSize = Math.min(display.size.x, display.size.y); + int minSize = Math.min(displayInfo.size.x, displayInfo.size.y); int swDp = (int) dpiFromPx(minSize, densityDpi); Resources systemRes; @@ -255,7 +237,7 @@ public class WindowManagerProxy implements ResourceBasedOverride { Point tempSize = new Point(); for (int i = 0; i < 4; i++) { int rotationChange = deltaRotation(rotation, i); - tempSize.set(display.size.x, display.size.y); + tempSize.set(displayInfo.size.x, displayInfo.size.y); rotateSize(tempSize, rotationChange); Rect bounds = new Rect(0, 0, tempSize.x, tempSize.y); @@ -311,55 +293,78 @@ public class WindowManagerProxy implements ResourceBasedOverride { * Returns a CachedDisplayInfo initialized for the current display */ @TargetApi(Build.VERSION_CODES.S) - public CachedDisplayInfo getDisplayInfo(Context displayContext, Display display) { - int rotation = getRotation(displayContext); - Rect cutoutRect = new Rect(); - Point size = new Point(); + public CachedDisplayInfo getDisplayInfo(Context displayInfoContext) { + int rotation = getRotation(displayInfoContext); if (Utilities.ATLEAST_S) { - WindowMetrics wm = displayContext.getSystemService(WindowManager.class) + WindowMetrics windowMetrics = displayInfoContext.getSystemService(WindowManager.class) .getMaximumWindowMetrics(); - DisplayCutout cutout = wm.getWindowInsets().getDisplayCutout(); - if (cutout != null) { - cutoutRect.set(cutout.getSafeInsetLeft(), cutout.getSafeInsetTop(), - cutout.getSafeInsetRight(), cutout.getSafeInsetBottom()); - } - - size.set(wm.getBounds().right, wm.getBounds().bottom); + return getDisplayInfo(windowMetrics, rotation); } else { + Point size = new Point(); + Display display = getDisplay(displayInfoContext); display.getRealSize(size); + Rect cutoutRect = new Rect(); + return new CachedDisplayInfo(size, rotation, cutoutRect); } - return new CachedDisplayInfo(getDisplayId(display), size, rotation, cutoutRect); } /** - * Returns a unique ID representing the display + * Returns a CachedDisplayInfo initialized for the current display */ - protected String getDisplayId(Display display) { - return Integer.toString(display.getDisplayId()); + @TargetApi(Build.VERSION_CODES.S) + protected CachedDisplayInfo getDisplayInfo(WindowMetrics windowMetrics, int rotation) { + Point size = new Point(windowMetrics.getBounds().right, windowMetrics.getBounds().bottom); + Rect cutoutRect = new Rect(); + DisplayCutout cutout = windowMetrics.getWindowInsets().getDisplayCutout(); + if (cutout != null) { + cutoutRect.set(cutout.getSafeInsetLeft(), cutout.getSafeInsetTop(), + cutout.getSafeInsetRight(), cutout.getSafeInsetBottom()); + } + return new CachedDisplayInfo(size, rotation, cutoutRect); } /** - * Returns rotation of the display associated with the context. + * Returns rotation of the display associated with the context, or rotation of DEFAULT_DISPLAY + * if the context isn't associated with a display. */ - public int getRotation(Context context) { - Display d = null; + public int getRotation(Context displayInfoContext) { + return getDisplay(displayInfoContext).getRotation(); + } + + /** + * + * Returns the display associated with the context, or DEFAULT_DISPLAY if the context isn't + * associated with a display. + */ + protected Display getDisplay(Context displayInfoContext) { if (Utilities.ATLEAST_R) { try { - d = context.getDisplay(); + return displayInfoContext.getDisplay(); } catch (UnsupportedOperationException e) { // Ignore } } - if (d == null) { - d = context.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY); - } - return d.getRotation(); + return displayInfoContext.getSystemService(DisplayManager.class).getDisplay( + DEFAULT_DISPLAY); } /** - * Returns all currently valid logical displays. + * Returns the current navigation mode from resource. */ - protected Display[] getDisplays(Context context) { - return context.getSystemService(DisplayManager.class).getDisplays(); + 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 47503b118e..f73347a7b2 100644 --- a/src/com/android/launcher3/views/AbstractSlideInView.java +++ b/src/com/android/launcher3/views/AbstractSlideInView.java @@ -33,6 +33,8 @@ import android.view.View; 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 com.android.launcher3.touch.SingleAxisSwipeDetector; 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 @@ public abstract class AbstractSlideInView<T extends Context & ActivityContext> 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 @@ public abstract class AbstractSlideInView<T extends Context & ActivityContext> } } + /** 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 @@ public abstract class AbstractSlideInView<T extends Context & ActivityContext> 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 93078e4cf6..79b4cb4da4 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 @@ public interface ActivityContext { 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(); } @@ -141,6 +201,7 @@ public interface ActivityContext { default void applyOverwritesToLogItem(LauncherAtom.ItemInfo.Builder itemInfoBuilder) { } /** Onboarding preferences for any onboarding data within this context. */ + @Nullable default OnboardingPrefs<?> getOnboardingPrefs() { return null; } @@ -150,6 +211,222 @@ public interface ActivityContext { 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. @@ -175,30 +452,4 @@ public interface ActivityContext { 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 b1e69c7de4..0000000000 --- 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 19e66abd78..0000000000 --- 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 8d16a8d982..73c5ad457b 100644 --- a/src/com/android/launcher3/views/ArrowTipView.java +++ b/src/com/android/launcher3/views/ArrowTipView.java @@ -162,6 +162,7 @@ public class ArrowTipView extends AbstractFloatingView { 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 f553fb4e8e..8ff68880a6 100644 --- a/src/com/android/launcher3/views/BaseDragLayer.java +++ b/src/com/android/launcher3/views/BaseDragLayer.java @@ -22,13 +22,10 @@ import static android.view.MotionEvent.ACTION_UP; import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs; -import android.annotation.TargetApi; -import android.app.WallpaperManager; import android.content.Context; import android.graphics.Insets; import android.graphics.Rect; import android.graphics.RectF; -import android.os.Build; import android.util.AttributeSet; import android.util.Property; import android.view.MotionEvent; @@ -43,8 +40,8 @@ import com.android.launcher3.AbstractFloatingView; 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; @@ -110,7 +107,6 @@ public abstract class BaseDragLayer<T extends Context & ActivityContext> protected final T mActivity; private final MultiValueAlpha mMultiValueAlpha; - private final WallpaperManager mWallpaperManager; // All the touch controllers for the view protected TouchController[] mControllers; @@ -123,9 +119,8 @@ public abstract class BaseDragLayer<T extends Context & ActivityContext> 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); } /** @@ -504,8 +499,8 @@ public abstract class BaseDragLayer<T extends Context & ActivityContext> 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) { @@ -550,18 +545,24 @@ public abstract class BaseDragLayer<T extends Context & ActivityContext> } @Override - @TargetApi(Build.VERSION_CODES.Q) public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) { if (Utilities.ATLEAST_Q) { Insets gestureInsets = insets.getMandatorySystemGestureInsets(); int gestureInsetBottom = gestureInsets.bottom; + Insets imeInset = Utilities.ATLEAST_R + ? insets.getInsets(WindowInsets.Type.ime()) + : Insets.NONE; DeviceProfile dp = mActivity.getDeviceProfile(); if (dp.isTaskbarPresent) { // Ignore taskbar gesture insets to avoid interfering with TouchControllers. gestureInsetBottom = Math.max(0, gestureInsetBottom - dp.taskbarSize); } - mSystemGestureRegion.set(gestureInsets.left, gestureInsets.top, - gestureInsets.right, gestureInsetBottom); + mSystemGestureRegion.set( + Math.max(gestureInsets.left, imeInset.left), + Math.max(gestureInsets.top, imeInset.top), + Math.max(gestureInsets.right, imeInset.right), + Math.max(gestureInsetBottom, imeInset.bottom) + ); } return super.dispatchApplyWindowInsets(insets); } diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java index efc83ebcc9..55af6221d8 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 @@ public class FloatingIconView extends FrameLayout implements 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 0000000000..dd48c99c7d --- /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 2a9a8a5d36..5b57e22d81 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; @@ -50,7 +47,8 @@ import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.popup.ArrowPopup; import com.android.launcher3.shortcuts.DeepShortcutView; import com.android.launcher3.testing.TestLogging; -import com.android.launcher3.testing.TestProtocol; +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 @@ import java.util.List; 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 @@ public class OptionsPopupView extends ArrowPopup<Launcher> 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 @@ public class OptionsPopupView extends ArrowPopup<Launcher> 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 @@ public class OptionsPopupView extends ArrowPopup<Launcher> .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 @@ public class OptionsPopupView extends ArrowPopup<Launcher> 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 11ca130179..3af2e3c9dc 100644 --- a/src/com/android/launcher3/views/RecyclerViewFastScroller.java +++ b/src/com/android/launcher3/views/RecyclerViewFastScroller.java @@ -20,8 +20,6 @@ import static android.view.HapticFeedbackConstants.CLOCK_TICK; 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 @@ public class RecyclerViewFastScroller extends View { // 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 @@ public class RecyclerViewFastScroller extends View { 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 class RecyclerViewFastScroller extends View { 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 @@ public class RecyclerViewFastScroller extends View { } 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/Snackbar.java b/src/com/android/launcher3/views/Snackbar.java index e582114fc9..86b341953f 100644 --- a/src/com/android/launcher3/views/Snackbar.java +++ b/src/com/android/launcher3/views/Snackbar.java @@ -31,6 +31,7 @@ import android.widget.TextView; 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 @@ public class Snackbar extends AbstractFloatingView { 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/views/StickyHeaderLayout.java b/src/com/android/launcher3/views/StickyHeaderLayout.java new file mode 100644 index 0000000000..d6481a9b57 --- /dev/null +++ b/src/com/android/launcher3/views/StickyHeaderLayout.java @@ -0,0 +1,327 @@ +/* + * 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.views; + +import static android.view.View.MeasureSpec.EXACTLY; +import static android.view.View.MeasureSpec.makeMeasureSpec; + +import static com.android.launcher3.anim.AnimatorListeners.forEndCallback; + +import android.animation.Animator; +import android.animation.ObjectAnimator; +import android.content.Context; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.util.FloatProperty; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import com.android.launcher3.R; + +/** + * A {@link LinearLayout} container which allows scrolling parts of its content based on the + * scroll of a different view. Views which are marked as sticky are not scrolled, giving the + * illusion of a sticky header. + */ +public class StickyHeaderLayout extends LinearLayout implements + RecyclerView.OnChildAttachStateChangeListener { + + private static final FloatProperty<StickyHeaderLayout> SCROLL_OFFSET = + new FloatProperty<StickyHeaderLayout>("scrollAnimOffset") { + @Override + public void setValue(StickyHeaderLayout view, float offset) { + view.mScrollOffset = offset; + view.updateHeaderScroll(); + } + + @Override + public Float get(StickyHeaderLayout view) { + return view.mScrollOffset; + } + }; + + private static final MotionEventProxyMethod INTERCEPT_PROXY = ViewGroup::onInterceptTouchEvent; + private static final MotionEventProxyMethod TOUCH_PROXY = ViewGroup::onTouchEvent; + + private RecyclerView mCurrentRecyclerView; + private EmptySpaceView mCurrentEmptySpaceView; + + private float mLastScroll = 0; + private float mScrollOffset = 0; + private Animator mOffsetAnimator; + + private boolean mShouldForwardToRecyclerView = false; + private int mHeaderHeight; + + public StickyHeaderLayout(Context context) { + this(context, /* attrs= */ null); + } + + public StickyHeaderLayout(Context context, AttributeSet attrs) { + this(context, attrs, /* defStyleAttr= */ 0); + } + + public StickyHeaderLayout(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, /* defStyleRes= */ 0); + } + + public StickyHeaderLayout(Context context, AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + /** + * Sets the recycler view, this sticky header should track + */ + public void setCurrentRecyclerView(RecyclerView currentRecyclerView) { + boolean animateReset = mCurrentRecyclerView != null; + if (mCurrentRecyclerView != null) { + mCurrentRecyclerView.removeOnChildAttachStateChangeListener(this); + } + mCurrentRecyclerView = currentRecyclerView; + mCurrentRecyclerView.addOnChildAttachStateChangeListener(this); + findCurrentEmptyView(); + reset(animateReset); + } + + public int getHeaderHeight() { + return mHeaderHeight; + } + + private void updateHeaderScroll() { + mLastScroll = getCurrentScroll(); + int count = getChildCount(); + for (int i = 0; i < count; i++) { + View child = getChildAt(i); + MyLayoutParams lp = (MyLayoutParams) child.getLayoutParams(); + child.setTranslationY(Math.max(mLastScroll, lp.scrollLimit)); + } + } + + private float getCurrentScroll() { + return mScrollOffset + (mCurrentEmptySpaceView == null ? 0 : mCurrentEmptySpaceView.getY()); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + + mHeaderHeight = getMeasuredHeight(); + if (mCurrentEmptySpaceView != null) { + mCurrentEmptySpaceView.setFixedHeight(mHeaderHeight); + } + } + + /** Resets any previous view translation. */ + public void reset(boolean animate) { + if (mOffsetAnimator != null) { + mOffsetAnimator.cancel(); + mOffsetAnimator = null; + } + + mScrollOffset = 0; + if (!animate) { + updateHeaderScroll(); + } else { + float startValue = mLastScroll - getCurrentScroll(); + mOffsetAnimator = ObjectAnimator.ofFloat(this, SCROLL_OFFSET, startValue, 0); + mOffsetAnimator.addListener(forEndCallback(() -> mOffsetAnimator = null)); + mOffsetAnimator.start(); + } + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent event) { + return (mShouldForwardToRecyclerView = proxyMotionEvent(event, INTERCEPT_PROXY)) + || super.onInterceptTouchEvent(event); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + return mShouldForwardToRecyclerView && proxyMotionEvent(event, TOUCH_PROXY) + || super.onTouchEvent(event); + } + + private boolean proxyMotionEvent(MotionEvent event, MotionEventProxyMethod method) { + float dx = mCurrentRecyclerView.getLeft() - getLeft(); + float dy = mCurrentRecyclerView.getTop() - getTop(); + event.offsetLocation(dx, dy); + try { + return method.proxyEvent(mCurrentRecyclerView, event); + } finally { + event.offsetLocation(-dx, -dy); + } + } + + @Override + public void onChildViewAttachedToWindow(@NonNull View view) { + if (view instanceof EmptySpaceView) { + findCurrentEmptyView(); + } + } + + @Override + public void onChildViewDetachedFromWindow(@NonNull View view) { + if (view == mCurrentEmptySpaceView) { + findCurrentEmptyView(); + } + } + + private void findCurrentEmptyView() { + if (mCurrentEmptySpaceView != null) { + mCurrentEmptySpaceView.setOnYChangeCallback(null); + mCurrentEmptySpaceView = null; + } + int childCount = mCurrentRecyclerView.getChildCount(); + for (int i = 0; i < childCount; i++) { + View view = mCurrentRecyclerView.getChildAt(i); + if (view instanceof EmptySpaceView) { + mCurrentEmptySpaceView = (EmptySpaceView) view; + mCurrentEmptySpaceView.setFixedHeight(getHeaderHeight()); + mCurrentEmptySpaceView.setOnYChangeCallback(this::updateHeaderScroll); + return; + } + } + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + super.onLayout(changed, l, t, r, b); + + // Update various stick parameters + int count = getChildCount(); + int stickyHeaderHeight = 0; + for (int i = 0; i < count; i++) { + View v = getChildAt(i); + MyLayoutParams lp = (MyLayoutParams) v.getLayoutParams(); + if (lp.sticky) { + lp.scrollLimit = -v.getTop() + stickyHeaderHeight; + stickyHeaderHeight += v.getHeight(); + } else { + lp.scrollLimit = Integer.MIN_VALUE; + } + } + updateHeaderScroll(); + } + + @Override + protected LayoutParams generateDefaultLayoutParams() { + return new MyLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); + } + + @Override + protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) { + return new MyLayoutParams(lp.width, lp.height); + } + + @Override + public LayoutParams generateLayoutParams(AttributeSet attrs) { + return new MyLayoutParams(getContext(), attrs); + } + + @Override + protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { + return p instanceof MyLayoutParams; + } + + private static class MyLayoutParams extends LayoutParams { + + public final boolean sticky; + public int scrollLimit; + + MyLayoutParams(int width, int height) { + super(width, height); + sticky = false; + } + + MyLayoutParams(Context c, AttributeSet attrs) { + super(c, attrs); + TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.StickyScroller_Layout); + sticky = a.getBoolean(R.styleable.StickyScroller_Layout_layout_sticky, false); + a.recycle(); + } + } + + private interface MotionEventProxyMethod { + + boolean proxyEvent(ViewGroup view, MotionEvent event); + } + + /** + * Empty view which allows listening for 'Y' changes + */ + public static class EmptySpaceView extends View { + + private Runnable mOnYChangeCallback; + private int mHeight = 0; + + public EmptySpaceView(Context context) { + super(context); + animate().setUpdateListener(v -> notifyYChanged()); + } + + /** + * Sets the height for the empty view + * @return true if the height changed, false otherwise + */ + public boolean setFixedHeight(int height) { + if (mHeight != height) { + mHeight = height; + requestLayout(); + return true; + } + return false; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, makeMeasureSpec(mHeight, EXACTLY)); + } + + public void setOnYChangeCallback(Runnable callback) { + mOnYChangeCallback = callback; + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + notifyYChanged(); + } + + @Override + public void offsetTopAndBottom(int offset) { + super.offsetTopAndBottom(offset); + notifyYChanged(); + } + + @Override + public void setTranslationY(float translationY) { + super.setTranslationY(translationY); + notifyYChanged(); + } + + private void notifyYChanged() { + if (mOnYChangeCallback != null) { + mOnYChangeCallback.run(); + } + } + } +} diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java index 8962c4f5eb..2ac1e9481b 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; 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; @@ -42,7 +45,7 @@ import com.android.launcher3.Utilities; import com.android.launcher3.dragndrop.DragOptions; import com.android.launcher3.popup.PopupDataProvider; import com.android.launcher3.testing.TestLogging; -import com.android.launcher3.testing.TestProtocol; +import com.android.launcher3.testing.shared.TestProtocol; import com.android.launcher3.touch.ItemLongClickListener; import com.android.launcher3.util.SystemUiController; import com.android.launcher3.util.Themes; @@ -246,6 +249,12 @@ public abstract class BaseWidgetSheet extends AbstractSlideInView<Launcher> 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 fe83f3f606..9c21ea2bb7 100644 --- a/src/com/android/launcher3/widget/LauncherAppWidgetHost.java +++ b/src/com/android/launcher3/widget/LauncherAppWidgetHost.java @@ -16,242 +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.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.model.WidgetsModel; -import com.android.launcher3.model.data.ItemInfo; -import com.android.launcher3.testing.TestLogging; -import com.android.launcher3.testing.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 { - - 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<>(); +class LauncherAppWidgetHost extends AppWidgetHost { + @NonNull + private final ArrayList<LauncherWidgetHolder.ProviderChangedListener> + mProviderChangeListeners = new ArrayList<>(); + @NonNull private final Context mContext; - private int mFlags = FLAG_STATE_IS_NORMAL; - private IntConsumer mAppWidgetRemovedCallback = null; + @Nullable + private final IntConsumer mAppWidgetRemovedCallback; + @NonNull + private final LauncherWidgetHolder mHolder; - public LauncherAppWidgetHost(Context context) { - this(context, null); - } - - 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 { - 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 widget - for (int i = mViews.size() - 1; i >= 0; i--) { - LauncherAppWidgetHostView view = mViews.valueAt(i); - if (view instanceof DeferredAppWidgetHostView) { - view.reInflate(); - } - } - } - - @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) { - 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); @@ -265,6 +110,7 @@ public class LauncherAppWidgetHost extends AppWidgetHost { * * @param appWidgetId TODO: make this override when SDK is updated */ + @Override public void onAppWidgetRemoved(int appWidgetId) { if (mAppWidgetRemovedCallback == null) { return; @@ -272,81 +118,12 @@ public class LauncherAppWidgetHost extends AppWidgetHost { 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(); - 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 08651523a6..bc3889fd26 100644 --- a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java +++ b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java @@ -43,6 +43,7 @@ import com.android.launcher3.CheckLongPressHelper; import com.android.launcher3.Launcher; import com.android.launcher3.R; import com.android.launcher3.Utilities; +import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.LauncherAppWidgetInfo; @@ -85,16 +86,12 @@ public class LauncherAppWidgetHostView extends BaseLauncherAppWidgetHostView private Runnable mAutoAdvanceRunnable; private long mDeferUpdatesUntilMillis = 0; - private RemoteViews mDeferredRemoteViews; + RemoteViews mLastRemoteViews; private boolean mHasDeferredColorChange = false; private @Nullable SparseIntArray mDeferredColorChange = null; // 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; @@ -150,11 +147,18 @@ public class LauncherAppWidgetHostView extends BaseLauncherAppWidgetHostView TRACE_METHOD_NAME + getAppWidgetInfo().provider, getAppWidgetId()); mTrackingWidgetUpdate = false; } - if (isDeferringUpdates()) { - mDeferredRemoteViews = remoteViews; - return; + if (FeatureFlags.ENABLE_CACHED_WIDGET.get()) { + mLastRemoteViews = remoteViews; + if (isDeferringUpdates()) { + return; + } + } else { + if (isDeferringUpdates()) { + mLastRemoteViews = remoteViews; + return; + } + mLastRemoteViews = null; } - mDeferredRemoteViews = null; super.updateAppWidget(remoteViews); @@ -218,8 +222,7 @@ public class LauncherAppWidgetHostView extends BaseLauncherAppWidgetHostView SparseIntArray deferredColors; boolean hasDeferredColors; mDeferUpdatesUntilMillis = 0; - remoteViews = mDeferredRemoteViews; - mDeferredRemoteViews = null; + remoteViews = mLastRemoteViews; deferredColors = mDeferredColorChange; hasDeferredColors = mHasDeferredColorChange; mDeferredColorChange = null; @@ -307,27 +310,9 @@ public class LauncherAppWidgetHostView extends BaseLauncherAppWidgetHostView } } - @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}. */ @@ -340,8 +325,6 @@ public class LauncherAppWidgetHostView extends BaseLauncherAppWidgetHostView /** 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 0000000000..d7235ade3c --- /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 470a800366..ccf4b2ea04 100644 --- a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java +++ b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java @@ -19,6 +19,9 @@ import android.appwidget.AppWidgetHostView; 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 @@ public class PendingAddWidgetInfo extends PendingAddItemInfo { 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 46c0b99938..bbbc329c9c 100644 --- a/src/com/android/launcher3/widget/PendingItemDragHelper.java +++ b/src/com/android/launcher3/widget/PendingItemDragHelper.java @@ -39,10 +39,11 @@ import com.android.launcher3.R; 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; -import com.android.launcher3.testing.TestProtocol; +import com.android.launcher3.testing.shared.TestProtocol; import com.android.launcher3.widget.dragndrop.AppWidgetHostViewDragListener; import com.android.launcher3.widget.util.WidgetSizes; @@ -181,7 +182,8 @@ public class PendingItemDragHelper extends DragPreviewProvider { 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 93132664ef..9319a9c2c4 100644 --- a/src/com/android/launcher3/widget/WidgetAddFlowHandler.java +++ b/src/com/android/launcher3/widget/WidgetAddFlowHandler.java @@ -55,7 +55,7 @@ public class WidgetAddFlowHandler implements Parcelable { 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 @@ public class WidgetAddFlowHandler implements Parcelable { 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 2796721c63..ce47d70c25 100644 --- a/src/com/android/launcher3/widget/WidgetCell.java +++ b/src/com/android/launcher3/widget/WidgetCell.java @@ -284,6 +284,40 @@ public class WidgetCell extends LinearLayout { 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 @@ public class WidgetCell extends LinearLayout { // 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 @@ public class WidgetCell extends LinearLayout { 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 46141e0bc3..b18cd471cb 100644 --- a/src/com/android/launcher3/widget/WidgetHostViewLoader.java +++ b/src/com/android/launcher3/widget/WidgetHostViewLoader.java @@ -59,7 +59,7 @@ public class WidgetHostViewLoader implements DragController.DragListener { // Cleanup widget id if (mWidgetLoadingId != -1) { - mLauncher.getAppWidgetHost().deleteAppWidgetId(mWidgetLoadingId); + mLauncher.getAppWidgetHolder().deleteAppWidgetId(mWidgetLoadingId); mWidgetLoadingId = -1; } @@ -69,7 +69,7 @@ public class WidgetHostViewLoader implements DragController.DragListener { 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 @@ public class WidgetHostViewLoader implements DragController.DragListener { 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 @@ public class WidgetHostViewLoader implements DragController.DragListener { 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/SearchAndRecommendationsScrollController.java b/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java deleted file mode 100644 index 716dcf3398..0000000000 --- a/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java +++ /dev/null @@ -1,223 +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.widget.picker; - -import static com.android.launcher3.anim.AnimatorListeners.forEndCallback; - -import android.animation.Animator; -import android.animation.ObjectAnimator; -import android.util.FloatProperty; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.recyclerview.widget.RecyclerView; - -import com.android.launcher3.R; -import com.android.launcher3.widget.picker.WidgetsSpaceViewHolderBinder.EmptySpaceView; -import com.android.launcher3.widget.picker.search.WidgetsSearchBar; - -/** - * A controller which measures & updates {@link WidgetsFullSheet}'s views padding, margin and - * vertical displacement upon scrolling. - */ -final class SearchAndRecommendationsScrollController implements - RecyclerView.OnChildAttachStateChangeListener { - - private static final FloatProperty<SearchAndRecommendationsScrollController> SCROLL_OFFSET = - new FloatProperty<SearchAndRecommendationsScrollController>("scrollAnimOffset") { - @Override - public void setValue(SearchAndRecommendationsScrollController controller, float offset) { - controller.mScrollOffset = offset; - controller.updateHeaderScroll(); - } - - @Override - public Float get(SearchAndRecommendationsScrollController controller) { - return controller.mScrollOffset; - } - }; - - private static final MotionEventProxyMethod INTERCEPT_PROXY = ViewGroup::onInterceptTouchEvent; - private static final MotionEventProxyMethod TOUCH_PROXY = ViewGroup::onTouchEvent; - - final SearchAndRecommendationsView mContainer; - final View mSearchBarContainer; - final WidgetsSearchBar mSearchBar; - final TextView mHeaderTitle; - final WidgetsRecommendationTableLayout mRecommendedWidgetsTable; - @Nullable final View mTabBar; - - private WidgetsRecyclerView mCurrentRecyclerView; - private EmptySpaceView mCurrentEmptySpaceView; - - private float mLastScroll = 0; - private float mScrollOffset = 0; - private Animator mOffsetAnimator; - - private boolean mShouldForwardToRecyclerView = false; - - private int mHeaderHeight; - - SearchAndRecommendationsScrollController( - SearchAndRecommendationsView searchAndRecommendationContainer) { - mContainer = searchAndRecommendationContainer; - mSearchBarContainer = mContainer.findViewById(R.id.search_bar_container); - mSearchBar = mContainer.findViewById(R.id.widgets_search_bar); - mHeaderTitle = mContainer.findViewById(R.id.title); - mRecommendedWidgetsTable = mContainer.findViewById(R.id.recommended_widget_table); - mTabBar = mContainer.findViewById(R.id.tabs); - - mContainer.setSearchAndRecommendationScrollController(this); - } - - public void setCurrentRecyclerView(WidgetsRecyclerView currentRecyclerView) { - boolean animateReset = mCurrentRecyclerView != null; - if (mCurrentRecyclerView != null) { - mCurrentRecyclerView.removeOnChildAttachStateChangeListener(this); - } - mCurrentRecyclerView = currentRecyclerView; - mCurrentRecyclerView.addOnChildAttachStateChangeListener(this); - findCurrentEmptyView(); - reset(animateReset); - } - - public int getHeaderHeight() { - return mHeaderHeight; - } - - private void updateHeaderScroll() { - mLastScroll = getCurrentScroll(); - mHeaderTitle.setTranslationY(mLastScroll); - mRecommendedWidgetsTable.setTranslationY(mLastScroll); - - float searchYDisplacement = Math.max(mLastScroll, -mSearchBarContainer.getTop()); - mSearchBarContainer.setTranslationY(searchYDisplacement); - - if (mTabBar != null) { - float tabsDisplacement = Math.max(mLastScroll, -mTabBar.getTop() - + mSearchBarContainer.getHeight()); - mTabBar.setTranslationY(tabsDisplacement); - } - } - - private float getCurrentScroll() { - return mScrollOffset + (mCurrentEmptySpaceView == null ? 0 : mCurrentEmptySpaceView.getY()); - } - - /** - * Updates the scrollable header height - * - * @return {@code true} if the header height or dependent property changed. - */ - public boolean updateHeaderHeight() { - boolean hasSizeUpdated = false; - - int headerHeight = mContainer.getMeasuredHeight(); - if (headerHeight != mHeaderHeight) { - mHeaderHeight = headerHeight; - hasSizeUpdated = true; - } - - if (mCurrentEmptySpaceView != null - && mCurrentEmptySpaceView.setFixedHeight(mHeaderHeight)) { - hasSizeUpdated = true; - } - return hasSizeUpdated; - } - - /** Resets any previous view translation. */ - public void reset(boolean animate) { - if (mOffsetAnimator != null) { - mOffsetAnimator.cancel(); - mOffsetAnimator = null; - } - - mScrollOffset = 0; - if (!animate) { - updateHeaderScroll(); - } else { - float startValue = mLastScroll - getCurrentScroll(); - mOffsetAnimator = ObjectAnimator.ofFloat(this, SCROLL_OFFSET, startValue, 0); - mOffsetAnimator.addListener(forEndCallback(() -> mOffsetAnimator = null)); - mOffsetAnimator.start(); - } - } - - /** - * Returns {@code true} if a touch event should be intercepted by this controller. - */ - public boolean onInterceptTouchEvent(MotionEvent event) { - return (mShouldForwardToRecyclerView = proxyMotionEvent(event, INTERCEPT_PROXY)); - } - - /** - * Returns {@code true} if this controller has intercepted and consumed a touch event. - */ - public boolean onTouchEvent(MotionEvent event) { - return mShouldForwardToRecyclerView && proxyMotionEvent(event, TOUCH_PROXY); - } - - private boolean proxyMotionEvent(MotionEvent event, MotionEventProxyMethod method) { - float dx = mCurrentRecyclerView.getLeft() - mContainer.getLeft(); - float dy = mCurrentRecyclerView.getTop() - mContainer.getTop(); - event.offsetLocation(dx, dy); - try { - return method.proxyEvent(mCurrentRecyclerView, event); - } finally { - event.offsetLocation(-dx, -dy); - } - } - - @Override - public void onChildViewAttachedToWindow(@NonNull View view) { - if (view instanceof EmptySpaceView) { - findCurrentEmptyView(); - } - } - - @Override - public void onChildViewDetachedFromWindow(@NonNull View view) { - if (view == mCurrentEmptySpaceView) { - findCurrentEmptyView(); - } - } - - private void findCurrentEmptyView() { - if (mCurrentEmptySpaceView != null) { - mCurrentEmptySpaceView.setOnYChangeCallback(null); - mCurrentEmptySpaceView = null; - } - int childCount = mCurrentRecyclerView.getChildCount(); - for (int i = 0; i < childCount; i++) { - View view = mCurrentRecyclerView.getChildAt(i); - if (view instanceof EmptySpaceView) { - mCurrentEmptySpaceView = (EmptySpaceView) view; - mCurrentEmptySpaceView.setFixedHeight(getHeaderHeight()); - mCurrentEmptySpaceView.setOnYChangeCallback(this::updateHeaderScroll); - return; - } - } - } - - private interface MotionEventProxyMethod { - - boolean proxyEvent(ViewGroup view, MotionEvent event); - } -} diff --git a/src/com/android/launcher3/widget/picker/SearchAndRecommendationsView.java b/src/com/android/launcher3/widget/picker/SearchAndRecommendationsView.java deleted file mode 100644 index 0d7d2b5e78..0000000000 --- a/src/com/android/launcher3/widget/picker/SearchAndRecommendationsView.java +++ /dev/null @@ -1,62 +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.widget.picker; - -import android.content.Context; -import android.util.AttributeSet; -import android.view.MotionEvent; -import android.widget.LinearLayout; - -/** - * A {@link LinearLayout} container for holding search and widgets recommendation. - * - * <p>This class intercepts touch events and dispatch them to the right view. - */ -public class SearchAndRecommendationsView extends LinearLayout { - private SearchAndRecommendationsScrollController mController; - - public SearchAndRecommendationsView(Context context) { - this(context, /* attrs= */ null); - } - - public SearchAndRecommendationsView(Context context, AttributeSet attrs) { - this(context, attrs, /* defStyleAttr= */ 0); - } - - public SearchAndRecommendationsView(Context context, AttributeSet attrs, int defStyleAttr) { - this(context, attrs, defStyleAttr, /* defStyleRes= */ 0); - } - - public SearchAndRecommendationsView(Context context, AttributeSet attrs, int defStyleAttr, - int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - } - - public void setSearchAndRecommendationScrollController( - SearchAndRecommendationsScrollController controller) { - mController = controller; - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent event) { - return mController.onInterceptTouchEvent(event) || super.onInterceptTouchEvent(event); - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - return mController.onTouchEvent(event) || super.onTouchEvent(event); - } -} diff --git a/src/com/android/launcher3/widget/picker/WidgetPagedView.java b/src/com/android/launcher3/widget/picker/WidgetPagedView.java new file mode 100644 index 0000000000..c95ec5f44b --- /dev/null +++ b/src/com/android/launcher3/widget/picker/WidgetPagedView.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.widget.picker; + +import android.content.Context; +import android.graphics.Rect; +import android.util.AttributeSet; + +import com.android.launcher3.PagedView; +import com.android.launcher3.workprofile.PersonalWorkPagedView; + +/** + * A {@link PagedView} for showing different widgets for the personal and work profile. + */ +public class WidgetPagedView extends PersonalWorkPagedView { + + public WidgetPagedView(Context context) { + this(context, null); + } + + public WidgetPagedView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public WidgetPagedView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + setPageSpacing(getPaddingLeft()); + } + + @Override + public void getDrawingRect(Rect outRect) { + super.getDrawingRect(outRect); + outRect.left += getPaddingLeft(); + outRect.right -= getPaddingRight(); + } +} diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java index a49cdc005a..38cdb4b500 100644 --- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java +++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java @@ -19,7 +19,7 @@ import static android.view.View.MeasureSpec.makeMeasureSpec; import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGETSTRAY_SEARCHED; -import static com.android.launcher3.testing.TestProtocol.NORMAL_STATE_ORDINAL; +import static com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -62,11 +62,13 @@ import com.android.launcher3.pm.UserCache; import com.android.launcher3.views.ArrowTipView; import com.android.launcher3.views.RecyclerViewFastScroller; import com.android.launcher3.views.SpringRelativeLayout; +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; import com.android.launcher3.widget.util.WidgetsTableUtils; import com.android.launcher3.workprofile.PersonalWorkPagedView; import com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip.OnActivePageChangedListener; @@ -83,7 +85,6 @@ public class WidgetsFullSheet extends BaseWidgetSheet 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; @@ -161,7 +162,14 @@ public class WidgetsFullSheet extends BaseWidgetSheet private boolean mIsNoWidgetsViewNeeded; private int mMaxSpansPerRow = DEFAULT_MAX_HORIZONTAL_SPANS; private TextView mNoWidgetsView; - private SearchAndRecommendationsScrollController mSearchScrollController; + + private StickyHeaderLayout mSearchScrollView; + private WidgetsRecommendationTableLayout mRecommendedWidgetsTable; + private View mTabBar; + 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); @@ -214,17 +222,23 @@ public class WidgetsFullSheet extends BaseWidgetSheet } mNoWidgetsView = findViewById(R.id.no_widgets_text); - mSearchScrollController = new SearchAndRecommendationsScrollController( - findViewById(R.id.search_and_recommendations_container)); - mSearchScrollController.setCurrentRecyclerView( - findViewById(R.id.primary_widgets_list_view)); - mSearchScrollController.mRecommendedWidgetsTable.setWidgetCellLongClickListener(this); - mSearchScrollController.mRecommendedWidgetsTable.setWidgetCellOnClickListener(this); + + mSearchScrollView = findViewById(R.id.search_and_recommendations_container); + mSearchScrollView.setCurrentRecyclerView(findViewById(R.id.primary_widgets_list_view)); + + mRecommendedWidgetsTable = mSearchScrollView.findViewById(R.id.recommended_widget_table); + mRecommendedWidgetsTable.setWidgetCellLongClickListener(this); + mRecommendedWidgetsTable.setWidgetCellOnClickListener(this); + + mTabBar = mSearchScrollView.findViewById(R.id.tabs); + mSearchBarContainer = mSearchScrollView.findViewById(R.id.search_bar_container); + mSearchBar = mSearchScrollView.findViewById(R.id.widgets_search_bar); + mHeaderTitle = mSearchScrollView.findViewById(R.id.title); onRecommendedWidgetsBound(); onWidgetsBound(); - mSearchScrollController.mSearchBar.initialize( + mSearchBar.initialize( mActivityContext.getPopupDataProvider(), /* searchModeListener= */ this); setUpEducationViewsIfNeeded(); @@ -258,7 +272,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet reset(); resetExpandedHeaders(); mCurrentWidgetsRecyclerView = recyclerView; - mSearchScrollController.setCurrentRecyclerView(recyclerView); + mSearchScrollView.setCurrentRecyclerView(recyclerView); } } @@ -285,7 +299,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet mAdapters.get(AdapterHolder.WORK).mWidgetsRecyclerView.scrollToTop(); } mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView.scrollToTop(); - mSearchScrollController.reset(/* animate= */ true); + mSearchScrollView.reset(/* animate= */ true); } @VisibleForTesting @@ -308,7 +322,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); - mActivityContext.getAppWidgetHost().addProviderChangeListener(this); + mActivityContext.getAppWidgetHolder().addProviderChangeListener(this); notifyWidgetProvidersChanged(); onRecommendedWidgetsBound(); } @@ -316,7 +330,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - mActivityContext.getAppWidgetHost().removeProviderChangeListener(this); + mActivityContext.getAppWidgetHolder().removeProviderChangeListener(this); mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView .removeOnAttachStateChangeListener(mBindScrollbarInSearchMode); if (mHasWorkProfile) { @@ -355,8 +369,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet @Override protected void onContentHorizontalMarginChanged(int contentHorizontalMarginInPx) { - setContentViewChildHorizontalMargin(mSearchScrollController.mContainer, - contentHorizontalMarginInPx); + setContentViewChildHorizontalMargin(mSearchScrollView, contentHorizontalMarginInPx); if (mViewPager == null) { setContentViewChildHorizontalPadding( mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView, @@ -390,16 +403,8 @@ public class WidgetsFullSheet extends BaseWidgetSheet protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { doMeasure(widthMeasureSpec, heightMeasureSpec); - if (mSearchScrollController.updateHeaderHeight()) { - doMeasure(widthMeasureSpec, heightMeasureSpec); - } - if (updateMaxSpansPerRow()) { doMeasure(widthMeasureSpec, heightMeasureSpec); - - if (mSearchScrollController.updateHeaderHeight()) { - doMeasure(widthMeasureSpec, heightMeasureSpec); - } } } @@ -460,7 +465,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet if (mHasWorkProfile) { mViewPager.setVisibility(VISIBLE); - mSearchScrollController.mTabBar.setVisibility(VISIBLE); + mTabBar.setVisibility(VISIBLE); AdapterHolder workUserAdapterHolder = mAdapters.get(AdapterHolder.WORK); workUserAdapterHolder.mWidgetsListAdapter.setWidgets(allWidgets); onActivePageChanged(mViewPager.getCurrentPage()); @@ -508,10 +513,10 @@ public class WidgetsFullSheet extends BaseWidgetSheet private void setViewVisibilityBasedOnSearch(boolean isInSearchMode) { mIsInSearchMode = isInSearchMode; if (isInSearchMode) { - mSearchScrollController.mRecommendedWidgetsTable.setVisibility(GONE); + mRecommendedWidgetsTable.setVisibility(GONE); if (mHasWorkProfile) { mViewPager.setVisibility(GONE); - mSearchScrollController.mTabBar.setVisibility(GONE); + mTabBar.setVisibility(GONE); } else { mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView.setVisibility(GONE); } @@ -539,7 +544,6 @@ public class WidgetsFullSheet extends BaseWidgetSheet } List<WidgetItem> recommendedWidgets = mActivityContext.getPopupDataProvider().getRecommendedWidgets(); - WidgetsRecommendationTableLayout table = mSearchScrollController.mRecommendedWidgetsTable; if (recommendedWidgets.size() > 0) { float noWidgetsViewHeight = 0; if (mIsNoWidgetsViewNeeded) { @@ -562,9 +566,10 @@ public class WidgetsFullSheet extends BaseWidgetSheet List<ArrayList<WidgetItem>> recommendedWidgetsInTable = WidgetsTableUtils.groupWidgetItemsIntoTableWithoutReordering( recommendedWidgets, mMaxSpansPerRow); - table.setRecommendedWidgets(recommendedWidgetsInTable, maxTableHeight); + mRecommendedWidgetsTable.setRecommendedWidgets( + recommendedWidgetsInTable, maxTableHeight); } else { - table.setVisibility(GONE); + mRecommendedWidgetsTable.setVisibility(GONE); } } @@ -577,7 +582,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet 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() { @@ -598,7 +603,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet @Override protected void handleClose(boolean animate) { - handleClose(animate, DEFAULT_OPEN_DURATION); + handleClose(animate, mActivityContext.getDeviceProfile().bottomSheetCloseDuration); } @Override @@ -611,18 +616,18 @@ public class WidgetsFullSheet extends BaseWidgetSheet // 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 (mSearchScrollController.mSearchBar.isSearchBarFocused() - && !getPopupContainer().isEventOverView( - mSearchScrollController.mSearchBarContainer, ev)) { - mSearchScrollController.mSearchBar.clearSearchBarFocus(); + if (mSearchBar.isSearchBarFocused() + && !getPopupContainer().isEventOverView(mSearchBarContainer, ev)) { + mSearchBar.clearSearchBarFocus(); } } return super.onControllerInterceptTouchEvent(ev); @@ -638,6 +643,51 @@ public class WidgetsFullSheet extends BaseWidgetSheet 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) { @@ -663,8 +713,8 @@ public class WidgetsFullSheet extends BaseWidgetSheet @Override public int getHeaderViewHeight() { - return measureHeightWithVerticalMargins(mSearchScrollController.mHeaderTitle) - + measureHeightWithVerticalMargins(mSearchScrollController.mSearchBarContainer); + return measureHeightWithVerticalMargins(mHeaderTitle) + + measureHeightWithVerticalMargins(mSearchBarContainer); } /** private the height, in pixel, + the vertical margins of a given view. */ @@ -681,14 +731,14 @@ public class WidgetsFullSheet extends BaseWidgetSheet protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); if (mIsInSearchMode) { - mSearchScrollController.mSearchBar.reset(); + mSearchBar.reset(); } } @Override public boolean onBackPressed() { if (mIsInSearchMode) { - mSearchScrollController.mSearchBar.reset(); + mSearchBar.reset(); return true; } return super.onBackPressed(); @@ -701,10 +751,9 @@ public class WidgetsFullSheet extends BaseWidgetSheet } @Nullable private View getViewToShowEducationTip() { - if (mSearchScrollController.mRecommendedWidgetsTable.getVisibility() == VISIBLE - && mSearchScrollController.mRecommendedWidgetsTable.getChildCount() > 0) { - return ((ViewGroup) mSearchScrollController.mRecommendedWidgetsTable.getChildAt(0)) - .getChildAt(0); + if (mRecommendedWidgetsTable.getVisibility() == VISIBLE + && mRecommendedWidgetsTable.getChildCount() > 0) { + return ((ViewGroup) mRecommendedWidgetsTable.getChildAt(0)).getChildAt(0); } AdapterHolder adapterHolder = mAdapters.get(mIsInSearchMode @@ -801,7 +850,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet } private int getEmptySpaceHeight() { - return mSearchScrollController.getHeaderHeight(); + return mSearchScrollView.getHeaderHeight(); } void setup(WidgetsRecyclerView recyclerView) { diff --git a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java index 0e5a7d7ba9..e6b9dcaae1 100644 --- a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java +++ b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java @@ -21,7 +21,6 @@ import static com.android.launcher3.recyclerview.ViewHolderBinder.POSITION_FIRST import static com.android.launcher3.recyclerview.ViewHolderBinder.POSITION_LAST; import android.content.Context; -import android.graphics.Rect; import android.os.Process; import android.util.Log; import android.util.SparseArray; @@ -36,7 +35,6 @@ import androidx.annotation.Nullable; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView.Adapter; -import androidx.recyclerview.widget.RecyclerView.LayoutParams; import androidx.recyclerview.widget.RecyclerView.ViewHolder; import com.android.launcher3.R; @@ -80,10 +78,10 @@ public class WidgetsListAdapter extends Adapter<ViewHolder> implements OnHeaderC private static final boolean DEBUG = false; /** Uniquely identifies widgets list view type within the app. */ - private static final int VIEW_TYPE_WIDGETS_SPACE = R.id.view_type_widgets_space; - private static final int VIEW_TYPE_WIDGETS_LIST = R.id.view_type_widgets_list; - private static final int VIEW_TYPE_WIDGETS_HEADER = R.id.view_type_widgets_header; - private static final int VIEW_TYPE_WIDGETS_SEARCH_HEADER = R.id.view_type_widgets_search_header; + public static final int VIEW_TYPE_WIDGETS_SPACE = R.id.view_type_widgets_space; + public static final int VIEW_TYPE_WIDGETS_LIST = R.id.view_type_widgets_list; + public static final int VIEW_TYPE_WIDGETS_HEADER = R.id.view_type_widgets_header; + public static final int VIEW_TYPE_WIDGETS_SEARCH_HEADER = R.id.view_type_widgets_search_header; private final Context mContext; private final WidgetsDiffReporter mDiffReporter; @@ -103,7 +101,6 @@ public class WidgetsListAdapter extends Adapter<ViewHolder> implements OnHeaderC @Nullable private Predicate<WidgetsListBaseEntry> mFilter = null; @Nullable private RecyclerView mRecyclerView; @Nullable private PackageUserKey mPendingClickHeader; - private final int mSpacingBetweenEntries; private int mMaxSpanSize = 4; public WidgetsListAdapter(Context context, LayoutInflater layoutInflater, @@ -133,28 +130,11 @@ public class WidgetsListAdapter extends Adapter<ViewHolder> implements OnHeaderC mViewHolderBinders.put( VIEW_TYPE_WIDGETS_SPACE, new WidgetsSpaceViewHolderBinder(emptySpaceHeightProvider)); - mSpacingBetweenEntries = - context.getResources().getDimensionPixelSize(R.dimen.widget_list_entry_spacing); } @Override public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) { mRecyclerView = recyclerView; - - mRecyclerView.addItemDecoration(new RecyclerView.ItemDecoration() { - @Override - public void getItemOffsets( - @NonNull Rect outRect, - @NonNull View view, - @NonNull RecyclerView parent, - @NonNull RecyclerView.State state) { - super.getItemOffsets(outRect, view, parent, state); - int position = ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(); - boolean isHeader = - view.getTag(R.id.tag_widget_entry) instanceof WidgetsListBaseEntry.Header; - outRect.top += position > 0 && isHeader ? mSpacingBetweenEntries : 0; - } - }); } @Override @@ -286,7 +266,6 @@ public class WidgetsListAdapter extends Adapter<ViewHolder> implements OnHeaderC listPos |= POSITION_LAST; } viewHolderBinder.bindViewHolder(holder, mVisibleEntries.get(pos), listPos, payloads); - holder.itemView.setTag(R.id.tag_widget_entry, entry); } @Override diff --git a/src/com/android/launcher3/widget/picker/WidgetsListDrawableFactory.java b/src/com/android/launcher3/widget/picker/WidgetsListDrawableFactory.java index c61e3a4349..984a2741e7 100644 --- a/src/com/android/launcher3/widget/picker/WidgetsListDrawableFactory.java +++ b/src/com/android/launcher3/widget/picker/WidgetsListDrawableFactory.java @@ -27,6 +27,7 @@ import android.content.res.ColorStateList; import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; +import android.graphics.drawable.InsetDrawable; import android.graphics.drawable.RippleDrawable; import android.graphics.drawable.StateListDrawable; @@ -40,6 +41,8 @@ final class WidgetsListDrawableFactory { private final float mMiddleCornerRadius; private final ColorStateList mSurfaceColor; private final ColorStateList mRippleColor; + private final int mVerticalPadding; + private final int mHeaderMargin; WidgetsListDrawableFactory(Context context) { Resources res = context.getResources(); @@ -48,6 +51,9 @@ final class WidgetsListDrawableFactory { mSurfaceColor = context.getColorStateList(R.color.surface); mRippleColor = ColorStateList.valueOf( Themes.getAttrColor(context, android.R.attr.colorControlHighlight)); + mVerticalPadding = + res.getDimensionPixelSize(R.dimen.widget_list_header_view_vertical_padding); + mHeaderMargin = res.getDimensionPixelSize(R.dimen.widget_list_entry_spacing); } /** @@ -74,7 +80,10 @@ final class WidgetsListDrawableFactory { stateList.addState( LAST.mStateSet, createRoundedRectDrawable(mMiddleCornerRadius, mTopBottomCornerRadius)); - return new RippleDrawable(mRippleColor, /* content= */ stateList, /* mask= */ stateList); + RippleDrawable ripple = + new RippleDrawable(mRippleColor, /* content= */ stateList, /* mask= */ stateList); + ripple.setPadding(0, mVerticalPadding, 0, mVerticalPadding); + return new InsetDrawable(ripple, 0, mHeaderMargin, 0, 0); } /** diff --git a/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java index bdf646bfaf..698e76498a 100644 --- a/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java +++ b/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java @@ -20,23 +20,14 @@ import android.content.Context; import android.graphics.Point; import android.util.AttributeSet; import android.view.MotionEvent; -import android.view.View; -import android.widget.TableLayout; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView.OnItemTouchListener; -import com.android.launcher3.DeviceProfile; import com.android.launcher3.FastScrollRecyclerView; import com.android.launcher3.R; -import com.android.launcher3.views.ActivityContext; -import com.android.launcher3.widget.model.WidgetListSpaceEntry; -import com.android.launcher3.widget.model.WidgetsListBaseEntry; -import com.android.launcher3.widget.model.WidgetsListContentEntry; -import com.android.launcher3.widget.model.WidgetsListHeaderEntry; -import com.android.launcher3.widget.model.WidgetsListSearchHeaderEntry; -import com.android.launcher3.widget.picker.WidgetsSpaceViewHolderBinder.EmptySpaceView; +import com.android.launcher3.util.ScrollableLayoutManager; /** * The widgets recycler view. @@ -51,13 +42,6 @@ public class WidgetsRecyclerView extends FastScrollRecyclerView implements OnIte private boolean mTouchDownOnScroller; private HeaderViewDimensionsProvider mHeaderViewDimensionsProvider; - // Cached sizes - private int mLastVisibleWidgetContentTableHeight = 0; - private int mWidgetHeaderHeight = 0; - private int mWidgetEmptySpaceHeight = 0; - - private final int mSpacingBetweenEntries; - public WidgetsRecyclerView(Context context) { this(context, null); } @@ -71,21 +55,12 @@ public class WidgetsRecyclerView extends FastScrollRecyclerView implements OnIte super(context, attrs, defStyleAttr); mScrollbarTop = getResources().getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin); addOnItemTouchListener(this); - - ActivityContext activity = ActivityContext.lookupContext(getContext()); - DeviceProfile grid = activity.getDeviceProfile(); - - // The spacing used between entries. - mSpacingBetweenEntries = - getResources().getDimensionPixelSize(R.dimen.widget_list_entry_spacing); } @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 @@ -129,7 +104,7 @@ public class WidgetsRecyclerView extends FastScrollRecyclerView implements OnIte } // Skip early if, there no child laid out in the container. - int scrollY = getCurrentScrollY(); + int scrollY = computeVerticalScrollOffset(); if (scrollY < 0) { mScrollbar.setThumbOffsetY(-1); return; @@ -138,67 +113,6 @@ public class WidgetsRecyclerView extends FastScrollRecyclerView implements OnIte synchronizeScrollBarThumbOffsetToViewScroll(scrollY, getAvailableScrollHeight()); } - @Override - public int getCurrentScrollY() { - // Skip early if widgets are not bound. - if (isModelNotReady() || getChildCount() == 0) { - return -1; - } - - int rowIndex = -1; - 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. - rowIndex = ((LinearLayoutManager) layoutManager).findFirstVisibleItemPosition(); - child = layoutManager.findViewByPosition(rowIndex); - } - - 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); - rowIndex = getChildPosition(child); - } - - for (int i = 0; i < getChildCount(); i++) { - View view = getChildAt(i); - if (view instanceof TableLayout) { - // This assumes there is ever only one content shown in this recycler view. - mLastVisibleWidgetContentTableHeight = view.getMeasuredHeight(); - } else if (view instanceof WidgetsListHeader - && mWidgetHeaderHeight == 0 - && view.getMeasuredHeight() > 0) { - // This assumes all header views are of the same height. - mWidgetHeaderHeight = view.getMeasuredHeight(); - } else if (view instanceof EmptySpaceView && view.getMeasuredHeight() > 0) { - mWidgetEmptySpaceHeight = view.getMeasuredHeight(); - } - } - - int scrollPosition = getItemsHeight(rowIndex); - int offset = getLayoutManager().getDecoratedTop(child); - - return getPaddingTop() + scrollPosition - offset; - } - - /** - * Returns the available scroll height, in pixel. - * - * <p>If the recycler view can't be scrolled, returns 0. - */ - @Override - protected int getAvailableScrollHeight() { - // AvailableScrollHeight = Total height of the all items - first page height - int firstPageHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom(); - int totalHeightOfAllItems = getItemsHeight(/* untilIndex= */ mAdapter.getItemCount()); - int availableScrollHeight = totalHeightOfAllItems - firstPageHeight; - return Math.max(0, availableScrollHeight); - } - private boolean isModelNotReady() { return mAdapter.getItemCount() == 0; } @@ -213,8 +127,7 @@ public class WidgetsRecyclerView extends FastScrollRecyclerView implements OnIte @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); @@ -230,6 +143,15 @@ public class WidgetsRecyclerView extends FastScrollRecyclerView implements OnIte } } + /** + * 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) { } @@ -240,39 +162,6 @@ public class WidgetsRecyclerView extends FastScrollRecyclerView implements OnIte } /** - * 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. - */ - private int getItemsHeight(int untilIndex) { - if (untilIndex > mAdapter.getItems().size()) { - untilIndex = mAdapter.getItems().size(); - } - int totalItemsHeight = 0; - for (int i = 0; i < untilIndex; i++) { - WidgetsListBaseEntry entry = mAdapter.getItems().get(i); - if (entry instanceof WidgetsListHeaderEntry - || entry instanceof WidgetsListSearchHeaderEntry) { - totalItemsHeight += mWidgetHeaderHeight; - if (i > 0) { - // Each header contains the spacing between entries as top decoration, except - // the first one. - totalItemsHeight += mSpacingBetweenEntries; - } - } else if (entry instanceof WidgetsListContentEntry) { - totalItemsHeight += mLastVisibleWidgetContentTableHeight; - } else if (entry instanceof WidgetListSpaceEntry) { - totalItemsHeight += mWidgetEmptySpaceHeight; - } else { - throw new UnsupportedOperationException("Can't estimate height for " + entry); - } - } - 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/widget/picker/WidgetsSpaceViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsSpaceViewHolderBinder.java index 1aa5753791..0c4f7aa7a2 100644 --- a/src/com/android/launcher3/widget/picker/WidgetsSpaceViewHolderBinder.java +++ b/src/com/android/launcher3/widget/picker/WidgetsSpaceViewHolderBinder.java @@ -15,16 +15,12 @@ */ package com.android.launcher3.widget.picker; -import static android.view.View.MeasureSpec.EXACTLY; -import static android.view.View.MeasureSpec.makeMeasureSpec; - -import android.content.Context; -import android.view.View; import android.view.ViewGroup; import androidx.recyclerview.widget.RecyclerView.ViewHolder; import com.android.launcher3.recyclerview.ViewHolderBinder; +import com.android.launcher3.views.StickyHeaderLayout.EmptySpaceView; import com.android.launcher3.widget.model.WidgetListSpaceEntry; import java.util.List; @@ -52,64 +48,4 @@ public class WidgetsSpaceViewHolderBinder @ListPosition int position, List<Object> payloads) { ((EmptySpaceView) holder.itemView).setFixedHeight(mEmptySpaceHeightProvider.getAsInt()); } - - /** - * Empty view which allows listening for 'Y' changes - */ - public static class EmptySpaceView extends View { - - private Runnable mOnYChangeCallback; - private int mHeight = 0; - - private EmptySpaceView(Context context) { - super(context); - animate().setUpdateListener(v -> notifyYChanged()); - } - - /** - * Sets the height for the empty view - * @return true if the height changed, false otherwise - */ - public boolean setFixedHeight(int height) { - if (mHeight != height) { - mHeight = height; - requestLayout(); - return true; - } - return false; - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, makeMeasureSpec(mHeight, EXACTLY)); - } - - public void setOnYChangeCallback(Runnable callback) { - mOnYChangeCallback = callback; - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - super.onLayout(changed, left, top, right, bottom); - notifyYChanged(); - } - - @Override - public void offsetTopAndBottom(int offset) { - super.offsetTopAndBottom(offset); - notifyYChanged(); - } - - @Override - public void setTranslationY(float translationY) { - super.setTranslationY(translationY); - notifyYChanged(); - } - - private void notifyYChanged() { - if (mOnYChangeCallback != null) { - mOnYChangeCallback.run(); - } - } - } } diff --git a/src/com/android/launcher3/workprofile/PersonalWorkSlidingTabStrip.java b/src/com/android/launcher3/workprofile/PersonalWorkSlidingTabStrip.java index 49db2a0b5f..e94f3a065d 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.NonNull; 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 @@ import com.android.launcher3.views.ActivityContext; * 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 @@ public class PersonalWorkSlidingTabStrip extends LinearLayout implements PageInd @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 13e4999623..173b4540e6 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 @@ public interface LauncherExterns { * 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 ac02ba4528..582ab230c3 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 @@ public interface LauncherOverlayManager extends Application.ActivityLifecycleCal interface LauncherOverlayCallbacks { - void onScrollChanged(float progress); + void onOverlayScrollChanged(float progress); } } diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java b/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java index 67157494d8..47bf135cee 100644 --- a/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java +++ b/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java @@ -17,11 +17,13 @@ package com.android.launcher3.uioverrides; import android.app.Person; -import android.content.Context; import android.content.pm.ShortcutInfo; import com.android.launcher3.Utilities; +/** + * A wrapper for the hidden API calls + */ public class ApiWrapper { public static final boolean TASKBAR_DRAWN_IN_PROCESS = false; @@ -29,11 +31,4 @@ public class ApiWrapper { public static Person[] getPersons(ShortcutInfo si) { return Utilities.EMPTY_PERSON_ARRAY; } - - /** - * Returns the minimum space that should be left empty at the end of hotseat - */ - public static int getHotseatEndOffset(Context context) { - return 0; - } } 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 bf35dd8d08..772a995324 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 static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_ALLAP 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 @@ import com.android.launcher3.util.Themes; 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 @@ public class AllAppsState extends LauncherState { } @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 @@ public class AllAppsState extends LauncherState { @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 @@ public class AllAppsState extends LauncherState { 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 54cded0fd3..39bd3074a3 100644 --- a/tests/Android.bp +++ b/tests/Android.bp @@ -24,6 +24,18 @@ filegroup { "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 @@ filegroup { "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 @@ android_test { 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 9cc3aeda1f..b17006181e 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/res/layout/test_layout_appwidget_blue.xml b/tests/res/layout/test_layout_appwidget_blue.xml index 8111978c9c..f33e57575f 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 bdc22f98cb..94e8edd936 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 6d0fcb6faa..13e56f3456 100644 --- a/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt +++ b/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt @@ -17,12 +17,17 @@ package com.android.launcher3 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 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 { @@ -49,88 +54,277 @@ abstract class DeviceProfileBaseTest { inv, info, windowBounds, + SparseArray(), isMultiWindowMode, transposeLayoutWithOrientation, useTwoPanels, - isGestureMode + isGestureMode, + DEFAULT_PROVIDER ) - protected fun initializeVarsForPhone(isLandscape: Boolean = false) { - val (x, y) = if (isLandscape) - Pair(3120, 1440) + protected fun initializeVarsForPhone(isGestureMode: Boolean = true, + isVerticalBar: Boolean = false) { + val (x, y) = if (isVerticalBar) + Pair(2400, 1080) else - Pair(1440, 3120) + 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 = 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 - inv = newScalableInvariantDeviceProfile() + 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) { + 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) - inv = newScalableInvariantDeviceProfile() - } + this.isGestureMode = isGestureMode + useTwoPanels = 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() - /** - * 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) + PointF(16f, 64f), + PointF(64f, 16f), + PointF(16f, 64f), + PointF(16f, 64f) ).toTypedArray() - allAppsBorderSpaces = listOf( - PointF(16f, 16f), - PointF(16f, 16f), - PointF(16f, 16f), - PointF(16f, 16f) + + 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() - hotseatBorderSpaces = FloatArray(4) { 16f } - hotseatColumnSpan = IntArray(4) { 4 } - iconSize = FloatArray(4) { 56f } - allAppsIconSize = FloatArray(4) { 56f } - iconTextSize = FloatArray(4) { 14f } + 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 + } + } + + protected fun initializeVarsForTwoPanel(isLandscape: Boolean = false, + isGestureMode: Boolean = true) { + val (x, y) = if (isLandscape) + Pair(2208, 1840) + else + Pair(1840, 2208) + + 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(64f, 83f), - PointF(64f, 83f), - PointF(64f, 83f), - PointF(64f, 83f) + 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(64f, 83f), - PointF(64f, 83f), - PointF(64f, 83f), - PointF(64f, 83f) + 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 + false, + false, + false, + false ) + + devicePaddingId = R.xml.paddings_handhelds } + } + + fun dump(dp: DeviceProfile): String { + val stringWriter = StringWriter() + val printWriter = PrintWriter(stringWriter) + dp.dump(context, "", printWriter) + printWriter.flush() + return stringWriter.toString() + } }
\ No newline at end of file diff --git a/tests/src/com/android/launcher3/DeviceProfileGridDimensionsTest.kt b/tests/src/com/android/launcher3/DeviceProfileGridDimensionsTest.kt deleted file mode 100644 index 80259a59b4..0000000000 --- a/tests/src/com/android/launcher3/DeviceProfileGridDimensionsTest.kt +++ /dev/null @@ -1,128 +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 android.graphics.PointF -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.SmallTest -import com.android.launcher3.util.WindowBounds -import com.google.common.truth.Truth.assertThat -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.ArgumentMatchers -import org.mockito.Mockito.`when` as whenever - -/** - * Test for [DeviceProfile] grid dimensions. - * - * This includes workspace, cell layout, shortcut and widget container, cell sizes, etc. - */ -@SmallTest -@RunWith(AndroidJUnit4::class) -class DeviceProfileGridDimensionsTest : DeviceProfileBaseTest() { - - @Test - fun getCellLayoutWidth_twoPanelLandscapeScalable4By4GridTablet_equalsSinglePanelWidth() { - val tabletWidth = 2560 - val tabletHeight = 1600 - val availableWidth = 2560 - val availableHeight = 1500 - windowBounds = WindowBounds(tabletWidth, tabletHeight, availableWidth, availableHeight, 0) - useTwoPanels = true - whenever(info.isTablet(ArgumentMatchers.any())).thenReturn(true) - whenever(info.densityDpi).thenReturn(320) - inv = newScalableInvariantDeviceProfile() - - val dp = newDP() - - val expectedWorkspaceWidth = availableWidth - val expectedCellLayoutWidth = - (expectedWorkspaceWidth - (dp.workspacePadding.right + dp.workspacePadding.left)) / - dp.panelCount - assertThat(dp.cellLayoutWidth).isEqualTo(expectedCellLayoutWidth) - } - - @Test - fun getCellLayoutHeight_twoPanelLandscapeScalable4By4GridTablet_equalsSinglePanelHeight() { - val tabletWidth = 2560 - val tabletHeight = 1600 - val availableWidth = 2560 - val availableHeight = 1500 - windowBounds = WindowBounds(tabletWidth, tabletHeight, availableWidth, availableHeight, 0) - useTwoPanels = true - whenever(info.isTablet(ArgumentMatchers.any())).thenReturn(true) - whenever(info.densityDpi).thenReturn(320) - inv = newScalableInvariantDeviceProfile() - - val dp = newDP() - - val expectedWorkspaceHeight = availableHeight - val expectedCellLayoutHeight = - expectedWorkspaceHeight - (dp.workspacePadding.top + dp.workspacePadding.bottom) - assertThat(dp.cellLayoutHeight).isEqualTo(expectedCellLayoutHeight) - } - - @Test - fun getCellSize_twoPanelLandscapeScalable4By4GridTablet_equalsSinglePanelWidth() { - val tabletWidth = 2560 - val tabletHeight = 1600 - val availableWidth = 2560 - val availableHeight = 1500 - windowBounds = WindowBounds(tabletWidth, tabletHeight, availableWidth, availableHeight, 0) - useTwoPanels = true - whenever(info.isTablet(ArgumentMatchers.any())).thenReturn(true) - whenever(info.densityDpi).thenReturn(320) - inv = newScalableInvariantDeviceProfile() - - val dp = newDP() - - val expectedWorkspaceWidth = availableWidth - val expectedCellLayoutWidth = - (expectedWorkspaceWidth - (dp.workspacePadding.right + dp.workspacePadding.left)) / - dp.panelCount - val expectedShortcutAndWidgetContainerWidth = - expectedCellLayoutWidth - - (dp.cellLayoutPaddingPx.left + dp.cellLayoutPaddingPx.right) - assertThat(dp.getCellSize().x).isEqualTo( - (expectedShortcutAndWidgetContainerWidth - - ((inv!!.numColumns - 1) * dp.cellLayoutBorderSpacePx.x)) / inv!!.numColumns) - val expectedWorkspaceHeight = availableHeight - val expectedCellLayoutHeight = - expectedWorkspaceHeight - (dp.workspacePadding.top + dp.workspacePadding.bottom) - val expectedShortcutAndWidgetContainerHeight = expectedCellLayoutHeight - - (dp.cellLayoutPaddingPx.top + dp.cellLayoutPaddingPx.bottom) - assertThat(dp.getCellSize().y).isEqualTo( - (expectedShortcutAndWidgetContainerHeight - - ((inv!!.numRows - 1) * dp.cellLayoutBorderSpacePx.y)) / inv!!.numRows) - } - - @Test - fun getPanelCount_twoPanelLandscapeScalable4By4GridTablet_equalsTwoPanels() { - val tabletWidth = 2560 - val tabletHeight = 1600 - val availableWidth = 2560 - val availableHeight = 1500 - windowBounds = WindowBounds(tabletWidth, tabletHeight, availableWidth, availableHeight, 0) - useTwoPanels = true - whenever(info.isTablet(ArgumentMatchers.any())).thenReturn(true) - whenever(info.densityDpi).thenReturn(320) - inv = newScalableInvariantDeviceProfile() - - val dp = newDP() - - assertThat(dp.panelCount).isEqualTo(2) - } -}
\ 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 593239d6f7..0000000000 --- a/tests/src/com/android/launcher3/HotseatShownIconsTest.kt +++ /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 - -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_normal_for_handhelds() { - initializeVarsForPhone() - inv = newScalableInvariantDeviceProfile().apply { - deviceType = TYPE_PHONE - } - - val dp = newDP() - - assertThat(dp.isQsbInline).isFalse() - assertThat(dp.numShownHotseatIcons).isEqualTo(4) - } - - @Test - fun hotseat_size_is_max_when_large_screen() { - initializeVarsForTablet(isLandscape = true) - inv = newScalableInvariantDeviceProfile().apply { - deviceType = TYPE_MULTI_DISPLAY - } - useTwoPanels = true - - val dp = newDP() - - assertThat(dp.isQsbInline).isFalse() - assertThat(dp.numShownHotseatIcons).isEqualTo(6) - } - - @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/InlineQsbTest.kt b/tests/src/com/android/launcher3/InlineQsbTest.kt deleted file mode 100644 index 905c1e1a0f..0000000000 --- a/tests/src/com/android/launcher3/InlineQsbTest.kt +++ /dev/null @@ -1,103 +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.google.common.truth.Truth.assertThat -import org.junit.Test -import org.junit.runner.RunWith - -/** - * Test for [DeviceProfile] - */ -@SmallTest -@RunWith(AndroidJUnit4::class) -class InlineQsbTest : DeviceProfileBaseTest() { - - @Test - fun qsb_is_not_inline_for_phones() { - initializeVarsForPhone() - - val dp = newDP() - - assertThat(dp.isQsbInline).isFalse() - } - - @Test - fun qsb_is_inline_for_tablet_portrait() { - initializeVarsForTablet() - inv = newScalableInvariantDeviceProfile().apply { - inlineQsb = booleanArrayOf( - false, - true, // landscape - false, - false - ) - } - - val dp = DeviceProfile( - context, - inv, - info, - windowBounds, - isMultiWindowMode, - transposeLayoutWithOrientation, - useTwoPanels, - isGestureMode - ) - - assertThat(dp.isQsbInline).isFalse() - } - - @Test - fun qsb_is_inline_for_tablet_landscape() { - initializeVarsForTablet(isLandscape = true) - inv = newScalableInvariantDeviceProfile().apply { - inlineQsb = booleanArrayOf( - false, - true, // landscape - false, - false - ) - numColumns = 6 - numRows = 5 - numShownHotseatIcons = 6 - } - - val dp = newDP() - - if (dp.hotseatQsbHeight > 0) { - assertThat(dp.isQsbInline).isTrue() - } else { // Launcher3 doesn't have QSB height - assertThat(dp.isQsbInline).isFalse() - } - } - - /** - * This test is to make sure that a tablet doesn't inline the QSB if the layout doesn't support - */ - @Test - fun qsb_is_not_inline_for_tablet_landscape_without_inline() { - initializeVarsForTablet(isLandscape = true) - useTwoPanels = true - - val dp = newDP() - - assertThat(dp.isQsbInline).isFalse() - } - -}
\ No newline at end of file diff --git a/tests/src/com/android/launcher3/celllayout/CellLayoutBoard.java b/tests/src/com/android/launcher3/celllayout/CellLayoutBoard.java new file mode 100644 index 0000000000..a32ce3c558 --- /dev/null +++ b/tests/src/com/android/launcher3/celllayout/CellLayoutBoard.java @@ -0,0 +1,230 @@ +/* + * 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.graphics.Point; +import android.graphics.Rect; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.Set; + + +public class CellLayoutBoard { + + public static class CellType { + // The cells marked by this will be filled by 1x1 widgets and will be ignored when + // validating + public static final char IGNORE = 'x'; + // The cells marked by this will be filled by app icons + public static final char ICON = 'i'; + // Empty space + public static final char EMPTY = '-'; + // Widget that will be saved as "main widget" for easier retrieval + public static final char MAIN_WIDGET = 'm'; + // Everything else will be consider a widget + } + + public static class WidgetRect { + public char mType; + public Rect mBounds; + + WidgetRect(char type, Rect bounds) { + this.mType = type; + this.mBounds = bounds; + } + + int getSpanX() { + return mBounds.right - mBounds.left + 1; + } + + int getSpanY() { + return mBounds.top - mBounds.bottom + 1; + } + + int getCellX() { + return mBounds.left; + } + + int getCellY() { + return mBounds.bottom; + } + + boolean shouldIgnore() { + return this.mType == CellType.IGNORE; + } + + @Override + public String toString() { + return "WidgetRect type = " + mType + " bounds = " + mBounds.toString(); + } + } + + public static class IconPoint { + public Point coord; + public char mType; + + public IconPoint(Point coord, char type) { + this.coord = coord; + mType = type; + } + + public char getType() { + return mType; + } + + public void setType(char type) { + mType = type; + } + + public Point getCoord() { + return coord; + } + + public void setCoord(Point coord) { + this.coord = coord; + } + } + + static final int INFINITE = 99999; + + char[][] mWidget = new char[30][30]; + + List<WidgetRect> mWidgetsRects = new ArrayList<>(); + Map<Character, WidgetRect> mWidgetsMap = new HashMap<>(); + + List<IconPoint> mIconPoints = new ArrayList<>(); + Map<Character, IconPoint> mIconsMap = new HashMap<>(); + + Point mMain = new Point(); + + CellLayoutBoard() { + for (int x = 0; x < mWidget.length; x++) { + for (int y = 0; y < mWidget[0].length; y++) { + mWidget[x][y] = CellType.EMPTY; + } + } + } + + public List<WidgetRect> getWidgets() { + return mWidgetsRects; + } + + public List<IconPoint> getIcons() { + return mIconPoints; + } + + public Point getMain() { + return mMain; + } + + public WidgetRect getWidgetRect(char c) { + return mWidgetsMap.get(c); + } + + 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); + search.add(current); + used.add(current); + List<Point> neighbors = new ArrayList<>(List.of( + new Point(-1, 0), + new Point(0, -1), + new Point(1, 0), + new Point(0, 1)) + ); + Rect widgetRect = new Rect(INFINITE, -INFINITE, -INFINITE, INFINITE); + while (!search.isEmpty()) { + current = search.poll(); + widgetRect.top = Math.max(widgetRect.top, current.y); + widgetRect.right = Math.max(widgetRect.right, current.x); + widgetRect.bottom = Math.min(widgetRect.bottom, current.y); + widgetRect.left = Math.min(widgetRect.left, current.x); + for (Point p : neighbors) { + Point next = new Point(current.x + p.x, current.y + p.y); + if (next.x < 0 || next.x >= board.length) continue; + if (next.y < 0 || next.y >= board[0].length) continue; + if (board[next.x][next.y] == type && !used.contains(next)) { + used.add(next); + search.add(next); + } + } + } + return new WidgetRect(type, widgetRect); + } + + public static boolean isWidget(char type) { + return type != CellType.ICON && type != CellType.EMPTY; + } + + public static boolean isIcon(char type) { + return type == CellType.ICON; + } + + private static List<WidgetRect> getRects(char[][] board) { + Set<Point> used = new HashSet<>(); + 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])) { + widgetsRects.add(getWidgetRect(x, y, used, board)); + } + } + } + return widgetsRects; + } + + 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 IconPoint(new Point(x, y), board[x][y])); + } + } + } + return iconPoints; + } + + public static CellLayoutBoard boardFromString(String boardStr) { + String[] lines = boardStr.split("\n"); + CellLayoutBoard board = new CellLayoutBoard(); + + for (int y = 0; y < lines.length; y++) { + String line = lines[y]; + for (int x = 0; x < line.length(); x++) { + char c = line.charAt(x); + if (c == CellType.MAIN_WIDGET) { + board.mMain = new Point(x, y); + } + if (c != CellType.EMPTY) { + board.mWidget[x][y] = line.charAt(x); + } + } + } + board.mWidgetsRects = getRects(board.mWidget); + board.mWidgetsRects.forEach( + widgetRect -> board.mWidgetsMap.put(widgetRect.mType, widgetRect)); + board.mIconPoints = getIconPoints(board.mWidget); + return board; + } +} diff --git a/tests/src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java b/tests/src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java new file mode 100644 index 0000000000..8ce932d281 --- /dev/null +++ b/tests/src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.celllayout; + +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; +import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; + +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Context; + +import com.android.launcher3.LauncherAppState; +import com.android.launcher3.LauncherSettings; +import com.android.launcher3.model.data.ItemInfo; +import com.android.launcher3.ui.AbstractLauncherUiTest; +import com.android.launcher3.util.ContentWriter; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutionException; + +public class FavoriteItemsTransaction { + private ArrayList<ItemInfo> mItemsToSubmit; + private Context mContext; + private ContentResolver mResolver; + public AbstractLauncherUiTest mTest; + + public FavoriteItemsTransaction(Context context, AbstractLauncherUiTest test) { + mItemsToSubmit = new ArrayList<>(); + mContext = context; + mResolver = mContext.getContentResolver(); + mTest = test; + } + + public FavoriteItemsTransaction addItem(ItemInfo itemInfo) { + this.mItemsToSubmit.add(itemInfo); + return this; + } + + public FavoriteItemsTransaction removeLast() { + this.mItemsToSubmit.remove(this.mItemsToSubmit.size() - 1); + return this; + } + + /** + * Commits all the ItemInfo into the database of Favorites + **/ + public void commit() throws ExecutionException, InterruptedException { + List<ContentValues> values = new ArrayList<>(); + for (ItemInfo item : this.mItemsToSubmit) { + ContentWriter writer = new ContentWriter(mContext); + item.onAddToDatabase(writer); + writer.put(LauncherSettings.Favorites._ID, item.id); + values.add(writer.getValues(mContext)); + } + // Submit the icons to the database in the model thread to prevent race conditions + MODEL_EXECUTOR.submit(() -> mResolver.bulkInsert(LauncherSettings.Favorites.CONTENT_URI, + values.toArray(new ContentValues[0]))).get(); + // Reload the state of the Launcher + MAIN_EXECUTOR.submit(() -> LauncherAppState.getInstance( + mContext).getModel().forceReload()).get(); + } +} diff --git a/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java b/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java new file mode 100644 index 0000000000..9da7e0f39b --- /dev/null +++ b/tests/src/com/android/launcher3/celllayout/ReorderWidgets.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.launcher3.celllayout; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import android.graphics.Point; +import android.util.Log; +import android.view.View; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import com.android.launcher3.CellLayout; +import com.android.launcher3.celllayout.testcases.FullReorderCase; +import com.android.launcher3.celllayout.testcases.MoveOutReorderCase; +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.tapl.Widget; +import com.android.launcher3.tapl.WidgetResizeFrame; +import com.android.launcher3.ui.AbstractLauncherUiTest; +import com.android.launcher3.ui.TaplTestsLauncher3; +import com.android.launcher3.util.rule.ShellCommandRule; +import com.android.launcher3.views.DoubleShadowBubbleTextView; +import com.android.launcher3.widget.LauncherAppWidgetHostView; + +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.Map; +import java.util.concurrent.ExecutionException; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class ReorderWidgets extends AbstractLauncherUiTest { + + @Rule + public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind(); + + private static final String TAG = ReorderWidgets.class.getSimpleName(); + + TestWorkspaceBuilder mWorkspaceBuilder; + + private View getViewAt(int cellX, int cellY) { + return getFromLauncher(l -> l.getWorkspace().getScreenWithId( + l.getWorkspace().getScreenIdForPageIndex(0)).getChildAt(cellX, cellY)); + } + + 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()); + }); + } + + @Before + public void setup() throws Throwable { + mWorkspaceBuilder = new TestWorkspaceBuilder(this, mTargetContext); + TaplTestsLauncher3.initialize(this); + clearHomescreen(); + } + + /** + * Validate if the given board represent the current CellLayout + **/ + private boolean validateBoard(CellLayoutBoard board) { + boolean match = true; + Point cellDimensions = getCellDimensions(); + for (CellLayoutBoard.WidgetRect widgetRect : board.getWidgets()) { + if (widgetRect.shouldIgnore()) { + continue; + } + View widget = getViewAt(widgetRect.getCellX(), widgetRect.getCellY()); + assertTrue("The view selected at " + board + " is not a widget", + widget instanceof LauncherAppWidgetHostView); + match &= widgetRect.getSpanX() + == Math.round(widget.getWidth() / (float) cellDimensions.x); + match &= widgetRect.getSpanY() + == Math.round(widget.getHeight() / (float) cellDimensions.y); + if (!match) return match; + } + for (CellLayoutBoard.IconPoint iconPoint : board.getIcons()) { + View icon = getViewAt(iconPoint.getCoord().x, iconPoint.getCoord().y); + assertTrue("The view selected at " + iconPoint.coord + " is not an Icon", + icon instanceof DoubleShadowBubbleTextView); + } + return match; + } + + private void runTestCase(ReorderTestCase testCase) + throws ExecutionException, InterruptedException { + Point mainWidgetCellPos = testCase.mStart.getMain(); + + FavoriteItemsTransaction transaction = + new FavoriteItemsTransaction(mTargetContext, this); + mWorkspaceBuilder.buildFromBoard(testCase.mStart, transaction).commit(); + waitForLauncherCondition("Workspace didn't finish loading", l -> !l.isWorkspaceLoading()); + Widget widget = mLauncher.getWorkspace().getWidgetAtCell(mainWidgetCellPos.x, + mainWidgetCellPos.y); + assertNotNull(widget); + WidgetResizeFrame resizeFrame = widget.dragWidgetToWorkspace(testCase.moveMainTo.x, + testCase.moveMainTo.y); + resizeFrame.dismiss(); + + boolean isValid = false; + for (CellLayoutBoard board : testCase.mEnd) { + isValid |= validateBoard(board); + } + assertTrue("Non of the valid boards match with the current state", isValid); + } + + /** + * Run only the test define for the current grid size if such test exist + * + * @param testCaseMap map containing all the tests per grid size (Point) + */ + 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( + "The test " + testName + " doesn't support " + iconGridDimensions + " grid layout", + testCaseMap.containsKey(iconGridDimensions)); + runTestCase(testCaseMap.get(iconGridDimensions)); + } + + @Test + public void simpleReorder() throws ExecutionException, InterruptedException { + runTestCaseMap(SimpleReorderCase.TEST_BY_GRID_SIZE, + SimpleReorderCase.class.getSimpleName()); + } + + @Ignore + @Test + public void pushTest() throws ExecutionException, InterruptedException { + runTestCaseMap(PushReorderCase.TEST_BY_GRID_SIZE, PushReorderCase.class.getSimpleName()); + } + + @Ignore + @Test + public void fullReorder() throws ExecutionException, InterruptedException { + runTestCaseMap(FullReorderCase.TEST_BY_GRID_SIZE, FullReorderCase.class.getSimpleName()); + } + + @Test + public void moveOutReorder() throws ExecutionException, InterruptedException { + runTestCaseMap(MoveOutReorderCase.TEST_BY_GRID_SIZE, + MoveOutReorderCase.class.getSimpleName()); + } +} diff --git a/tests/src/com/android/launcher3/celllayout/TestBoardAppIcon.java b/tests/src/com/android/launcher3/celllayout/TestBoardAppIcon.java new file mode 100644 index 0000000000..04604d7735 --- /dev/null +++ b/tests/src/com/android/launcher3/celllayout/TestBoardAppIcon.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.launcher3.celllayout; + +import android.graphics.Point; + +public class TestBoardAppIcon { + public Point coord; + public char mType; + + public TestBoardAppIcon(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; + } +} diff --git a/tests/src/com/android/launcher3/celllayout/TestBoardWidget.java b/tests/src/com/android/launcher3/celllayout/TestBoardWidget.java new file mode 100644 index 0000000000..7f9aa9516c --- /dev/null +++ b/tests/src/com/android/launcher3/celllayout/TestBoardWidget.java @@ -0,0 +1,48 @@ +/* + * 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.graphics.Rect; + +public class TestBoardWidget { + public char mType; + public Rect mBounds; + + TestBoardWidget(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 == 'x'; + } +} 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 0000000000..16448afda0 --- /dev/null +++ b/tests/src/com/android/launcher3/celllayout/TestWorkspaceBuilder.java @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.celllayout; + +import static com.android.launcher3.WorkspaceLayoutManager.FIRST_SCREEN_ID; +import static com.android.launcher3.util.WidgetUtils.createWidgetInfo; + +import android.content.ComponentName; +import android.content.ContentResolver; +import android.content.Context; +import android.graphics.Rect; +import android.os.Process; +import android.os.UserHandle; +import android.util.Log; + +import androidx.test.core.app.ApplicationProvider; + +import com.android.launcher3.InvariantDeviceProfile; +import com.android.launcher3.LauncherSettings; +import com.android.launcher3.model.data.AppInfo; +import com.android.launcher3.model.data.ItemInfo; +import com.android.launcher3.model.data.LauncherAppWidgetInfo; +import com.android.launcher3.model.data.WorkspaceItemInfo; +import com.android.launcher3.ui.AbstractLauncherUiTest; +import com.android.launcher3.ui.TestViewHelpers; +import com.android.launcher3.widget.LauncherAppWidgetProviderInfo; + +public class TestWorkspaceBuilder { + + private static final String TAG = "CellLayoutBoardBuilder"; + private static final ComponentName APP_COMPONENT_NAME = new ComponentName( + "com.google.android.calculator", "com.android.calculator2.Calculator"); + + public AbstractLauncherUiTest mTest; + + private UserHandle mMyUser; + + private Context mContext; + private ContentResolver mResolver; + + public TestWorkspaceBuilder(AbstractLauncherUiTest test, Context context) { + mTest = test; + mMyUser = Process.myUserHandle(); + mContext = context; + mResolver = mContext.getContentResolver(); + } + + /** + * Fills the given rect in WidgetRect with 1x1 widgets. This is useful to equalize cases. + */ + private FavoriteItemsTransaction fillWithWidgets(CellLayoutBoard.WidgetRect widgetRect, + FavoriteItemsTransaction transaction) { + int initX = widgetRect.getCellX(); + int initY = widgetRect.getCellY(); + for (int x = initX; x < initX + widgetRect.getSpanX(); x++) { + for (int y = initY; y < initY + widgetRect.getSpanY(); y++) { + try { + // this widgets are filling, we don't care if we can't place them + ItemInfo item = createWidgetInCell( + new CellLayoutBoard.WidgetRect(CellLayoutBoard.CellType.IGNORE, + new Rect(x, y, x, y)) + ); + transaction.addItem(item); + } catch (Exception e) { + Log.d(TAG, "Unable to place filling widget at " + x + "," + y); + } + } + } + return transaction; + } + + private int getID() { + return LauncherSettings.Settings.call( + mResolver, LauncherSettings.Settings.METHOD_NEW_ITEM_ID) + .getInt(LauncherSettings.Settings.EXTRA_VALUE); + } + + private AppInfo getApp() { + return new AppInfo(APP_COMPONENT_NAME, "test icon", mMyUser, + AppInfo.makeLaunchIntent(APP_COMPONENT_NAME)); + } + + private void addCorrespondingWidgetRect(CellLayoutBoard.WidgetRect widgetRect, + FavoriteItemsTransaction transaction) { + if (widgetRect.mType == 'x') { + fillWithWidgets(widgetRect, transaction); + } else { + transaction.addItem(createWidgetInCell(widgetRect)); + } + } + + /** + * Builds the given board into the transaction + */ + public FavoriteItemsTransaction buildFromBoard(CellLayoutBoard board, + FavoriteItemsTransaction transaction) { + board.getWidgets().forEach( + (widgetRect) -> addCorrespondingWidgetRect(widgetRect, transaction)); + board.getIcons().forEach((iconPoint) -> + transaction.addItem(createIconInCell(iconPoint)) + ); + return transaction; + } + + /** + * Fills the hotseat row with apps instead of suggestions, for this to work the workspace should + * be clean otherwise this doesn't overrides the existing icons. + */ + public FavoriteItemsTransaction fillHotseatIcons(FavoriteItemsTransaction transaction) { + int hotseatCount = InvariantDeviceProfile.INSTANCE.get(mContext).numShownHotseatIcons; + for (int i = 0; i < hotseatCount; i++) { + transaction.addItem(getHotseatValues(i)); + } + return transaction; + } + + private ItemInfo createWidgetInCell(CellLayoutBoard.WidgetRect widgetRect) { + LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(mTest, false); + LauncherAppWidgetInfo item = createWidgetInfo(info, + ApplicationProvider.getApplicationContext(), true); + item.id = getID(); + item.cellX = widgetRect.getCellX(); + item.cellY = widgetRect.getCellY(); + item.spanX = widgetRect.getSpanX(); + item.spanY = widgetRect.getSpanY(); + item.screenId = FIRST_SCREEN_ID; + return item; + } + + private ItemInfo createIconInCell(CellLayoutBoard.IconPoint iconPoint) { + WorkspaceItemInfo item = new WorkspaceItemInfo(getApp()); + item.id = getID(); + item.screenId = FIRST_SCREEN_ID; + item.cellX = iconPoint.getCoord().x; + item.cellY = iconPoint.getCoord().y; + item.minSpanY = item.minSpanX = item.spanX = item.spanY = 1; + item.container = LauncherSettings.Favorites.CONTAINER_DESKTOP; + return item; + } + + private ItemInfo getHotseatValues(int x) { + WorkspaceItemInfo item = new WorkspaceItemInfo(getApp()); + item.id = getID(); + item.cellX = x; + item.cellY = 0; + item.minSpanY = item.minSpanX = item.spanX = item.spanY = 1; + item.rank = x; + item.screenId = x; + item.container = LauncherSettings.Favorites.CONTAINER_HOTSEAT; + return item; + } +} diff --git a/tests/src/com/android/launcher3/celllayout/testcases/FullReorderCase.java b/tests/src/com/android/launcher3/celllayout/testcases/FullReorderCase.java new file mode 100644 index 0000000000..d68d2ede61 --- /dev/null +++ b/tests/src/com/android/launcher3/celllayout/testcases/FullReorderCase.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.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 FullReorderCase { + + /** 5x5 Test + **/ + private static final String START_BOARD_STR_5x5 = "" + + "xxxxx\n" + + "222mm\n" + + "222mm\n" + + "ii111\n" + + "ii111"; + private static final Point MOVE_TO_5x5 = new Point(0, 4); + private static final String END_BOARD_STR_5x5 = "" + + "xxxxx\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" + + "ii1111\n" + + "ii1111"; + private static final Point MOVE_TO_6x5 = new Point(0, 4); + private static final String END_BOARD_STR_6x5 = "" + + "xxxxxx\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, + 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 new file mode 100644 index 0000000000..047d3422df --- /dev/null +++ b/tests/src/com/android/launcher3/celllayout/testcases/MoveOutReorderCase.java @@ -0,0 +1,69 @@ +/* + * 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 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); +} diff --git a/tests/src/com/android/launcher3/celllayout/testcases/PushReorderCase.java b/tests/src/com/android/launcher3/celllayout/testcases/PushReorderCase.java new file mode 100644 index 0000000000..38c9aee0fd --- /dev/null +++ b/tests/src/com/android/launcher3/celllayout/testcases/PushReorderCase.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.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 PushReorderCase { + + /** 5x5 Test + **/ + private static final String START_BOARD_STR_5x5 = "" + + "xxxxx\n" + + "222m-\n" + + "--111\n" + + "--333\n" + + "-----"; + 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 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" + + "2222m-\n" + + "--111-\n" + + "--333-\n" + + "------"; + 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 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); +} diff --git a/tests/src/com/android/launcher3/celllayout/testcases/ReorderTestCase.java b/tests/src/com/android/launcher3/celllayout/testcases/ReorderTestCase.java new file mode 100644 index 0000000000..0a28668d96 --- /dev/null +++ b/tests/src/com/android/launcher3/celllayout/testcases/ReorderTestCase.java @@ -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.celllayout.testcases; + +import android.graphics.Point; + +import com.android.launcher3.celllayout.CellLayoutBoard; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public class ReorderTestCase { + public CellLayoutBoard mStart; + public Point moveMainTo; + public List<CellLayoutBoard> mEnd; + + ReorderTestCase(CellLayoutBoard start, Point moveMainTo, CellLayoutBoard ... end) { + mStart = start; + this.moveMainTo = moveMainTo; + mEnd = Arrays.asList(end); + } + + ReorderTestCase(String start, Point moveMainTo, String ... end) { + mStart = CellLayoutBoard.boardFromString(start); + this.moveMainTo = moveMainTo; + mEnd = Arrays + .asList(end) + .stream() + .map(CellLayoutBoard::boardFromString) + .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 new file mode 100644 index 0000000000..546c48bccb --- /dev/null +++ b/tests/src/com/android/launcher3/celllayout/testcases/SimpleReorderCase.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.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 SimpleReorderCase { + + /** 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(4, 4); + 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(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); + + 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); +} diff --git a/tests/src/com/android/launcher3/deviceemulator/DisplayEmulator.java b/tests/src/com/android/launcher3/deviceemulator/DisplayEmulator.java index 31468c5336..e2ed65fa5b 100644 --- a/tests/src/com/android/launcher3/deviceemulator/DisplayEmulator.java +++ b/tests/src/com/android/launcher3/deviceemulator/DisplayEmulator.java @@ -47,8 +47,7 @@ public class DisplayEmulator { * By changing the WindowManagerProxy we can override the window insets information **/ private IWindowManager changeWindowManagerInstance(DeviceEmulationData deviceData) { - WindowManagerProxy.INSTANCE.initializeForTesting( - new TestWindowManagerProxy(mContext, deviceData)); + WindowManagerProxy.INSTANCE.initializeForTesting(new TestWindowManagerProxy(deviceData)); return WindowManagerGlobal.getWindowManagerService(); } @@ -57,8 +56,7 @@ public class DisplayEmulator { WindowManagerProxy original = WindowManagerProxy.INSTANCE.get(mContext); // Set up emulation final int userId = UserHandle.myUserId(); - WindowManagerProxy.INSTANCE.initializeForTesting( - new TestWindowManagerProxy(mContext, device)); + WindowManagerProxy.INSTANCE.initializeForTesting(new TestWindowManagerProxy(device)); IWindowManager wm = changeWindowManagerInstance(device); // Change density twice to force display controller to reset its state wm.setForcedDisplayDensityForUser(Display.DEFAULT_DISPLAY, device.density / 2, userId); diff --git a/tests/src/com/android/launcher3/deviceemulator/TestWindowManagerProxy.java b/tests/src/com/android/launcher3/deviceemulator/TestWindowManagerProxy.java index cbea688a9a..2d6bbccb68 100644 --- a/tests/src/com/android/launcher3/deviceemulator/TestWindowManagerProxy.java +++ b/tests/src/com/android/launcher3/deviceemulator/TestWindowManagerProxy.java @@ -19,7 +19,6 @@ import android.content.Context; import android.content.res.Resources; import android.graphics.Point; import android.graphics.Rect; -import android.view.Display; import android.view.WindowInsets; import com.android.launcher3.deviceemulator.models.DeviceEmulationData; @@ -32,17 +31,12 @@ public class TestWindowManagerProxy extends WindowManagerProxy { private final DeviceEmulationData mDevice; - public TestWindowManagerProxy(Context context, DeviceEmulationData device) { + public TestWindowManagerProxy(DeviceEmulationData device) { super(true); mDevice = device; } @Override - public boolean isInternalDisplay(Display display) { - return display.getDisplayId() == Display.DEFAULT_DISPLAY; - } - - @Override protected int getDimenByName(Resources res, String resName) { Integer mock = mDevice.resourceOverrides.get(resName); return mock != null ? mock : super.getDimenByName(res, resName); @@ -54,27 +48,25 @@ public class TestWindowManagerProxy extends WindowManagerProxy { } @Override - public CachedDisplayInfo getDisplayInfo(Context context, Display display) { - int rotation = display.getRotation(); + public CachedDisplayInfo getDisplayInfo(Context displayInfoContext) { + int rotation = getRotation(displayInfoContext); Point size = new Point(mDevice.width, mDevice.height); RotationUtils.rotateSize(size, rotation); Rect cutout = new Rect(mDevice.cutout); RotationUtils.rotateRect(cutout, rotation); - return new CachedDisplayInfo(getDisplayId(display), size, rotation, cutout); + return new CachedDisplayInfo(size, rotation, cutout); } @Override - public WindowBounds getRealBounds(Context windowContext, Display display, - CachedDisplayInfo info) { - return estimateInternalDisplayBounds(windowContext) - .get(getDisplayId(display)).second[display.getRotation()]; + public WindowBounds getRealBounds(Context displayInfoContext, CachedDisplayInfo info) { + return estimateInternalDisplayBounds(displayInfoContext).get( + getDisplayInfo(displayInfoContext))[getDisplay(displayInfoContext).getRotation()]; } @Override public WindowInsets normalizeWindowInsets(Context context, WindowInsets oldInsets, Rect outInsets) { - outInsets.set(getRealBounds(context, context.getDisplay(), - getDisplayInfo(context, context.getDisplay())).insets); + outInsets.set(getRealBounds(context, getDisplayInfo(context)).insets); return oldInsets; } } diff --git a/tests/src/com/android/launcher3/deviceemulator/models/DeviceEmulationData.java b/tests/src/com/android/launcher3/deviceemulator/models/DeviceEmulationData.java index 8d275cc04b..55b7bf199b 100644 --- a/tests/src/com/android/launcher3/deviceemulator/models/DeviceEmulationData.java +++ b/tests/src/com/android/launcher3/deviceemulator/models/DeviceEmulationData.java @@ -17,10 +17,10 @@ package com.android.launcher3.deviceemulator.models; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; -import static com.android.launcher3.ResourceUtils.NAVBAR_HEIGHT; -import static com.android.launcher3.ResourceUtils.NAVBAR_HEIGHT_LANDSCAPE; -import static com.android.launcher3.ResourceUtils.NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE; -import static com.android.launcher3.ResourceUtils.getDimenByName; +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.getDimenByName; import android.content.Context; import android.content.res.Resources; diff --git a/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java b/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java index dba0a4063f..f55b244c24 100644 --- a/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java +++ b/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java @@ -57,23 +57,26 @@ public class CacheDataUpdatedTaskTest { 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/GridSizeMigrationUtilTest.kt index 90d7b434df..76a186bd83 100644 --- a/tests/src/com/android/launcher3/model/GridSizeMigrationTaskV2Test.kt +++ b/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt @@ -17,6 +17,7 @@ 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 @@ -26,7 +27,7 @@ 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.model.GridSizeMigrationUtil.DbReader import com.android.launcher3.pm.UserCache import com.android.launcher3.provider.LauncherDbUtils import com.android.launcher3.util.LauncherModelHelper @@ -37,10 +38,10 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -/** Unit tests for [GridSizeMigrationTaskV2] */ +/** Unit tests for [GridSizeMigrationUtil] */ @SmallTest @RunWith(AndroidJUnit4::class) -class GridSizeMigrationTaskV2Test { +class GridSizeMigrationUtilTest { private lateinit var modelHelper: LauncherModelHelper private lateinit var context: Context private lateinit var db: SQLiteDatabase @@ -122,15 +123,16 @@ class GridSizeMigrationTaskV2Test { 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) + GridSizeMigrationUtil.migrate( + context, + db, + srcReader, + destReader, + idp.numDatabaseHotseatIcons, + Point(idp.numColumns, idp.numRows), + DeviceGridState(context), + DeviceGridState(idp) ) - task.migrate(DeviceGridState(context), DeviceGridState(idp)) // Check hotseat items var c = context.contentResolver.query( @@ -182,15 +184,232 @@ class GridSizeMigrationTaskV2Test { // Expected dest grid icons // _ _ _ _ // 5 6 7 8 - // 9 _ 10_ + // 9 _ _ _ // _ _ _ _ - assertThat(locMap.size.toLong()).isEqualTo(6) + 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)) - assertThat(locMap[testPackage10]).isEqualTo(Point(2, 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 @@ -207,15 +426,16 @@ class GridSizeMigrationTaskV2Test { 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) + GridSizeMigrationUtil.migrate( + context, + db, + srcReader, + destReader, + idp.numDatabaseHotseatIcons, + Point(idp.numColumns, idp.numRows), + DeviceGridState(context), + DeviceGridState(idp) ) - task.migrate(DeviceGridState(context), DeviceGridState(idp)) // Check hotseat items val c = context.contentResolver.query( @@ -262,15 +482,16 @@ class GridSizeMigrationTaskV2Test { 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) + GridSizeMigrationUtil.migrate( + context, + db, + srcReader, + destReader, + idp.numDatabaseHotseatIcons, + Point(idp.numColumns, idp.numRows), + DeviceGridState(context), + DeviceGridState(idp) ) - task.migrate(DeviceGridState(context), DeviceGridState(idp)) // Check hotseat items val c = context.contentResolver.query( @@ -327,15 +548,16 @@ class GridSizeMigrationTaskV2Test { 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) + GridSizeMigrationUtil.migrate( + context, + db, + srcReader, + destReader, + idp.numDatabaseHotseatIcons, + Point(idp.numColumns, idp.numRows), + DeviceGridState(context), + DeviceGridState(idp) ) - task.migrate(DeviceGridState(context), DeviceGridState(idp)) // Get workspace items val c = context.contentResolver.query( @@ -387,15 +609,16 @@ class GridSizeMigrationTaskV2Test { 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) + GridSizeMigrationUtil.migrate( + context, + db, + srcReader, + destReader, + idp.numDatabaseHotseatIcons, + Point(idp.numColumns, idp.numRows), + DeviceGridState(context), + DeviceGridState(idp) ) - task.migrate(DeviceGridState(context), DeviceGridState(idp)) // Get workspace items val c = context.contentResolver.query( @@ -448,15 +671,16 @@ class GridSizeMigrationTaskV2Test { 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) + GridSizeMigrationUtil.migrate( + context, + db, + srcReader, + destReader, + idp.numDatabaseHotseatIcons, + Point(idp.numColumns, idp.numRows), + DeviceGridState(context), + DeviceGridState(idp) ) - task.migrate(DeviceGridState(context), DeviceGridState(idp)) // Get workspace items val c = context.contentResolver.query( 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 0000000000..c99ad7604a --- /dev/null +++ b/tests/src/com/android/launcher3/nonquickstep/HotseatWidthCalculationTest.kt @@ -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.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 413f404406..3b53255550 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.filters.SmallTest; 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 @@ import org.junit.runner.RunWith; @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 @@ public class StringMatcherUtilityTest { 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/SDLauncherTest.java b/tests/src/com/android/launcher3/secondarydisplay/SDLauncherTest.java deleted file mode 100644 index fd86cf1144..0000000000 --- a/tests/src/com/android/launcher3/secondarydisplay/SDLauncherTest.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.launcher3.secondarydisplay; - -import static androidx.test.core.app.ActivityScenario.launch; - -import androidx.test.core.app.ActivityScenario; -import androidx.test.espresso.intent.Intents; -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.filters.MediumTest; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -/** - * Tests for {@link SecondaryDisplayLauncher} - */ -@MediumTest -@RunWith(AndroidJUnit4.class) -public class SDLauncherTest { - - @Before - public void setUp() { - Intents.init(); - } - - @After - public void tearDown() { - Intents.release(); - } - - @Test - public void testAllAppsListOpens() { - ActivityScenario<SecondaryDisplayLauncher> launcher = - launch(SecondaryDisplayLauncher.class); - launcher.onActivity(l -> l.showAppDrawer(true)); - } -} diff --git a/tests/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncherTest.java b/tests/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncherTest.java new file mode 100644 index 0000000000..082e243694 --- /dev/null +++ b/tests/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncherTest.java @@ -0,0 +1,287 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.secondarydisplay; + +import static android.content.Context.MODE_PRIVATE; +import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; +import static android.view.MotionEvent.ACTION_DOWN; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.Context; +import android.content.Intent; +import android.graphics.Point; +import android.os.SystemClock; +import android.view.MotionEvent; +import android.widget.TextView; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.LargeTest; +import androidx.test.uiautomator.By; +import androidx.test.uiautomator.UiObject2; +import androidx.test.uiautomator.Until; + +import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.tapl.LauncherInstrumentation; +import com.android.launcher3.ui.AbstractLauncherUiTest; +import com.android.launcher3.util.LauncherModelHelper; + +import org.junit.After; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Tests for {@link SecondaryDisplayLauncher}. + * TODO (b/242776943): Remove anti-patterns & migrate prediction row tests to Quickstep directory + */ +@LargeTest +@RunWith(AndroidJUnit4.class) +public final class SecondaryDisplayLauncherTest extends AbstractLauncherUiTest { + private static final int WAIT_TIME_MS = 5000; + private static final int LONG_PRESS_DURATION_MS = 1000; + private static final int DRAG_TIME_MS = 160; + + private static final String PINNED_APPS_KEY = "pinned_apps"; + + // Variables required to coordinate drag steps. + private Point mStartPoint; + private Point mEndPoint; + private long mDownTime; + + @Override + public void setUp() throws Exception { + super.setUp(); + setDragNDropFlag(true); + } + + @After + public void tearDown() { + mTargetContext.getSharedPreferences(PINNED_APPS_KEY, MODE_PRIVATE) + .edit().clear().commit(); + } + + @Test + @Ignore + public void initializeSecondaryDisplayLauncher_allAppsButtonVisible() { + assertThat(findObjectByResourceName("all_apps_button")).isNotNull(); + } + + @Test + @Ignore + public void allAppsButtonTap_opensAppDrawer() { + openAppDrawer(); + assertThat(findObjectByResourceName("search_container_all_apps")).isNotNull(); + } + + @Test + @Ignore("Launcher3 without quickstep doesn't have a predictions row.") + public void appDrawerOpened_predictionRowAppDividerVisible() { + openAppDrawer(); + assertThat(findObjectByResourceName("apps_divider_view")).isNotNull(); + } + + @Test + @Ignore + public void dragNDropDisabled_pinIconAddsToWorkspace() { + setDragNDropFlag(false); + openAppDrawer(); + UiObject2 app = findDescendantByResourceName( + findObjectByResourceName("apps_list_view"), "icon"); + app.click(LONG_PRESS_DURATION_MS); + UiObject2 popupContainer = findObjectByResourceName("popup_container"); + assertThat(popupContainer).isNotNull(); + UiObject2 pinIcon = findDescendantByTextOrDesc(popupContainer, "Add to home screen"); + assertThat(pinIcon).isNotNull(); + pinIcon.click(); + String appName = app.getContentDescription(); + assertThat(findAppInWorkspace(appName)).isNotNull(); + } + + @Test + @Ignore + public void pressBackFromAllApps_popupMenuOpen_returnsToWorkspace() { + openAppDrawer(); + assertThat(findObjectByResourceName("search_container_all_apps")).isNotNull(); + + findDescendantByResourceName(findObjectByResourceName("apps_list_view"), "icon") + .click(LONG_PRESS_DURATION_MS); + assertThat(findObjectByResourceName("popup_container")).isNotNull(); + + // First back press should close only popup menu. + mDevice.pressBack(); + assertThat(findObjectByResourceName("search_container_all_apps")).isNotNull(); + assertThat(findObjectByResourceName("popup_container")).isNull(); + + // Second back press should close app drawer. + mDevice.pressBack(); + assertThat(findObjectByResourceName("popup_container")).isNull(); + assertThat(findObjectByResourceName("search_container_all_apps")).isNull(); + } + + @Test + @Ignore("Launcher3 without quickstep doesn't have a predictions row.") + public void dragNDropFromPredictionsRow_pinToGrid() { + openAppDrawer(); + assertThat(findObjectByResourceName("prediction_row")).isNotNull(); + String appName = startDragFromPredictionRow(); + moveAppToCenterOfScreen(); + dropApp(); + + // Ensure app was added. + assertThat(findAppInWorkspace(appName)).isNotNull(); + } + + @Test + @Ignore + public void dragNDropFromAppDrawer_pinToGrid() { + openAppDrawer(); + String draggedAppName = startDragFromAllApps(); + moveAppToCenterOfScreen(); + dropApp(); + + // Ensure app was added. + assertThat(findAppInWorkspace(draggedAppName)).isNotNull(); + } + + @Test + @Ignore + public void tapRemoveButton_unpinApp() { + openAppDrawer(); + String draggedAppName = startDragFromAllApps(); + moveAppToCenterOfScreen(); + dropApp(); + removeAppByName(draggedAppName); + assertThat(findAppInWorkspace(draggedAppName)).isNull(); + } + + private void openAppDrawer() { + UiObject2 allAppsButton = findObjectByResourceName("all_apps_button"); + assertThat(allAppsButton).isNotNull(); + allAppsButton.click(); + } + + private String startDragFromAllApps() { + // Find app from app drawer. + UiObject2 allApps = findObjectByResourceName("apps_list_view"); + assertThat(allApps).isNotNull(); + UiObject2 icon = findDescendantByResourceName(allApps, "icon"); + assertThat(icon).isNotNull(); + String appName = icon.getContentDescription(); + + // Start drag action. + mDownTime = SystemClock.uptimeMillis(); + mStartPoint = icon.getVisibleCenter(); + mEndPoint = new Point(mStartPoint.x, mStartPoint.y); + mLauncher.sendPointer(mDownTime, mDownTime, ACTION_DOWN, mStartPoint, + LauncherInstrumentation.GestureScope.INSIDE); + assertThat(findObjectByResourceName("popup_container")).isNotNull(); + return appName; + } + + private String startDragFromPredictionRow() { + // Find app from predictions. + UiObject2 predictionRow = findObjectByResourceName("prediction_row"); + assertThat(predictionRow).isNotNull(); + + UiObject2 icon = findDescendantByResourceName(predictionRow, "icon"); + assertThat(icon).isNotNull(); + + String appName = icon.getContentDescription(); + UiObject2 app = findDescendantByAppName(predictionRow, appName); + assertThat(app).isNotNull(); + + // Start drag action. + mDownTime = SystemClock.uptimeMillis(); + mStartPoint = icon.getVisibleCenter(); + mEndPoint = new Point(mStartPoint.x, mStartPoint.y); + mLauncher.sendPointer(mDownTime, mDownTime, ACTION_DOWN, mStartPoint, + LauncherInstrumentation.GestureScope.INSIDE); + assertThat(findObjectByResourceName("popup_container")).isNotNull(); + return appName; + } + + private void moveAppToCenterOfScreen() { + mEndPoint.set(mDevice.getDisplayWidth() / 2, mDevice.getDisplayHeight() / 2); + mLauncher.movePointer(mDownTime, SystemClock.uptimeMillis(), DRAG_TIME_MS, true, + mStartPoint, mEndPoint, LauncherInstrumentation.GestureScope.INSIDE); + } + + private void dropApp() { + mLauncher.sendPointer(mDownTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, + mEndPoint, LauncherInstrumentation.GestureScope.INSIDE); + } + + private void removeAppByName(String appName) { + // Find app within home screen. + UiObject2 app = findDescendantByAppName(findObjectByResourceName("workspace_grid"), + appName); + if (app == null) return; + + // Open app's popup container. + app.click(LONG_PRESS_DURATION_MS); + UiObject2 popupContainer = findObjectByResourceName("popup_container"); + assertThat(popupContainer).isNotNull(); + + // Grab & click remove button. + UiObject2 removeButton = findDescendantByTextOrDesc(popupContainer, "Remove"); + assertThat(removeButton).isNotNull(); + removeButton.click(); + } + + private UiObject2 findAppInWorkspace(String appName) { + UiObject2 workspace = findObjectByResourceName("workspace_grid"); + return findDescendantByAppName(workspace, appName); + } + + private UiObject2 findObjectByResourceName(String resourceName) { + return mDevice.wait(Until.findObject(By.res(mTargetPackage, resourceName)), WAIT_TIME_MS); + } + + private UiObject2 findDescendantByResourceName(UiObject2 outerObject, + String resourceName) { + assertThat(outerObject).isNotNull(); + return outerObject.findObject(By.res(mTargetPackage, resourceName)); + } + + private UiObject2 findDescendantByAppName(UiObject2 outerObject, String appName) { + assertThat(outerObject).isNotNull(); + return outerObject.findObject(By.clazz(TextView.class).text(appName) + .pkg(mDevice.getLauncherPackageName())); + } + + private UiObject2 findDescendantByTextOrDesc(UiObject2 outerObject, String content) { + assertThat(outerObject).isNotNull(); + UiObject2 innerObject = outerObject.findObject(By.desc(content)); + if (innerObject == null) innerObject = outerObject.findObject(By.text(content)); + return innerObject; + } + + private void startSecondaryDisplayActivity() { + mTargetContext.startActivity(( + new Intent(mTargetContext, SecondaryDisplayLauncher.class).addFlags( + FLAG_ACTIVITY_NEW_TASK))); + } + + private void setDragNDropFlag(Boolean status) { + Context context = new LauncherModelHelper().sandboxContext; + context.getSharedPreferences(FeatureFlags.FLAGS_PREF_NAME, Context.MODE_PRIVATE).edit() + .putBoolean(FeatureFlags.SECONDARY_DRAG_N_DROP_TO_PIN.key, status) + .commit(); + FeatureFlags.initialize(context); + startSecondaryDisplayActivity(); + } +} diff --git a/tests/src/com/android/launcher3/testcomponent/AppWidgetWithDialog.java b/tests/src/com/android/launcher3/testcomponent/AppWidgetWithDialog.java new file mode 100644 index 0000000000..6d617fa240 --- /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 9c6d10232e..d3ce67c81c 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.graphics.Color; 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 @@ public class BaseTestingActivity extends Activity implements View.OnClickListene mView.addView(button, lp); } + protected void addEditor(String initText, String hint, boolean requestIme) { + EditText editText = new EditText(this); + editText.setHint(hint); + editText.setText(initText); + + LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); + lp.bottomMargin = mMargin; + mView.addView(editText, lp); + if (requestIme) { + editText.requestFocus(); + mView.getWindowInsetsController().show(WindowInsets.Type.ime()); + } + } + @Override protected void onResume() { super.onResume(); diff --git a/tests/src/com/android/launcher3/testcomponent/DialogTestActivity.java b/tests/src/com/android/launcher3/testcomponent/DialogTestActivity.java new file mode 100644 index 0000000000..9e5a2740f7 --- /dev/null +++ b/tests/src/com/android/launcher3/testcomponent/DialogTestActivity.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.testcomponent; + + +/** + * Extension of BaseTestingActivity with a Dialog theme + */ +public class DialogTestActivity extends BaseTestingActivity {} diff --git a/tests/src/com/android/launcher3/testcomponent/ImeTestActivity.java b/tests/src/com/android/launcher3/testcomponent/ImeTestActivity.java new file mode 100644 index 0000000000..43952d5508 --- /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 6f8b9d2967..70d122bc90 100644 --- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java +++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java @@ -61,7 +61,7 @@ import com.android.launcher3.tapl.LauncherInstrumentation; import com.android.launcher3.tapl.LauncherInstrumentation.ContainerType; import com.android.launcher3.tapl.TestHelpers; import com.android.launcher3.testcomponent.TestCommandReceiver; -import com.android.launcher3.testing.TestProtocol; +import com.android.launcher3.testing.shared.TestProtocol; import com.android.launcher3.util.LooperExecutor; import com.android.launcher3.util.PackageManagerHelper; import com.android.launcher3.util.Wait; @@ -151,6 +151,8 @@ public abstract class AbstractLauncherUiTest { device.executeShellCommand( "am dumpheap " + device.getLauncherPackageName() + " " + fileName); } + Log.d(TAG, "Saved leak dump, the leak is still present: " + + !launcher.noLeakedActivities()); sDumpWasGenerated = true; result = "saved memory dump as an artifact"; } catch (Throwable e) { @@ -319,7 +321,7 @@ public abstract class AbstractLauncherUiTest { /** * Adds {@param item} on the homescreen on the 0th screen */ - protected void addItemToScreen(ItemInfo item) { + public void addItemToScreen(ItemInfo item) { WidgetUtils.addItemToScreen(item, mTargetContext); resetLoaderState(); @@ -477,6 +479,16 @@ public abstract class AbstractLauncherUiTest { false /* newTask */); } + public static void startImeTestActivity() { + final String packageName = getAppPackageName(); + final Intent intent = getInstrumentation().getContext().getPackageManager(). + getLaunchIntentForPackage(packageName); + intent.setComponent(new ComponentName(packageName, + "com.android.launcher3.testcomponent.ImeTestActivity")); + startIntent(intent, By.pkg(packageName).text("ImeTestActivity"), + false /* newTask */); + } + private static void startIntent(Intent intent, BySelector selector, boolean newTask) { intent.addCategory(Intent.CATEGORY_LAUNCHER); if (newTask) { @@ -525,7 +537,7 @@ public abstract class AbstractLauncherUiTest { } protected int getAllAppsScroll(Launcher launcher) { - return launcher.getAppsView().getActiveRecyclerView().getCurrentScrollY(); + return launcher.getAppsView().getActiveRecyclerView().computeVerticalScrollOffset(); } private void checkLauncherIntegrity( @@ -563,10 +575,13 @@ public abstract class AbstractLauncherUiTest { break; } case OVERVIEW: { - checkLauncherStateInOverview(launcher, expectedContainerType, isStarted, - isResumed); - assertTrue(TestProtocol.stateOrdinalToString(ordinal), - ordinal == TestProtocol.OVERVIEW_STATE_ORDINAL); + verifyOverviewState(launcher, expectedContainerType, isStarted, isResumed, + ordinal, TestProtocol.OVERVIEW_STATE_ORDINAL); + break; + } + case SPLIT_SCREEN_SELECT: { + verifyOverviewState(launcher, expectedContainerType, isStarted, isResumed, + ordinal, TestProtocol.OVERVIEW_SPLIT_SELECT_ORDINAL); break; } case TASKBAR_ALL_APPS: @@ -632,5 +647,9 @@ public abstract class AbstractLauncherUiTest { return homeAppIcon; } - + private void verifyOverviewState(Launcher launcher, ContainerType expectedContainerType, + boolean isStarted, boolean isResumed, int ordinal, int expectedOrdinal) { + checkLauncherStateInOverview(launcher, expectedContainerType, isStarted, isResumed); + assertEquals(TestProtocol.stateOrdinalToString(ordinal), ordinal, expectedOrdinal); + } } diff --git a/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java b/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java index 8f2d5283ee..266f0aeb1d 100644 --- a/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java +++ b/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java @@ -4,7 +4,6 @@ import android.util.Log; import android.view.Surface; import com.android.launcher3.tapl.TestHelpers; -import com.android.launcher3.testing.TestProtocol; import org.junit.rules.TestRule; import org.junit.runner.Description; diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java index 8a97c6ba5b..e1a2c1bf4d 100644 --- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java +++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java @@ -25,9 +25,11 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeTrue; import android.content.Intent; import android.graphics.Point; +import android.platform.test.annotations.IwTest; import androidx.test.filters.LargeTest; import androidx.test.runner.AndroidJUnit4; @@ -53,7 +55,7 @@ import com.android.launcher3.widget.picker.WidgetsFullSheet; import com.android.launcher3.widget.picker.WidgetsRecyclerView; import org.junit.Before; -import org.junit.Rule; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; @@ -192,6 +194,27 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { } @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()); + + mLauncher.getWorkspace().switchToAllApps().dismissByTappingOutsideForTablet( + true /* tapRight */); + mLauncher.getWorkspace().switchToAllApps().dismissByTappingOutsideForTablet( + false /* tapRight */); + } + + @IwTest(focusArea="launcher") + @Test @ScreenRecord // b/202433017 public void testWorkspace() throws Exception { final Workspace workspace = mLauncher.getWorkspace(); @@ -199,9 +222,9 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { // Test that ensureWorkspaceIsScrollable adds a page by dragging an icon there. executeOnLauncher(launcher -> assertFalse("Initial workspace state is scrollable", isWorkspaceScrollable(launcher))); - assertNull("Chrome app was found on empty workspace", - workspace.tryGetWorkspaceAppIcon("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( @@ -288,7 +311,7 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { } private int getWidgetsScroll(Launcher launcher) { - return getWidgetsView(launcher).getCurrentScrollY(); + return getWidgetsView(launcher).computeVerticalScrollOffset(); } private boolean isOptionsPopupVisible(Launcher launcher) { @@ -318,8 +341,10 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { } } + @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. @@ -376,6 +401,8 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { @Test @PortraitLandscape + @ScreenRecord + @Ignore // b/233075289 public void testDragToFolder() { // TODO: add the use case to drag an icon to an existing folder. Currently it either fails // on tablets or phones due to difference in resolution. @@ -388,10 +415,10 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { folder.getAppIcon(GMAIL_APP_NAME); Workspace workspace = folder.close(); - assertNull(STORE_APP_NAME + " should be moved to a folder.", - workspace.tryGetWorkspaceAppIcon(STORE_APP_NAME)); - assertNull(GMAIL_APP_NAME + " should be moved to a folder.", - workspace.tryGetWorkspaceAppIcon(GMAIL_APP_NAME)); + workspace.verifyWorkspaceAppIconIsGone(STORE_APP_NAME + " should be moved to a folder.", + STORE_APP_NAME); + workspace.verifyWorkspaceAppIconIsGone(GMAIL_APP_NAME + " should be moved to a folder.", + GMAIL_APP_NAME); final HomeAppIcon mapIcon = createShortcutInCenterIfNotExist(MAPS_APP_NAME); folderIcon = mapIcon.dragToIcon(folderIcon); @@ -399,8 +426,8 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { folder.getAppIcon(MAPS_APP_NAME); workspace = folder.close(); - assertNull(MAPS_APP_NAME + " should be moved to a folder.", - workspace.tryGetWorkspaceAppIcon(MAPS_APP_NAME)); + workspace.verifyWorkspaceAppIconIsGone(MAPS_APP_NAME + " should be moved to a folder.", + MAPS_APP_NAME); } @Test @@ -419,13 +446,31 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { @Test @PortraitLandscape + public void testDragAndCancelAppIcon() { + final HomeAppIcon homeAppIcon = createShortcutInCenterIfNotExist(GMAIL_APP_NAME); + Point positionBeforeDrag = + mLauncher.getWorkspace().getWorkspaceIconsPositions().get(GMAIL_APP_NAME); + assertNotNull("App not found in Workspace before dragging.", positionBeforeDrag); + + mLauncher.getWorkspace().dragAndCancelAppIcon(homeAppIcon); + + Point positionAfterDrag = + mLauncher.getWorkspace().getWorkspaceIconsPositions().get(GMAIL_APP_NAME); + assertNotNull("App not found in Workspace after dragging.", positionAfterDrag); + assertEquals("App not returned to same position in Workspace after drag & cancel", + positionBeforeDrag, positionAfterDrag); + } + + @Test + @PortraitLandscape public void testDeleteFromWorkspace() throws Exception { // test delete both built-in apps and user-installed app from workspace for (String appName : new String[]{"Gmail", "Play Store", APP_NAME}) { final HomeAppIcon homeAppIcon = createShortcutInCenterIfNotExist(appName); Workspace workspace = mLauncher.getWorkspace().deleteAppIcon(homeAppIcon); - assertNull(appName + " app was found after being deleted from workspace", - workspace.tryGetWorkspaceAppIcon(appName)); + workspace.verifyWorkspaceAppIconIsGone( + appName + " app was found after being deleted from workspace", + appName); } } @@ -443,7 +488,7 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { @Test @PortraitLandscape public void testUninstallFromWorkspace() throws Exception { - TestUtil.installDummyApp(); + installDummyAppAndWaitForUIUpdate(); try { verifyAppUninstalledFromAllApps( createShortcutInCenterIfNotExist(DUMMY_APP_NAME).uninstall(), DUMMY_APP_NAME); @@ -454,8 +499,9 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { @Test @PortraitLandscape + @ScreenRecord // (b/256659409) public void testUninstallFromAllApps() throws Exception { - TestUtil.installDummyApp(); + installDummyAppAndWaitForUIUpdate(); try { Workspace workspace = mLauncher.getWorkspace(); final HomeAllApps allApps = workspace.switchToAllApps(); @@ -500,7 +546,7 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { 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 = @@ -509,10 +555,8 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { .containsAtLeast(DUMMY_APP_NAME, MAPS_APP_NAME, STORE_APP_NAME); mLauncher.getWorkspace().getWorkspaceAppIcon(DUMMY_APP_NAME).uninstall(); - - assertNull( - DUMMY_APP_NAME + " app was found after being uninstalled", - mLauncher.getWorkspace().tryGetWorkspaceAppIcon(DUMMY_APP_NAME)); + mLauncher.getWorkspace().verifyWorkspaceAppIconIsGone( + DUMMY_APP_NAME + " was expected to disappear after uninstall.", DUMMY_APP_NAME); Map<String, Point> finalPositions = mLauncher.getWorkspace().getWorkspaceIconsPositions(); @@ -522,6 +566,48 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { } } + @Test + @PortraitLandscape + public void testDragShortcutToWorkspaceCell() throws Exception { + Point[] targets = getCornersAndCenterPositions(); + + for (Point target : targets) { + final HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps(); + allApps.freeze(); + try { + allApps.getAppIcon(APP_NAME) + .openDeepShortcutMenu() + .getMenuItem(0) + .dragToWorkspace(target.x, target.y); + } finally { + allApps.unfreeze(); + } + } + } + + @Test + @PortraitLandscape + public void testAddDeleteShortcutOnHotseat() { + mLauncher.getWorkspace() + .deleteAppIcon(mLauncher.getWorkspace().getHotseatAppIcon(0)) + .switchToAllApps() + .getAppIcon(APP_NAME) + .dragToHotseat(0); + mLauncher.getWorkspace().deleteAppIcon( + 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()} @@ -540,4 +626,16 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { 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 35b4ca6431..c7628ccd63 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 com.android.launcher3.allapps.AllAppsStore.DEFER_UPDATES_TEST; 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; @@ -30,7 +31,6 @@ import androidx.recyclerview.widget.RecyclerView.ViewHolder; import com.android.launcher3.R; import com.android.launcher3.allapps.ActivityAllAppsContainerView; import com.android.launcher3.allapps.AllAppsPagedView; -import com.android.launcher3.allapps.WorkAdapterProvider; import com.android.launcher3.allapps.WorkEduCard; import com.android.launcher3.allapps.WorkPausedCard; import com.android.launcher3.allapps.WorkProfileManager; @@ -38,6 +38,7 @@ import com.android.launcher3.tapl.LauncherInstrumentation; import org.junit.After; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import java.util.Objects; @@ -48,6 +49,8 @@ public class WorkProfileTest extends AbstractLauncherUiTest { private static final int WORK_PAGE = ActivityAllAppsContainerView.AdapterHolder.WORK; private int mProfileUserId; + private boolean mWorkProfileSetupSuccessful; + private final String TAG = "WorkProfileTest"; @Before @Override @@ -56,12 +59,17 @@ public class WorkProfileTest extends AbstractLauncherUiTest { 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); @@ -100,6 +108,8 @@ public class WorkProfileTest extends AbstractLauncherUiTest { @Test public void workTabExists() { + assumeTrue(mWorkProfileSetupSuccessful); + waitForWorkTabSetup(); waitForLauncherCondition("Personal tab is missing", launcher -> launcher.getAppsView().isPersonalTabVisible(), LauncherInstrumentation.WAIT_TIME_MS); @@ -109,9 +119,10 @@ public class WorkProfileTest extends AbstractLauncherUiTest { } @Test + @Ignore("b/243855320") public void toggleWorks() { + assumeTrue(mWorkProfileSetupSuccessful); waitForWorkTabSetup(); - executeOnLauncher(launcher -> { AllAppsPagedView pagedView = (AllAppsPagedView) launcher.getAppsView().getContentView(); pagedView.setCurrentPage(WORK_PAGE); @@ -127,7 +138,11 @@ public class WorkProfileTest extends AbstractLauncherUiTest { 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 @@ -153,9 +168,10 @@ public class WorkProfileTest extends AbstractLauncherUiTest { @Test public void testEdu() { + assumeTrue(mWorkProfileSetupSuccessful); waitForWorkTabSetup(); executeOnLauncher(l -> { - l.getSharedPrefs().edit().putInt(WorkAdapterProvider.KEY_WORK_EDU_STEP, 0).commit(); + l.getSharedPrefs().edit().putInt(WorkProfileManager.KEY_WORK_EDU_STEP, 0).commit(); ((AllAppsPagedView) l.getAppsView().getContentView()).setCurrentPage(WORK_PAGE); l.getAppsView().getWorkManager().reset(); }); @@ -175,4 +191,14 @@ public class WorkProfileTest extends AbstractLauncherUiTest { } }, LauncherInstrumentation.WAIT_TIME_MS); } + + private void updateWorkProfileSetupSuccessful(String cli, String output) { + Log.d(TAG, "updateWorkProfileSetupSuccessful, cli=" + cli + " " + "output=" + output); + if (output.startsWith("Success")) { + assertTrue(output, output.startsWith("Success")); + mWorkProfileSetupSuccessful = true; + } else { + mWorkProfileSetupSuccessful = false; + } + } } diff --git a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java index 194ee4f1c9..0fccf7960b 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 com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName; 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.ui.TestViewHelpers; 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 @@ public class AddWidgetTest extends AbstractLauncherUiTest { @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 @@ public class AddWidgetTest extends AbstractLauncherUiTest { * 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 fa39ce0604..0f861eb1f3 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 @@ public class BindWidgetTest extends AbstractLauncherUiTest { 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/workspace/ThemeIconsTest.java b/tests/src/com/android/launcher3/ui/workspace/ThemeIconsTest.java index e66810cc3d..9dae00c29c 100644 --- a/tests/src/com/android/launcher3/ui/workspace/ThemeIconsTest.java +++ b/tests/src/com/android/launcher3/ui/workspace/ThemeIconsTest.java @@ -32,8 +32,11 @@ import com.android.launcher3.BubbleTextView; 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 com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord; import org.junit.Test; @@ -48,7 +51,9 @@ import java.util.Queue; @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 +65,28 @@ public class ThemeIconsTest extends AbstractLauncherUiTest { 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 +102,42 @@ public class ThemeIconsTest extends AbstractLauncherUiTest { 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 + @ScreenRecord // b/260722220 + 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 +151,7 @@ public class ThemeIconsTest extends AbstractLauncherUiTest { } } 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 f646b504a9..6a938daf96 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.tapl.Workspace; 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 @@ public class TwoPanelWorkspaceTest extends AbstractLauncherUiTest { @Before public void setUp() throws Exception { super.setUp(); + mLauncher.useTest2WorkspaceLayoutOnReload(); TaplTestsLauncher3.initialize(this); assumeTrue(mLauncher.isTwoPanels()); @@ -63,6 +65,11 @@ public class TwoPanelWorkspaceTest extends AbstractLauncherUiTest { }); } + @After + public void tearDown() { + mLauncher.useDefaultWorkspaceLayoutOnReload(); + } + @Test @PortraitLandscape public void testDragIconToRightPanel() { @@ -339,4 +346,4 @@ public class TwoPanelWorkspaceTest extends AbstractLauncherUiTest { + 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 2618a2e1f1..191d284939 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.content.ContextWrapper; 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 @@ public class ActivityContextWrapper extends ContextThemeWrapper implements Activ } @Override + public List<OnDeviceProfileChangeListener> getOnDeviceProfileChangeListeners() { + return mDpChangeListeners; + } + + @Override public DeviceProfile getDeviceProfile() { return mProfile; } diff --git a/tests/src/com/android/launcher3/util/LauncherModelHelper.java b/tests/src/com/android/launcher3/util/LauncherModelHelper.java index 33249592c2..93bf31256d 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.provider.Settings; import android.test.mock.MockContentResolver; import android.util.ArrayMap; +import androidx.annotation.NonNull; import androidx.test.core.app.ApplicationProvider; import androidx.test.uiautomator.UiDevice; @@ -194,8 +195,9 @@ public class LauncherModelHelper { 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 +362,12 @@ public class LauncherModelHelper { 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); } diff --git a/tests/src/com/android/launcher3/util/MultiAdditivePropertyTest.kt b/tests/src/com/android/launcher3/util/MultiPropertyFactoryTest.kt index 309d055bad..bf3a092adb 100644 --- a/tests/src/com/android/launcher3/util/MultiAdditivePropertyTest.kt +++ b/tests/src/com/android/launcher3/util/MultiPropertyFactoryTest.kt @@ -16,38 +16,44 @@ package com.android.launcher3.util -import android.view.View +import android.util.FloatProperty import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.google.common.truth.Truth.assertThat import org.junit.Test import org.junit.runner.RunWith -/** Unit tests for [MultiAdditivePropertyFactory] */ +/** Unit tests for [MultiPropertyFactory] */ @SmallTest @RunWith(AndroidJUnit4::class) -class MultiAdditivePropertyTest { +class MultiPropertyFactoryTest { private val received = mutableListOf<Float>() - private val factory = - object : MultiAdditivePropertyFactory<View?>("Test", View.TRANSLATION_X) { - override fun apply(obj: View?, value: Float) { - received.add(value) - } + private val receiveProperty: FloatProperty<Any> = object : FloatProperty<Any>("receive") { + override fun setValue(obj: Any?, value: Float) { + received.add(value) } + override fun get(o: Any): Float { + return 0f + } + } + + private val factory = MultiPropertyFactory(null, receiveProperty, 3) { + x: Float, y: Float -> x + y + } - private val p1 = factory.get(1) - private val p2 = factory.get(2) - private val p3 = factory.get(3) + 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 @@ class MultiAdditivePropertyTest { 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 67f3902095..d7c6c4fda9 100644 --- a/tests/src/com/android/launcher3/util/TestUtil.java +++ b/tests/src/com/android/launcher3/util/TestUtil.java @@ -17,8 +17,13 @@ package com.android.launcher3.util; 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 org.junit.Assert; 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 @@ public class TestUtil { 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); - } - in.close(); - out.close(); + 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+", ""))); + 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); + } } 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 0000000000..d6c6e91e49 --- /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.filters.SmallTest +import com.google.common.truth.Truth.assertThat +import androidx.test.ext.junit.runners.AndroidJUnit4 +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 6fc84914f8..b0df0558dd 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 androidx.test.platform.app.InstrumentationRegistry.getInstrumentat 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 android.os.Process; import com.android.launcher3.LauncherSettings; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.LauncherAppWidgetInfo; -import com.android.launcher3.widget.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 @@ public class WidgetUtils { 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/src/com/android/launcher3/util/rule/FailureWatcher.java b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java index 4c41d7ec4f..0a0dfcbf5d 100644 --- a/tests/src/com/android/launcher3/util/rule/FailureWatcher.java +++ b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java @@ -32,7 +32,6 @@ public class FailureWatcher extends TestWatcher { public FailureWatcher(UiDevice device, LauncherInstrumentation launcher) { mDevice = device; mLauncher = launcher; - Log.d("b/196820244", "FailureWatcher.ctor", new Exception()); } @Override @@ -48,10 +47,8 @@ public class FailureWatcher extends TestWatcher { public void evaluate() throws Throwable { boolean success = false; try { - Log.d("b/196820244", "Before evaluate"); mDevice.executeShellCommand("cmd statusbar tracing start"); FailureWatcher.super.apply(base, description).evaluate(); - Log.d("b/196820244", "After evaluate"); success = true; } finally { // Save artifact for Launcher Winscope trace. @@ -96,9 +93,7 @@ public class FailureWatcher extends TestWatcher { public static void onError(LauncherInstrumentation launcher, Description description, Throwable e) { final UiDevice device = launcher.getDevice(); - Log.d("b/196820244", "onError 1"); if (device == null) return; - Log.d("b/196820244", "onError 2"); final File sceenshot = diagFile(description, "TestScreenshot", "png"); final File hierarchy = diagFile(description, "Hierarchy", "zip"); diff --git a/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java b/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java index 98eb32e818..10afe13d62 100644 --- a/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java +++ b/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java @@ -45,7 +45,8 @@ public class AddToHomeScreenPrompt { 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 bfb115d1b8..6f6428ad78 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; @@ -29,7 +32,7 @@ import androidx.test.uiautomator.Direction; import androidx.test.uiautomator.StaleObjectException; import androidx.test.uiautomator.UiObject2; -import com.android.launcher3.testing.TestProtocol; +import com.android.launcher3.testing.shared.TestProtocol; import java.util.stream.Collectors; @@ -37,6 +40,9 @@ import java.util.stream.Collectors; * 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 @@ public abstract class AllApps extends LauncherInstrumentation.VisibleContainer { 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 @@ public abstract class AllApps extends LauncherInstrumentation.VisibleContainer { 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 @@ public abstract class AllApps extends LauncherInstrumentation.VisibleContainer { 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 @@ public abstract class AllApps extends LauncherInstrumentation.VisibleContainer { 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 @@ public abstract class AllApps extends LauncherInstrumentation.VisibleContainer { 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 @@ public abstract class AllApps extends LauncherInstrumentation.VisibleContainer { 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 @@ public abstract class AllApps extends LauncherInstrumentation.VisibleContainer { .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 abstract class AllApps extends LauncherInstrumentation.VisibleContainer { */ 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 516402563d..f804e28ceb 100644 --- a/tests/tapl/com/android/launcher3/tapl/AllAppsFromTaskbar.java +++ b/tests/tapl/com/android/launcher3/tapl/AllAppsFromTaskbar.java @@ -18,6 +18,8 @@ package com.android.launcher3.tapl; 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 @@ public class AllAppsFromTaskbar extends AllApps { protected boolean hasSearchBox() { return false; } + + @Override + protected int getAppsListRecyclerTopPadding() { + return mLauncher.getTestInfo(TestProtocol.REQUEST_TASKBAR_ALL_APPS_TOP_PADDING) + .getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD); + } } diff --git a/tests/tapl/com/android/launcher3/tapl/AppIcon.java b/tests/tapl/com/android/launcher3/tapl/AppIcon.java index e28f0af1bc..2687b28bcb 100644 --- a/tests/tapl/com/android/launcher3/tapl/AppIcon.java +++ b/tests/tapl/com/android/launcher3/tapl/AppIcon.java @@ -18,11 +18,12 @@ package com.android.launcher3.tapl; import android.widget.TextView; +import androidx.annotation.NonNull; import androidx.test.uiautomator.By; import androidx.test.uiautomator.BySelector; import androidx.test.uiautomator.UiObject2; -import com.android.launcher3.testing.TestProtocol; +import com.android.launcher3.testing.shared.TestProtocol; import java.util.regex.Pattern; @@ -86,4 +87,10 @@ public abstract class AppIcon extends Launchable { 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/AppIconMenuItem.java b/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java index 5cf5abab0b..284168bd13 100644 --- a/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java +++ b/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java @@ -18,7 +18,7 @@ package com.android.launcher3.tapl; import androidx.test.uiautomator.UiObject2; -import com.android.launcher3.testing.TestProtocol; +import com.android.launcher3.testing.shared.TestProtocol; /** * Menu item in an app icon menu. diff --git a/tests/tapl/com/android/launcher3/tapl/Background.java b/tests/tapl/com/android/launcher3/tapl/Background.java index 589e13cab5..5a96d95316 100644 --- a/tests/tapl/com/android/launcher3/tapl/Background.java +++ b/tests/tapl/com/android/launcher3/tapl/Background.java @@ -19,7 +19,7 @@ package com.android.launcher3.tapl; import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED; import static com.android.launcher3.tapl.OverviewTask.TASK_START_EVENT; -import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL; +import static com.android.launcher3.testing.shared.TestProtocol.OVERVIEW_STATE_ORDINAL; import android.graphics.Point; import android.os.SystemClock; @@ -28,7 +28,7 @@ import android.view.MotionEvent; import androidx.annotation.NonNull; import androidx.test.uiautomator.UiObject2; -import com.android.launcher3.testing.TestProtocol; +import com.android.launcher3.testing.shared.TestProtocol; import java.util.List; import java.util.regex.Pattern; @@ -58,7 +58,7 @@ public abstract class Background extends LauncherInstrumentation.VisibleContaine "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 @@ public abstract class Background extends LauncherInstrumentation.VisibleContaine 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 b7bca02e44..afeb8d782b 100644 --- a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java +++ b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java @@ -19,6 +19,7 @@ package com.android.launcher3.tapl; import android.graphics.Rect; import androidx.annotation.NonNull; +import androidx.test.uiautomator.By; import androidx.test.uiautomator.BySelector; import androidx.test.uiautomator.Direction; import androidx.test.uiautomator.UiObject2; @@ -168,6 +169,27 @@ public class BaseOverview extends LauncherInstrumentation.VisibleContainer { return new OverviewTask(mLauncher, widestTask, this); } + /** Returns an overview task matching TestActivity {@param activityNumber}. */ + @NonNull + public OverviewTask getTestActivityTask(int activityNumber) { + final List<UiObject2> taskViews = getTasks(); + mLauncher.assertNotEquals("Unable to find a task", 0, taskViews.size()); + + final String activityName = "TestActivity" + activityNumber; + UiObject2 task = null; + for (UiObject2 taskView : taskViews) { + // TODO(b/239452415): Use equals instead of descEndsWith + if (taskView.getParent().hasObject(By.descEndsWith(activityName))) { + task = taskView; + break; + } + } + mLauncher.assertNotNull( + "Unable to find a task with " + activityName + " from the task list", task); + + return new OverviewTask(mLauncher, task, this); + } + /** * Returns a list of all tasks fully visible in the tablet grid overview. */ @@ -222,42 +244,44 @@ public class BaseOverview extends LauncherInstrumentation.VisibleContainer { * Returns if clear all button is visible. */ public boolean isClearAllVisible() { - return mLauncher.hasLauncherObject(mLauncher.getOverviewObjectSelector("clear_all")); + return verifyActiveContainer().hasObject( + mLauncher.getOverviewObjectSelector("clear_all")); } - private void verifyActionsViewVisibility() { - if (!hasTasks()) { - return; + 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() { 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."); } @@ -268,9 +292,9 @@ public class BaseOverview extends LauncherInstrumentation.VisibleContainer { 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/Folder.java b/tests/tapl/com/android/launcher3/tapl/Folder.java index 26f0a8b26d..1352cc07c1 100644 --- a/tests/tapl/com/android/launcher3/tapl/Folder.java +++ b/tests/tapl/com/android/launcher3/tapl/Folder.java @@ -16,11 +16,6 @@ package com.android.launcher3.tapl; -import android.graphics.Point; -import android.graphics.Rect; -import android.os.SystemClock; -import android.view.MotionEvent; - import androidx.annotation.NonNull; import androidx.test.uiautomator.UiObject2; @@ -50,25 +45,15 @@ public class Folder { } } - private void touchOutsideFolder() { - Rect containerBounds = mLauncher.getVisibleBounds(this.mContainer); - final long downTime = SystemClock.uptimeMillis(); - Point containerLeftTopCorner = new Point(containerBounds.left - 1, containerBounds.top - 1); - mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, - containerLeftTopCorner, LauncherInstrumentation.GestureScope.INSIDE); - mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_UP, - containerLeftTopCorner, LauncherInstrumentation.GestureScope.INSIDE); - } - /** - * CLose opened folder if possible. It throws assertion error if the folder is already closed. + * Close opened folder if possible. It throws assertion error if the folder is already closed. */ public Workspace close() { try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck(); LauncherInstrumentation.Closable c = mLauncher.addContextLayer( "Want to close opened folder")) { mLauncher.waitForLauncherObject(FOLDER_CONTENT_RES_ID); - touchOutsideFolder(); + mLauncher.touchOutsideContainer(this.mContainer, false /* tapRight */); mLauncher.waitUntilLauncherObjectGone(FOLDER_CONTENT_RES_ID); return mLauncher.getWorkspace(); } diff --git a/tests/tapl/com/android/launcher3/tapl/FolderIcon.java b/tests/tapl/com/android/launcher3/tapl/FolderIcon.java index 9b4717fe12..0c453bd5b5 100644 --- a/tests/tapl/com/android/launcher3/tapl/FolderIcon.java +++ b/tests/tapl/com/android/launcher3/tapl/FolderIcon.java @@ -21,7 +21,7 @@ import android.graphics.Rect; import androidx.annotation.NonNull; import androidx.test.uiautomator.UiObject2; -import com.android.launcher3.testing.TestProtocol; +import com.android.launcher3.testing.shared.TestProtocol; /** * Folder Icon, an app folder in workspace. diff --git a/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java b/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java index c275f3b320..50b03aa7df 100644 --- a/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java +++ b/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java @@ -15,15 +15,60 @@ */ 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"; HomeAllApps(LauncherInstrumentation launcher) { 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; @@ -45,4 +90,29 @@ public class HomeAllApps extends AllApps { protected boolean hasSearchBox() { 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. + */ + public Workspace dismissByTappingOutsideForTablet(boolean tapRight) { + try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck(); + LauncherInstrumentation.Closable c = mLauncher.addContextLayer( + "want to tap outside AllApps bottom sheet on the " + + (tapRight ? "right" : "left"))) { + final UiObject2 allAppsBottomSheet = + mLauncher.waitForLauncherObject(BOTTOM_SHEET_RES_ID); + mLauncher.touchOutsideContainer(allAppsBottomSheet, tapRight); + try (LauncherInstrumentation.Closable tapped = mLauncher.addContextLayer( + "tapped outside AllApps bottom sheet")) { + return mLauncher.getWorkspace(); + } + } + } } diff --git a/tests/tapl/com/android/launcher3/tapl/HomeAppIcon.java b/tests/tapl/com/android/launcher3/tapl/HomeAppIcon.java index 71d8ba9a41..693baa02c5 100644 --- a/tests/tapl/com/android/launcher3/tapl/HomeAppIcon.java +++ b/tests/tapl/com/android/launcher3/tapl/HomeAppIcon.java @@ -103,40 +103,45 @@ public abstract class HomeAppIcon extends AppIcon implements FolderDragTarget, W } /** - * Drag an object to the given cell in workspace. The target cell must be empty. + * Drag an object to the given cell in hotseat. The target cell should be expected to be empty. * - * @param cellX zero based column number, starting from the left of the screen. - * @param cellY zero based row number, starting from the top of the screen. + * @param cellInd zero based index number of the hotseat cells. + * @return the workspace app icon. */ - public HomeAppIcon dragToWorkspace(int cellX, int cellY) { + @NonNull + public WorkspaceAppIcon dragToHotseat(int cellInd) { try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck(); LauncherInstrumentation.Closable c = mLauncher.addContextLayer( - String.format("want to drag the icon to cell(%d, %d)", cellX, cellY)) + String.format("want to drag the icon to hotseat cell %d", cellInd)) ) { - final Supplier<Point> dest = () -> Workspace.getCellCenter(mLauncher, cellX, cellY); - Workspace.dragIconToWorkspace( + final Supplier<Point> dest = () -> Workspace.getHotseatCellCenter(mLauncher, cellInd); + + Workspace.dragIconToHotseat( mLauncher, - /* launchable= */ this, + this, dest, () -> addExpectedEventsForLongClick(), /*expectDropEvents= */ null); try (LauncherInstrumentation.Closable ignore = mLauncher.addContextLayer("dragged")) { WorkspaceAppIcon appIcon = - (WorkspaceAppIcon) mLauncher.getWorkspace().getWorkspaceAppIcon(mAppName); + (WorkspaceAppIcon) mLauncher.getWorkspace().getHotseatAppIcon(mAppName); mLauncher.assertTrue( - String.format( - "The %s icon should be in the cell (%d, %d).", mAppName, cellX, - cellY), - appIcon.isInCell(cellX, cellY)); + String.format("The %s icon should be in the hotseat cell %d.", mAppName, + cellInd), + appIcon.isInHotseatCell(cellInd)); return appIcon; } } } - /** This method requires public access, however should not be called in tests. */ @Override public Launchable getLaunchable() { return this; } + + boolean isInHotseatCell(int cellInd) { + final Point center = Workspace.getHotseatCellCenter(mLauncher, cellInd); + return mObject.getVisibleBounds().contains(center.x, center.y); + } } diff --git a/tests/tapl/com/android/launcher3/tapl/HomeQsb.java b/tests/tapl/com/android/launcher3/tapl/HomeQsb.java index 5f921996cd..c365708d46 100644 --- a/tests/tapl/com/android/launcher3/tapl/HomeQsb.java +++ b/tests/tapl/com/android/launcher3/tapl/HomeQsb.java @@ -15,12 +15,21 @@ */ package com.android.launcher3.tapl; +import androidx.annotation.NonNull; +import androidx.test.uiautomator.By; +import androidx.test.uiautomator.BySelector; +import androidx.test.uiautomator.UiObject2; +import androidx.test.uiautomator.Until; + /** * Operations on home screen qsb. */ public class HomeQsb { private final LauncherInstrumentation mLauncher; + private static final String ASSISTANT_APP_PACKAGE = "com.google.android.googlequicksearchbox"; + private static final String ASSISTANT_ICON_RES_ID = "mic_icon"; + HomeQsb(LauncherInstrumentation launcher) { mLauncher = launcher; @@ -28,6 +37,35 @@ public class HomeQsb { } /** + * Launch assistant app by tapping mic icon on qsb. + */ + @NonNull + public LaunchedAppState launchAssistant() { + try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer( + "want to click assistant mic icon button"); + LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) { + UiObject2 assistantIcon = mLauncher.waitForLauncherObject(ASSISTANT_ICON_RES_ID); + + LauncherInstrumentation.log("HomeQsb.launchAssistant before click " + + assistantIcon.getVisibleCenter() + " in " + + mLauncher.getVisibleBounds(assistantIcon)); + + mLauncher.clickLauncherObject(assistantIcon); + + try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer("clicked")) { + // assert Assistant App Launched + BySelector selector = By.pkg(ASSISTANT_APP_PACKAGE); + mLauncher.assertTrue( + "assistant app didn't start: (" + selector + ")", + mLauncher.getDevice().wait(Until.hasObject(selector), + LauncherInstrumentation.WAIT_TIME_MS) + ); + return new LaunchedAppState(mLauncher); + } + } + } + + /** * Show search result page from tapping qsb. */ public SearchResultFromQsb showSearchResult() { diff --git a/tests/tapl/com/android/launcher3/tapl/Launchable.java b/tests/tapl/com/android/launcher3/tapl/Launchable.java index 33fea2d10d..3dcb4377e7 100644 --- a/tests/tapl/com/android/launcher3/tapl/Launchable.java +++ b/tests/tapl/com/android/launcher3/tapl/Launchable.java @@ -16,7 +16,7 @@ package com.android.launcher3.tapl; -import static com.android.launcher3.testing.TestProtocol.SPRING_LOADED_STATE_ORDINAL; +import static com.android.launcher3.testing.shared.TestProtocol.SPRING_LOADED_STATE_ORDINAL; import android.graphics.Point; import android.view.MotionEvent; @@ -26,7 +26,7 @@ import androidx.test.uiautomator.BySelector; import androidx.test.uiautomator.UiObject2; import androidx.test.uiautomator.Until; -import com.android.launcher3.testing.TestProtocol; +import com.android.launcher3.testing.shared.TestProtocol; /** * Ancestor for AppIcon and AppMenuItem. diff --git a/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java b/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java index 046d36b0ba..4a3507ed69 100644 --- a/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java +++ b/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java @@ -16,9 +16,12 @@ package com.android.launcher3.tapl; -import static com.android.launcher3.testing.TestProtocol.REQUEST_DISABLE_MANUAL_TASKBAR_STASHING; -import static com.android.launcher3.testing.TestProtocol.REQUEST_ENABLE_MANUAL_TASKBAR_STASHING; -import static com.android.launcher3.testing.TestProtocol.REQUEST_STASHED_TASKBAR_HEIGHT; +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; import android.graphics.Point; import android.graphics.Rect; @@ -27,7 +30,7 @@ import android.view.MotionEvent; import androidx.test.uiautomator.By; -import com.android.launcher3.testing.TestProtocol; +import com.android.launcher3.testing.shared.TestProtocol; /** * Background state operations specific to when an app has been launched. @@ -54,24 +57,45 @@ public final class LaunchedAppState extends Background { 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 @@ public final class LaunchedAppState extends Background { 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 @@ public final class LaunchedAppState extends Background { } } 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 9d25b1ba90..ae9ba6787b 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -23,7 +23,7 @@ import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS; import static com.android.launcher3.tapl.Folder.FOLDER_CONTENT_RES_ID; import static com.android.launcher3.tapl.TestHelpers.getOverviewPackageName; -import static com.android.launcher3.testing.TestProtocol.NORMAL_STATE_ORDINAL; +import static com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL; import android.app.ActivityManager; import android.app.Instrumentation; @@ -48,7 +48,6 @@ import android.text.TextUtils; 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; @@ -65,10 +64,9 @@ import androidx.test.uiautomator.UiDevice; import androidx.test.uiautomator.UiObject2; import androidx.test.uiautomator.Until; -import com.android.launcher3.ResourceUtils; -import com.android.launcher3.testing.TestInformationRequest; -import com.android.launcher3.testing.TestProtocol; -import com.android.systemui.shared.system.ContextUtils; +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.QuickStepContract; import org.junit.Assert; @@ -84,8 +82,8 @@ import java.util.Deque; import java.util.LinkedList; import java.util.List; import java.util.Optional; -import java.util.concurrent.TimeUnit; 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; @@ -101,7 +99,6 @@ public final class LauncherInstrumentation { private static final String TAG = "Tapl"; private static final int ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME = 15; private static final int GESTURE_STEP_MS = 16; - private static final long FORCE_PAUSE_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(2); static final Pattern EVENT_TOUCH_DOWN = getTouchEventPattern("ACTION_DOWN"); static final Pattern EVENT_TOUCH_UP = getTouchEventPattern("ACTION_UP"); @@ -113,8 +110,11 @@ public final class LauncherInstrumentation { 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; @@ -123,8 +123,8 @@ public final class LauncherInstrumentation { // Types for launcher containers that the user is interacting with. "Background" is a // pseudo-container corresponding to inactive launcher covered by another app. public enum ContainerType { - WORKSPACE, HOME_ALL_APPS, OVERVIEW, WIDGETS, FALLBACK_OVERVIEW, LAUNCHED_APP, - TASKBAR_ALL_APPS + WORKSPACE, HOME_ALL_APPS, OVERVIEW, SPLIT_SCREEN_SELECT, WIDGETS, FALLBACK_OVERVIEW, + LAUNCHED_APP, TASKBAR_ALL_APPS } public enum NavigationModel {ZERO_BUTTON, THREE_BUTTON} @@ -138,6 +138,15 @@ public final class LauncherInstrumentation { 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; @@ -170,8 +179,10 @@ public final class LauncherInstrumentation { 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"; - public static final int WAIT_TIME_MS = 60000; + 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"; @@ -179,11 +190,13 @@ public final class LauncherInstrumentation { private final UiDevice mDevice; private final Instrumentation mInstrumentation; - private int mExpectedRotation = Surface.ROTATION_0; + private Integer mExpectedRotation = null; private final Uri mTestProviderUri; private final Deque<String> mDiagnosticContext = new LinkedList<>(); private Function<Long, String> mSystemHealthSupplier; + private boolean mIgnoreTaskbarVisibility = false; + private Consumer<ContainerType> mOnSettledStateAction; private LogEventChecker mEventChecker; @@ -238,7 +251,7 @@ public final class LauncherInstrumentation { // 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; @@ -264,7 +277,7 @@ public final class LauncherInstrumentation { 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", ""); @@ -285,6 +298,20 @@ public final class LauncherInstrumentation { } } + /** + * 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; } @@ -362,14 +389,14 @@ public final class LauncherInstrumentation { return getRealDisplaySize().x / 2f; } - private void setForcePauseTimeout(long timeout) { - getTestInfo(TestProtocol.REQUEST_SET_FORCE_PAUSE_TIMEOUT, Long.toString(timeout)); - } - public void setEnableRotation(boolean on) { 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); @@ -379,6 +406,17 @@ public final class LauncherInstrumentation { sActiveContainer = new WeakReference<>(container); } + /** + * Sets the accesibility interactive timeout to be effectively indefinite (UI using this + * accesibility timeout will not automatically dismiss if true). + */ + void setIndefiniteAccessibilityInteractiveUiTimeout(boolean indefiniteTimeout) { + final String cmd = indefiniteTimeout + ? "settings put secure accessibility_interactive_ui_timeout_ms 10000" + : "settings delete secure accessibility_interactive_ui_timeout_ms"; + logShellCommand(cmd); + } + public NavigationModel getNavigationModel() { final Context baseContext = mInstrumentation.getTargetContext(); try { @@ -523,7 +561,7 @@ public final class LauncherInstrumentation { 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() + ")"; @@ -658,7 +696,24 @@ public final class LauncherInstrumentation { } } - 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; } @@ -695,8 +750,10 @@ public final class LauncherInstrumentation { private UiObject2 verifyContainerType(ContainerType containerType) { waitForLauncherInitialized(); - assertEquals("Unexpected display rotation", - mExpectedRotation, mDevice.getDisplayRotation()); + if (mExpectedRotation != null) { + assertEquals("Unexpected display rotation", + mExpectedRotation, mDevice.getDisplayRotation()); + } final String error = getNavigationModeMismatchError(true); assertTrue(error, error == null); @@ -716,55 +773,97 @@ public final class LauncherInstrumentation { 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 OVERVIEW: + 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 waitForLauncherObject(OVERVIEW_RES_ID); + return waitForSystemLauncherObject(OVERVIEW_RES_ID); } - case FALLBACK_OVERVIEW: { + case SPLIT_SCREEN_SELECT: { 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); + } - return waitForFallbackLauncherObject(OVERVIEW_RES_ID); + waitForSystemLauncherObject(SPLIT_PLACEHOLDER_RES_ID); + return waitForSystemLauncherObject(OVERVIEW_RES_ID); } case LAUNCHED_APP: { waitUntilLauncherObjectGone(WORKSPACE_RES_ID); waitUntilLauncherObjectGone(APPS_RES_ID); - waitUntilLauncherObjectGone(OVERVIEW_RES_ID); waitUntilLauncherObjectGone(WIDGETS_RES_ID); + waitUntilSystemLauncherObjectGone(OVERVIEW_RES_ID); + waitUntilSystemLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID); + + if (mIgnoreTaskbarVisibility) { + return null; + } - if (isTablet() && !isFallbackOverview()) { - waitForLauncherObject(TASKBAR_RES_ID); + if (isTablet()) { + waitForSystemLauncherObject(TASKBAR_RES_ID); } else { - waitUntilLauncherObjectGone(TASKBAR_RES_ID); + waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID); } return null; } @@ -775,6 +874,10 @@ public final class LauncherInstrumentation { } } + public void waitForModelQueueCleared() { + getTestInfo(TestProtocol.REQUEST_MODEL_QUEUE_CLEARED); + } + public void waitForLauncherInitialized() { for (int i = 0; i < 100; ++i) { if (getTestInfo( @@ -870,7 +973,14 @@ public final class LauncherInstrumentation { } /** - * 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. */ @@ -886,17 +996,11 @@ public final class LauncherInstrumentation { final String action; if (getNavigationModel() == NavigationModel.ZERO_BUTTON) { checkForAnomaly(false, true); - setForcePauseTimeout(FORCE_PAUSE_TIMEOUT_MS); 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); @@ -929,7 +1033,7 @@ public final class LauncherInstrumentation { 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( @@ -964,8 +1068,12 @@ public final class LauncherInstrumentation { } } 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); + } } } } @@ -981,7 +1089,8 @@ public final class LauncherInstrumentation { 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)); } /** @@ -1064,6 +1173,14 @@ public final class LauncherInstrumentation { waitUntilGoneBySelector(getOverviewObjectSelector(resId)); } + void waitUntilSystemLauncherObjectGone(String resId) { + if (is3PLauncher()) { + waitUntilOverviewObjectGone(resId); + } else { + waitUntilLauncherObjectGone(resId); + } + } + void waitUntilLauncherObjectGone(BySelector selector) { waitUntilGoneBySelector(makeLauncherSelector(selector)); } @@ -1087,6 +1204,14 @@ public final class LauncherInstrumentation { } @NonNull + UiObject2 waitForSystemUiObject(BySelector selector) { + final UiObject2 object = TestHelpers.wait( + Until.findObject(selector), WAIT_TIME_MS); + assertNotNull("Can't find a systemui object with selector: " + selector, object); + return object; + } + + @NonNull UiObject2 waitForNavigationUiObject(String resId) { String resPackage = getNavigationButtonResPackage(); final UiObject2 object = mDevice.wait( @@ -1096,6 +1221,16 @@ public final class LauncherInstrumentation { } @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); @@ -1176,6 +1311,11 @@ public final class LauncherInstrumentation { 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)); } @@ -1195,6 +1335,12 @@ public final class LauncherInstrumentation { } @NonNull + UiObject2 waitForSystemLauncherObject(String resName) { + return is3PLauncher() ? waitForOverviewObject(resName) + : waitForLauncherObject(resName); + } + + @NonNull UiObject2 waitForLauncherObject(BySelector selector) { return waitForObjectBySelector(makeLauncherSelector(selector)); } @@ -1205,11 +1351,6 @@ public final class LauncherInstrumentation { } @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); @@ -1217,6 +1358,13 @@ public final class LauncherInstrumentation { return object; } + @NonNull + List<UiObject2> waitForObjectsBySelector(BySelector selector) { + final List<UiObject2> objects = mDevice.wait(Until.findObjects(selector), WAIT_TIME_MS); + assertNotNull("Can't find any view in Launcher, selector: " + selector, objects); + return objects; + } + private UiObject2 waitForObjectBySelector(BySelector selector) { final UiObject2 object = mDevice.wait(Until.findObject(selector), WAIT_TIME_MS); assertNotNull("Can't find a view in Launcher, selector: " + selector, object); @@ -1239,7 +1387,7 @@ public final class LauncherInstrumentation { return mDevice.getLauncherPackageName(); } - boolean isFallbackOverview() { + boolean is3PLauncher() { return !getOverviewPackageName().equals(getLauncherPackageName()); } @@ -1318,19 +1466,22 @@ public final class LauncherInstrumentation { 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( @@ -1700,6 +1851,14 @@ public final class LauncherInstrumentation { 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); @@ -1710,6 +1869,29 @@ public final class LauncherInstrumentation { 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); @@ -1831,4 +2013,60 @@ public final class LauncherInstrumentation { return ResourceUtils.getBoolByName( "config_supportsRoundedCornersOnWindows", resources, false); } + + /** + * 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 + */ + void touchOutsideContainer(UiObject2 container, boolean tapRight) { + try (LauncherInstrumentation.Closable c = addContextLayer( + "want to tap outside container on the " + (tapRight ? "right" : "left"))) { + Rect containerBounds = getVisibleBounds(container); + final long downTime = SystemClock.uptimeMillis(); + final Point tapTarget = new Point( + tapRight + ? (containerBounds.right + getRealDisplaySize().x) / 2 + : containerBounds.left / 2, + containerBounds.top + 1); + sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, tapTarget, + LauncherInstrumentation.GestureScope.INSIDE); + sendPointer(downTime, downTime, MotionEvent.ACTION_UP, tapTarget, + 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/LogEventChecker.java b/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java index 710e3cd691..672c6e0c8c 100644 --- a/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java +++ b/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java @@ -15,13 +15,13 @@ */ package com.android.launcher3.tapl; -import static com.android.launcher3.testing.TestProtocol.SEQUENCE_MAIN; -import static com.android.launcher3.testing.TestProtocol.SEQUENCE_PILFER; -import static com.android.launcher3.testing.TestProtocol.SEQUENCE_TIS; +import static com.android.launcher3.testing.shared.TestProtocol.SEQUENCE_MAIN; +import static com.android.launcher3.testing.shared.TestProtocol.SEQUENCE_PILFER; +import static com.android.launcher3.testing.shared.TestProtocol.SEQUENCE_TIS; import android.os.SystemClock; -import com.android.launcher3.testing.TestProtocol; +import com.android.launcher3.testing.shared.TestProtocol; import java.util.ArrayList; import java.util.HashMap; diff --git a/tests/tapl/com/android/launcher3/tapl/Overview.java b/tests/tapl/com/android/launcher3/tapl/Overview.java index 66a51a56a2..50c21368e0 100644 --- a/tests/tapl/com/android/launcher3/tapl/Overview.java +++ b/tests/tapl/com/android/launcher3/tapl/Overview.java @@ -21,7 +21,7 @@ import com.android.launcher3.tapl.LauncherInstrumentation.ContainerType; /** * Overview pane. */ -public final class Overview extends BaseOverview { +public class Overview extends BaseOverview { Overview(LauncherInstrumentation launcher) { super(launcher); @@ -29,7 +29,7 @@ public final class Overview extends BaseOverview { @Override protected ContainerType getContainerType() { - return LauncherInstrumentation.ContainerType.OVERVIEW; + return ContainerType.OVERVIEW; } @Override diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewActions.java b/tests/tapl/com/android/launcher3/tapl/OverviewActions.java index 2f44bb6aa4..386deac579 100644 --- a/tests/tapl/com/android/launcher3/tapl/OverviewActions.java +++ b/tests/tapl/com/android/launcher3/tapl/OverviewActions.java @@ -19,7 +19,7 @@ package com.android.launcher3.tapl; import androidx.annotation.NonNull; import androidx.test.uiautomator.UiObject2; -import com.android.launcher3.testing.TestProtocol; +import com.android.launcher3.testing.shared.TestProtocol; /** * View containing overview actions @@ -41,6 +41,8 @@ public class OverviewActions { try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck(); LauncherInstrumentation.Closable c = mLauncher.addContextLayer( "want to click screenshot button and exit screenshot ui")) { + mLauncher.setIndefiniteAccessibilityInteractiveUiTimeout(true); + UiObject2 screenshot = mLauncher.waitForObjectInContainer(mOverviewActions, "action_screenshot"); @@ -62,6 +64,8 @@ public class OverviewActions { return new Overview(mLauncher); } } + } finally { + mLauncher.setIndefiniteAccessibilityInteractiveUiTimeout(false); } } @@ -82,7 +86,6 @@ public class OverviewActions { "clicked select button")) { return getSelectModeButtons(); } - } } @@ -99,4 +102,22 @@ public class OverviewActions { return new SelectModeButtons(selectModeButtons, mLauncher); } } + + /** + * Clicks split button and enters split select mode. + */ + @NonNull + public SplitScreenSelect clickSplit() { + try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck(); + LauncherInstrumentation.Closable c = mLauncher.addContextLayer( + "want to click split button to enter split select mode")) { + UiObject2 split = mLauncher.waitForObjectInContainer(mOverviewActions, + "action_split"); + mLauncher.clickLauncherObject(split); + try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer( + "clicked split")) { + return new SplitScreenSelect(mLauncher); + } + } + } } diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java index c8caa42b6d..adc993df94 100644 --- a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java +++ b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java @@ -20,9 +20,12 @@ import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CH import android.graphics.Rect; +import androidx.annotation.NonNull; +import androidx.test.uiautomator.By; +import androidx.test.uiautomator.BySelector; import androidx.test.uiautomator.UiObject2; -import com.android.launcher3.testing.TestProtocol; +import com.android.launcher3.testing.shared.TestProtocol; import java.util.List; import java.util.regex.Pattern; @@ -32,8 +35,12 @@ import java.util.stream.Collectors; * A recent task in the overview panel carousel. */ public final class OverviewTask { + private static final String SYSTEMUI_PACKAGE = "com.android.systemui"; + static final Pattern TASK_START_EVENT = Pattern.compile("startActivityFromRecentsAsync"); + static final Pattern SPLIT_START_EVENT = + Pattern.compile("launchSplitTasks"); private final LauncherInstrumentation mLauncher; private final UiObject2 mTask; private final BaseOverview mOverview; @@ -61,6 +68,10 @@ public final class OverviewTask { return mTask.getVisibleCenter().x; } + float getExactCenterX() { + return mTask.getVisibleBounds().exactCenterX(); + } + /** * Dismisses the task by swiping up. */ @@ -125,7 +136,7 @@ public final class OverviewTask { } /** - * Clicks at the task. + * Clicks the task. */ public LaunchedAppState open() { try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) { @@ -136,8 +147,41 @@ public final class OverviewTask { () -> "Launching task didn't open a new window: " + mTask.getParent().getContentDescription(), "clicking an overview task"); - mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, TASK_START_EVENT); - return new LaunchedAppState(mLauncher); + if (mOverview.getContainerType() + == LauncherInstrumentation.ContainerType.SPLIT_SCREEN_SELECT) { + mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SPLIT_START_EVENT); + + try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer( + "launched splitscreen")) { + + BySelector divider = By.res(SYSTEMUI_PACKAGE, "docked_divider_handle"); + mLauncher.waitForSystemUiObject(divider); + return new LaunchedAppState(mLauncher); + } + } else { + mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, TASK_START_EVENT); + return new LaunchedAppState(mLauncher); + } + } + } + + /** 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 0000000000..8cdc8a036e --- /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/SearchResultFromQsb.java b/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java index 82652c7f27..80e4116406 100644 --- a/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java +++ b/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java @@ -20,12 +20,18 @@ import android.widget.TextView; import androidx.test.uiautomator.By; import androidx.test.uiautomator.UiObject2; +import java.util.ArrayList; + /** * Operations on search result page opened from home screen 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) { @@ -47,4 +53,50 @@ public class SearchResultFromQsb { UiObject2 icon = mLauncher.waitForLauncherObject(By.clazz(TextView.class).text(appName)); 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. + */ + public Workspace dismissByTappingOutsideForTablet(boolean tapRight) { + try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck(); + LauncherInstrumentation.Closable c = mLauncher.addContextLayer( + "want to tap outside AllApps bottom sheet on the " + + (tapRight ? "right" : "left"))) { + final UiObject2 allAppsBottomSheet = + mLauncher.waitForLauncherObject(BOTTOM_SHEET_RES_ID); + mLauncher.touchOutsideContainer(allAppsBottomSheet, tapRight); + try (LauncherInstrumentation.Closable tapped = mLauncher.addContextLayer( + "tapped outside AllApps bottom sheet")) { + return mLauncher.getWorkspace(); + } + } + } } diff --git a/tests/tapl/com/android/launcher3/tapl/SplitScreenSelect.java b/tests/tapl/com/android/launcher3/tapl/SplitScreenSelect.java new file mode 100644 index 0000000000..9fa74e48c2 --- /dev/null +++ b/tests/tapl/com/android/launcher3/tapl/SplitScreenSelect.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.tapl; + +import com.android.launcher3.tapl.LauncherInstrumentation.ContainerType; + +/** + * Represents a special state in Overview where the initial split app is shoved to the side and a + * second split app can be selected. + */ +public class SplitScreenSelect extends Overview { + + SplitScreenSelect(LauncherInstrumentation launcher) { + super(launcher); + } + + @Override + 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 b5a08c3ba6..6ca7f4bf79 100644 --- a/tests/tapl/com/android/launcher3/tapl/Taskbar.java +++ b/tests/tapl/com/android/launcher3/tapl/Taskbar.java @@ -15,8 +15,9 @@ */ package com.android.launcher3.tapl; -import static com.android.launcher3.testing.TestProtocol.REQUEST_DISABLE_MANUAL_TASKBAR_STASHING; -import static com.android.launcher3.testing.TestProtocol.REQUEST_ENABLE_MANUAL_TASKBAR_STASHING; +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; import android.graphics.Point; import android.os.SystemClock; @@ -51,7 +52,7 @@ public final class Taskbar { 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 @@ public final class Taskbar { 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 @@ public final class Taskbar { 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 @@ public final class Taskbar { 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 @@ public final class Taskbar { 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/TaskbarAppIconMenuItem.java b/tests/tapl/com/android/launcher3/tapl/TaskbarAppIconMenuItem.java index 69a8a08800..424c58e260 100644 --- a/tests/tapl/com/android/launcher3/tapl/TaskbarAppIconMenuItem.java +++ b/tests/tapl/com/android/launcher3/tapl/TaskbarAppIconMenuItem.java @@ -17,7 +17,7 @@ package com.android.launcher3.tapl; import androidx.test.uiautomator.UiObject2; -import com.android.launcher3.testing.TestProtocol; +import com.android.launcher3.testing.shared.TestProtocol; import java.util.regex.Pattern; diff --git a/tests/tapl/com/android/launcher3/tapl/Widget.java b/tests/tapl/com/android/launcher3/tapl/Widget.java index 2346249564..046308b595 100644 --- a/tests/tapl/com/android/launcher3/tapl/Widget.java +++ b/tests/tapl/com/android/launcher3/tapl/Widget.java @@ -23,7 +23,7 @@ import androidx.test.uiautomator.BySelector; import androidx.test.uiautomator.UiObject2; import androidx.test.uiautomator.Until; -import com.android.launcher3.testing.TestProtocol; +import com.android.launcher3.testing.shared.TestProtocol; import java.util.regex.Pattern; @@ -32,7 +32,7 @@ import java.util.regex.Pattern; */ 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); @@ -69,7 +69,24 @@ public final class Widget extends Launchable implements WorkspaceDragSource { */ @NonNull public WidgetResizeFrame dragWidgetToWorkspace() { - return dragWidgetToWorkspace(/* configurable= */ false, /* acceptsConfig= */ false); + try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) { + return dragWidgetToWorkspace(/* configurable= */ false, /* acceptsConfig= */ false, -1, + -1); + } + } + + /** + * Drags a non-configurable widget from the widgets container to the workspace at cellX and + * cellY and returns the resize frame that is shown after the widget is added. + */ + @NonNull + public WidgetResizeFrame dragWidgetToWorkspace(int cellX, int cellY) { + 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); + } } /** @@ -79,7 +96,32 @@ public final class Widget extends Launchable implements WorkspaceDragSource { */ @Nullable public WidgetResizeFrame dragConfigWidgetToWorkspace(boolean acceptsConfig) { - return dragWidgetToWorkspace(/* configurable= */ true, 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); + //} + } + + /** + * Drags an object to the center of homescreen. + * + * @param startsActivity whether it's expected to start an activity. + * @param isWidgetShortcut whether we drag a widget shortcut + * @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) { + Launchable launchable = getLaunchable(); + LauncherInstrumentation launcher = launchable.mLauncher; + Workspace.dragIconToWorkspace( + launcher, + launchable, + () -> Workspace.getCellCenter(launchable.mLauncher, cellX, cellY), + startsActivity, + isWidgetShortcut, + launchable::addExpectedEventsForLongClick); + } /** @@ -88,11 +130,28 @@ public final class Widget extends Launchable implements WorkspaceDragSource { * * <p> If {@code configurable} is true, then either accepts or cancels the configuration based * on {@code acceptsConfig}. + * <p> If either {@code cellX} or {@code cellY} are negative, then a default location would be + * chosen + * + * @param configurable if the widget has a configuration activity. + * @param acceptsConfig if the widget has a configuration, then if we should accept it or + * cancel it + * @param cellX X position to drop the widget in the workspace + * @param cellY Y position to drop the widget in the workspace + * @return returns the given resize frame of the widget after being dropped, if + * configurable is true and acceptsConfig is false then the widget would not be places and will + * be cancel and it returns null. */ @Nullable - private WidgetResizeFrame dragWidgetToWorkspace( - boolean configurable, boolean acceptsConfig) { - dragToWorkspace(/* startsActivity= */ configurable, /* isWidgetShortcut= */ false); + private WidgetResizeFrame dragWidgetToWorkspace(boolean configurable, boolean acceptsConfig, + int cellX, int cellY) { + if (cellX == -1 || cellY == -1) { + internalDragToWorkspace(/* startsActivity= */ configurable, /* isWidgetShortcut= */ + false); + } else { + dragToWorkspace(/* startsActivity= */ configurable, /* isWidgetShortcut= */ false, + cellX, cellY); + } if (configurable) { // Configure the widget. diff --git a/tests/tapl/com/android/launcher3/tapl/Widgets.java b/tests/tapl/com/android/launcher3/tapl/Widgets.java index 7fd68c09e9..96e2e3fbec 100644 --- a/tests/tapl/com/android/launcher3/tapl/Widgets.java +++ b/tests/tapl/com/android/launcher3/tapl/Widgets.java @@ -27,7 +27,7 @@ import androidx.test.uiautomator.Direction; import androidx.test.uiautomator.UiObject2; import androidx.test.uiautomator.Until; -import com.android.launcher3.testing.TestProtocol; +import com.android.launcher3.testing.shared.TestProtocol; import java.util.Collection; diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java index eb7f05bd1e..2c82c50eca 100644 --- a/tests/tapl/com/android/launcher3/tapl/Workspace.java +++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java @@ -18,8 +18,9 @@ package com.android.launcher3.tapl; import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_SCROLLED; -import static com.android.launcher3.testing.TestProtocol.ALL_APPS_STATE_ORDINAL; -import static com.android.launcher3.testing.TestProtocol.NORMAL_STATE_ORDINAL; +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; import static junit.framework.TestCase.assertNotNull; import static junit.framework.TestCase.assertTrue; @@ -39,8 +40,9 @@ import androidx.test.uiautomator.UiDevice; import androidx.test.uiautomator.UiObject2; import androidx.test.uiautomator.Until; -import com.android.launcher3.testing.TestProtocol; -import com.android.launcher3.testing.WorkspaceCellCenterRequest; +import com.android.launcher3.testing.shared.HotseatCellCenterRequest; +import com.android.launcher3.testing.shared.TestProtocol; +import com.android.launcher3.testing.shared.WorkspaceCellCenterRequest; import java.util.List; import java.util.Map; @@ -89,7 +91,7 @@ public final class Workspace extends Home { final int windowCornerRadius = (int) Math.ceil(mLauncher.getWindowCornerRadius()); final int startY = deviceHeight - Math.max(bottomGestureMargin, windowCornerRadius) - 1; final int swipeHeight = mLauncher.getTestInfo( - TestProtocol.REQUEST_HOME_TO_ALL_APPS_SWIPE_HEIGHT) + TestProtocol.REQUEST_HOME_TO_ALL_APPS_SWIPE_HEIGHT) .getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD); LauncherInstrumentation.log( "switchToAllApps: deviceHeight = " + deviceHeight + ", startY = " + startY @@ -140,6 +142,20 @@ public final class Workspace extends Home { } } + /** + * Waits for an app icon to be gone (e.g. after uninstall). Fails if it remains. + * + * @param errorMessage error message thrown then the icon doesn't disappear. + * @param appName app that should be gone. + */ + public void verifyWorkspaceAppIconIsGone(String errorMessage, String appName) { + final UiObject2 workspace = verifyActiveContainer(); + assertTrue(errorMessage, + workspace.wait( + Until.gone(AppIcon.getAppIconSelector(appName, mLauncher)), + LauncherInstrumentation.WAIT_TIME_MS)); + } + /** * Returns an icon for the app; fails if the icon doesn't exist. @@ -177,6 +193,12 @@ public final class Workspace extends Home { } } + /** 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. */ @@ -214,10 +236,11 @@ public final class Workspace extends Home { private void dragIcon(UiObject2 workspace, HomeAppIcon homeAppIcon, int pageDelta) { int pageWidth = mLauncher.getDevice().getDisplayWidth() / pagesPerScreen(); int targetX = (pageWidth / 2) + pageWidth * pageDelta; + int targetY = mLauncher.getVisibleBounds(workspace).centerY(); dragIconToWorkspace( mLauncher, homeAppIcon, - new Point(targetX, mLauncher.getVisibleBounds(workspace).centerY()), + () -> new Point(targetX, targetY), false, false, () -> mLauncher.expectEvent( @@ -236,8 +259,26 @@ public final class Workspace extends Home { } /** + * Returns an icon for the given cell; fails if the icon doesn't exist. + * + * @param cellInd zero based index number of the hotseat cells. + * @return app icon. + */ + @NonNull + public HomeAppIcon getHotseatAppIcon(int cellInd) { + List<UiObject2> icons = mHotseat.findObjects(AppIcon.getAnyAppIconSelector()); + final Point center = getHotseatCellCenter(mLauncher, cellInd); + return icons.stream() + .filter(icon -> icon.getVisibleBounds().contains(center.x, center.y)) + .findFirst() + .map(icon -> new WorkspaceAppIcon(mLauncher, icon)) + .orElseThrow(() -> + new AssertionError("Unable to get a hotseat icon on " + cellInd)); + } + + /** * @return map of text -> center of the view. In case of icons with the same name, the one with - * lower x coordinate is selected. + * lower x coordinate is selected. */ public Map<String, Point> getWorkspaceIconsPositions() { final UiObject2 workspace = verifyActiveContainer(); @@ -250,6 +291,7 @@ public final class Workspace extends Home { /* valueMapper= */ UiObject2::getVisibleCenter, /* mergeFunction= */ (p1, p2) -> p1.x < p2.x ? p1 : p2)); } + /* * Get the center point of the delete/uninstall icon in the drop target bar. */ @@ -261,6 +303,31 @@ public final class Workspace extends Home { } /** + * 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. @@ -284,6 +351,7 @@ public final class Workspace extends Home { } } + /** * Uninstall the appIcon by dragging it to the 'uninstall' drop point of the drop_target_bar. * @@ -317,7 +385,7 @@ public final class Workspace extends Home { 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)); @@ -345,6 +413,11 @@ public final class Workspace extends Home { TestProtocol.TEST_INFO_RESPONSE_FIELD); } + static Point getHotseatCellCenter(LauncherInstrumentation launcher, int cellInd) { + return launcher.getTestInfo(HotseatCellCenterRequest.builder() + .setCellInd(cellInd).build()).getParcelable(TestProtocol.TEST_INFO_RESPONSE_FIELD); + } + /** * Finds folder icons in the current workspace. * @@ -372,7 +445,7 @@ public final class Workspace extends Home { } static void dragIconToWorkspace(LauncherInstrumentation launcher, Launchable launchable, - Point dest, boolean startsActivity, boolean isWidgetShortcut, + Supplier<Point> dest, boolean startsActivity, boolean isWidgetShortcut, Runnable expectLongClickEvents) { Runnable expectDropEvents = null; if (startsActivity || isWidgetShortcut) { @@ -380,7 +453,7 @@ public final class Workspace extends Home { LauncherInstrumentation.EVENT_START); } dragIconToWorkspace( - launcher, launchable, () -> dest, expectLongClickEvents, expectDropEvents); + launcher, launchable, dest, expectLongClickEvents, expectDropEvents); } /** @@ -429,7 +502,9 @@ public final class Workspace extends Home { // 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, @@ -445,6 +520,7 @@ public final class Workspace extends Home { 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); } } @@ -456,6 +532,25 @@ public final class Workspace extends Home { () -> "Page scroll didn't happen", "Scrolling page"); } + static void dragIconToHotseat( + LauncherInstrumentation launcher, + Launchable launchable, + Supplier<Point> dest, + Runnable expectLongClickEvents, + @Nullable Runnable expectDropEvents) { + final long downTime = SystemClock.uptimeMillis(); + Point dragStart = launchable.startDrag( + downTime, + expectLongClickEvents, + /* runToSpringLoadedState= */ true); + Point targetDest = dest.get(); + + launcher.movePointer(dragStart, targetDest, DEFAULT_DRAG_STEPS, true, + downTime, SystemClock.uptimeMillis(), false, + LauncherInstrumentation.GestureScope.INSIDE); + dropDraggedIcon(launcher, targetDest, downTime, expectDropEvents); + } + /** * Flings to get to screens on the right. Waits for scrolling and a possible overscroll * recoil to complete. @@ -523,6 +618,32 @@ public final class Workspace extends Home { } } + /** + * @param cellX X position of the widget trying to get. + * @param cellY Y position of the widget trying to get. + * @return returns the Widget in the given position in the Launcher or an Exception if no such + * widget is in that position. + */ + @NonNull + public Widget getWidgetAtCell(int cellX, int cellY) { + try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer( + "getting widget at cell position " + cellX + "," + cellY)) { + final List<UiObject2> widgets = mLauncher.waitForObjectsBySelector( + By.clazz("com.android.launcher3.widget.LauncherAppWidgetHostView")); + Point coordinateInScreen = Workspace.getCellCenter(mLauncher, cellX, cellY); + for (UiObject2 widget : widgets) { + if (widget.getVisibleBounds().contains(coordinateInScreen.x, + coordinateInScreen.y)) { + return new Widget(mLauncher, widget); + } + } + } + mLauncher.fail("Unable to find widget at cell " + cellX + "," + cellY); + // This statement is unreachable because mLauncher.fail throws an exception + // but is needed for compiling + return null; + } + @Nullable public Widget tryGetPendingWidget(long timeout) { try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer( diff --git a/tests/tapl/com/android/launcher3/tapl/WorkspaceDragSource.java b/tests/tapl/com/android/launcher3/tapl/WorkspaceDragSource.java index d8d4420005..141476c782 100644 --- a/tests/tapl/com/android/launcher3/tapl/WorkspaceDragSource.java +++ b/tests/tapl/com/android/launcher3/tapl/WorkspaceDragSource.java @@ -17,6 +17,8 @@ package com.android.launcher3.tapl; import android.graphics.Point; +import java.util.function.Supplier; + /** Launchable that can serve as a source for dragging and dropping to the workspace. */ interface WorkspaceDragSource { @@ -30,20 +32,67 @@ interface WorkspaceDragSource { Launchable launchable = getLaunchable(); LauncherInstrumentation launcher = launchable.mLauncher; try (LauncherInstrumentation.Closable e = launcher.eventsCheck()) { - final Point launchableCenter = launchable.getObject().getVisibleCenter(); - final Point displaySize = launcher.getRealDisplaySize(); - final int width = displaySize.x / 2; + internalDragToWorkspace(startsActivity, isWidgetShortcut); + } + } + + /** + * TODO(Redesign WorkspaceDragSource to have actual private methods) + * Temporary private method + * + * @param startsActivity whether it's expected to start an activity. + * @param isWidgetShortcut whether we drag a widget shortcut + */ + default void internalDragToWorkspace(boolean startsActivity, boolean isWidgetShortcut) { + Launchable launchable = getLaunchable(); + LauncherInstrumentation launcher = launchable.mLauncher; + final Point launchableCenter = launchable.getObject().getVisibleCenter(); + final Point displaySize = launcher.getRealDisplaySize(); + final int width = displaySize.x / 2; + Workspace.dragIconToWorkspace( + launcher, + launchable, + () -> new Point( + launchableCenter.x >= width + ? launchableCenter.x - width / 2 + : launchableCenter.x + width / 2, + displaySize.y / 2), + startsActivity, + isWidgetShortcut, + launchable::addExpectedEventsForLongClick); + } + + /** + * Drag an object to the given cell in workspace. The target cell must be empty. + * + * @param cellX zero based column number, starting from the left of the screen. + * @param cellY zero based row number, starting from the top of the screen. * + */ + default HomeAppIcon dragToWorkspace(int cellX, int cellY) { + Launchable launchable = getLaunchable(); + final String iconName = launchable.getObject().getText(); + LauncherInstrumentation launcher = launchable.mLauncher; + try (LauncherInstrumentation.Closable e = launcher.eventsCheck(); + LauncherInstrumentation.Closable c = launcher.addContextLayer( + String.format("want to drag the icon to cell(%d, %d)", cellX, cellY))) { + final Supplier<Point> dest = () -> Workspace.getCellCenter(launcher, cellX, cellY); Workspace.dragIconToWorkspace( launcher, launchable, - new Point( - launchableCenter.x >= width - ? launchableCenter.x - width / 2 - : launchableCenter.x + width / 2, - displaySize.y / 2), - startsActivity, - isWidgetShortcut, - launchable::addExpectedEventsForLongClick); + dest, + launchable::addExpectedEventsForLongClick, + /*expectDropEvents= */ null); + + try (LauncherInstrumentation.Closable ignore = launcher.addContextLayer("dragged")) { + WorkspaceAppIcon appIcon = + (WorkspaceAppIcon) launcher.getWorkspace().getWorkspaceAppIcon(iconName); + launcher.assertTrue( + String.format( + "The %s icon should be in the cell (%d, %d).", iconName, cellX, + cellY), + appIcon.isInCell(cellX, cellY)); + return appIcon; + } } } |